| Index: lib/content/snippets.js |
| =================================================================== |
| --- a/lib/content/snippets.js |
| +++ b/lib/content/snippets.js |
| @@ -16,16 +16,50 @@ |
| */ |
| /* eslint-env webextensions */ |
| /* eslint no-console: "off" */ |
| "use strict"; |
| /** |
| + * Escapes regular expression special characters in a string. The returned |
| + * string may be passed to the <code>RegExp</code> constructor to match the |
| + * original string. |
| + * |
| + * @param {string} string The string in which to escape special characters. |
| + * |
| + * @returns {string} A new string with the special characters escaped. |
| + */ |
| +function regexEscape(string) |
| +{ |
| + return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); |
| +} |
| + |
| +/** |
| + * Converts a given pattern to a regular expression. |
| + * |
| + * @param {string} pattern The pattern to convert. If the pattern begins and |
| + * ends with a slash (<code>/</code>), the text in between is treated as a |
| + * regular expression; otherwise the pattern is treated as raw text. |
| + * |
| + * @returns {RegExp} A <code>RegExp</code> object based on the given pattern. |
| + */ |
| +function toRegExp(pattern) |
| +{ |
| + if (pattern.length >= 2 && pattern[0] == "/" && |
| + pattern[pattern.length - 1] == "/") |
| + { |
| + return new RegExp(pattern.substring(1, pattern.length - 1)); |
| + } |
| + |
| + return new RegExp(regexEscape(pattern)); |
| +} |
| + |
| +/** |
| * Injects JavaScript code into the document using a temporary |
| * <code>script</code> element. |
| * |
| * @param {string} code The code to inject. |
| * @param {Array.<function|string>} [dependencies] A list of dependencies |
| * to inject along with the code. A dependency may be either a function or a |
| * string containing some executable code. |
| */ |
| @@ -181,46 +215,49 @@ |
| exports["uabinject-defuser"] = makeInjector(uabinjectDefuser); |
| /** |
| * 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. |
| + * shadow. If the string begins and ends with a slash (<code>/</code>), the |
| + * text in between is treated as a regular expression. |
| * @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; |
| // 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) |
| return; |
| + let re = toRegExp(search); |
| + |
| // Mutation observers mapped to their corresponding shadow roots and their |
| // hosts. |
| let shadows = new WeakMap(); |
| function observeShadow(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)) |
| + if (re.test(root.textContent)) |
| { |
| let closest = host.closest(selector); |
| if (closest) |
| hideElement(closest); |
| } |
| } |
| Object.defineProperty(Element.prototype, "attachShadow", { |
| @@ -245,39 +282,44 @@ |
| shadows.set(observer, {host: this, root}); |
| return root; |
| } |
| }); |
| } |
| exports["hide-if-shadow-contains"] = makeInjector(hideIfShadowContains, |
| + toRegExp, regexEscape, |
| hideElement); |
| /** |
| * Hides any HTML element or one of its ancestors matching a CSS selector if |
| * the text content of the element contains a given string. |
| * |
| - * @param {string} search The string to look for in HTML elements. |
| + * @param {string} search The string to look for in HTML elements. If the |
| + * string begins and ends with a slash (<code>/</code>), the text in between |
| + * is treated as a regular expression. |
| * @param {string} selector The CSS selector that an HTML element must match |
| * for it to be hidden. |
| * @param {string?} [searchSelector] The CSS selector that an HTML element |
| * containing the given string must match. Defaults to the value of the |
| * <code>selector</code> argument. |
| */ |
| function hideIfContains(search, selector = "*", searchSelector = null) |
| { |
| if (searchSelector == null) |
| searchSelector = selector; |
| + let re = toRegExp(search); |
| + |
| new MutationObserver(() => |
| { |
| for (let element of document.querySelectorAll(searchSelector)) |
| { |
| - if (element.textContent.includes(search)) |
| + if (re.test(element.textContent)) |
| { |
| let closest = element.closest(selector); |
| if (closest) |
| hideElement(closest); |
| } |
| } |
| }) |
| .observe(document, {childList: true, characterData: true, subtree: true}); |