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