Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: lib/content/snippets.js

Issue 29833630: Issue 6798 - Implement hide-if-shadow-contains snippet (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Remove premature hardening Created July 19, 2018, 4:03 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/content/snippets.js
===================================================================
--- a/lib/content/snippets.js
+++ b/lib/content/snippets.js
@@ -92,16 +92,39 @@
*/
function makeInjector(injectable, ...dependencies)
{
return (...args) => injectCode(stringifyFunctionCall(injectable, ...args),
dependencies);
}
/**
+ * Hides an HTML element by settings its <code>style</code> attribute to
+ * <code>display: none !important</code>.
+ *
+ * @param {HTMLElement} element The HTML element to hide.
+ */
+function hideElement(element)
+{
+ element.style.setProperty("display", "none", "important");
+
+ // Listen for changes to the style property and if our values are unset
+ // then reset them.
+ new MutationObserver(() =>
+ {
+ if (element.style.getPropertyValue("display") != "none" ||
+ element.style.getPropertyPriority("display") != "important")
+ {
+ element.style.setProperty("display", "none", "important");
+ }
+ })
+ .observe(element, {attributes: true, attributeFilter: ["style"]});
+}
+
+/**
* Logs its arguments to the console. This may be used for testing and
* debugging.
*
* @param {...*} [args] The arguments to log.
*/
function log(...args)
{
console.log(...args);
@@ -120,8 +143,87 @@
function trace(...args)
{
// We could simply use console.log here, but the goal is to demonstrate the
// usage of snippet dependencies.
log(...args);
}
exports.trace = makeInjector(trace, log);
+
+/**
+ * Hides any HTML element or one of its ancestors matching a CSS selector if
+ * the text content of the element's shadow contains a given string.
+ *
+ * @param {string} search The string to look for in every HTML element's
+ * shadow.
+ * @param {string} selector The CSS selector that an HTML element must match
+ * for it to be hidden.
+ */
+function hideIfShadowContains(search, selector = "*")
+{
+ let originalAttachShadow = Element.prototype.attachShadow;
kzar 2018/07/19 16:11:41 With my suggestions below we can remove this line
+
+ // If there's no Element.attachShadow API present then we don't care, it must
+ // be Firefox or an older version of Chrome.
+ if (!originalAttachShadow)
kzar 2018/07/19 16:11:41 `if (!Element.prototype.attachShadow)`
+ return;
+
+ // Mutation observers mapped to their corresponding shadow roots and their
+ // hosts.
+ let shadows = new WeakMap();
+
+ function observe(mutations, observer)
+ {
+ let {host, root} = shadows.get(observer) || {};
+
+ // Since it's a weak map, it's possible that either the element or its
+ // shadow has been removed.
+ if (!host || !root)
+ return;
+
+ // If the shadow contains the given text, check if the host or one of its
+ // ancestors matches the selector; if a matching element is found, hide
+ // it.
+ if (root.textContent.includes(search))
+ {
+ let element = host;
+
+ do
+ {
+ if (element.matches(selector))
+ {
+ hideElement(element);
+ break;
+ }
+ }
+ while (element = element.parentElement);
+ }
+ }
+
+ Object.defineProperty(Element.prototype, "attachShadow", {
+ value(...args)
+ {
+ // Create the shadow root first. It doesn't matter if it's a closed
+ // shadow root, we keep the reference in a weak map.
+ let root = originalAttachShadow.apply(this, args);
kzar 2018/07/19 16:11:41 `let root = this.attachShadow(...args);`
Manish Jethani 2018/07/19 16:15:10 Isn't this going to be an infinite loop? this.atta
kzar 2018/07/19 16:28:51 Oh yea, good point we're overwriting it ourselves.
+
+ // Listen for relevant DOM mutations in the shadow.
+ let observer = new MutationObserver(observe);
+ observer.observe(root, {
+ childList: true,
+ characterData: true,
+ subtree: true
+ });
+
+ // Keep references to the shadow root and its host in a weak map. If
+ // either the shadow is detached or the host itself is removed from the
+ // DOM, the mutation observer too will be freed eventually and the entry
+ // will be removed.
+ shadows.set(observer, {host: this, root});
+
+ return root;
+ }
+ });
+}
+
+exports["hide-if-shadow-contains"] = makeInjector(hideIfShadowContains,
+ hideElement);
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld