| 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); |