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

Unified Diff: lib/content/snippets.js

Issue 30024560: Issue 7450 - Implement hide-if-contains-visible-text snippet (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Updated patch for real Created May 9, 2019, 12:04 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 | test/browser/snippets.js » ('j') | 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
@@ -403,50 +403,177 @@
}
exports["hide-if-shadow-contains"] = makeInjector(hideIfShadowContains,
toRegExp, regexEscape,
hideElement);
/**
* Hides any HTML element or one of its ancestors matching a CSS selector if
+ * it matches the provided condition.
+ *
+ * @param {function} match The function that provides the matching condition.
+ * @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 hideIfMatches(match, selector, searchSelector)
+{
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ let callback = () =>
+ {
+ for (let element of document.querySelectorAll(searchSelector))
+ {
+ let closest = element.closest(selector);
+ if (closest && match(element, closest))
+ hideElement(closest);
+ }
+ };
+ new MutationObserver(callback)
+ .observe(document, {childList: true, characterData: true, subtree: true});
+ callback();
+}
+
+/**
+ * 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. 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
+ * @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);
+
+ hideIfMatches(element => re.test(element.textContent),
+ selector, searchSelector);
+}
+
+exports["hide-if-contains"] = hideIfContains;
+
+/**
+ * Hides any HTML element matching a CSS selector if the visible text content
+ * of the element contains a given string.
+ *
+ * @param {string} search The string to match to the visible text. Is considered
+ * visible text that isn't hidden by CSS properties or other means.
+ * 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 hideIfContainsVisibleText(search, selector, searchSelector = null)
+{
+ /**
+ * Determines if the text inside the element is visible.
+ * @param {Element} element The element we are checking.
+ * @param {?CSSStyleDeclaration} style The computed style of element. If
+ * falsey it will be queried.
+ * @returns {bool} Whether the text is visible.
+ */
+ function isTextVisible(element, style)
+ {
+ if (!style)
+ style = window.getComputedStyle(element);
+
+ if (style.getPropertyValue("opacity") == "0")
+ return false;
+ if (style.getPropertyValue("font-size") == "0px")
+ return false;
+
+ let color = style.getPropertyValue("color");
+ // if color is transparent...
+ if (color == "rgba(0, 0, 0, 0)")
+ return false;
+ if (style.getPropertyValue("background-color") == color)
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Check if an element is visible
+ * @param {Element} element The element to check visibility of.
+ * @param {?CSSStyleDeclaration} style The computed style of element. If
+ * falsey it will be queried.
+ * @param {?Element} closest The closest parent to reach.
+ * @return {bool} Whether the element is visible.
+ */
+ function isVisible(element, style, closest)
+ {
+ if (!style)
+ style = window.getComputedStyle(element);
+
+ if (style.getPropertyValue("display") == "none")
+ return false;
+ let visibility = style.getPropertyValue("visibility");
+ if (visibility == "hidden" || visibility == "collapse")
+ return false;
+
+ if (!closest || element == closest)
+ return true;
+
+ let parent = element.parentElement;
+ if (!parent)
+ return true;
+
+ return isVisible(parent, null, closest);
+ }
+
+ /**
+ * Returns the visible text content from an element and its descendants.
+ * @param {Element} element The element whose visible text we want.
+ * @param {Element} closest The closest parent to reach while checking
+ * for visibility.
+ * @returns {string} The text that is visible.
+ */
+ function getVisibleContent(element, closest)
+ {
+ let style = window.getComputedStyle(element);
+ if (!isVisible(element, style, closest))
+ return "";
+
+ let text = "";
+ for (let node of element.childNodes)
+ {
+ switch (node.nodeType)
+ {
+ case Node.ELEMENT_NODE:
+ text += getVisibleContent(node, element);
+ break;
+ case Node.TEXT_NODE:
+ if (isTextVisible(element, style))
+ text += node.nodeValue;
+ break;
+ }
+ }
+ return text;
+ }
let re = toRegExp(search);
- new MutationObserver(() =>
- {
- for (let element of document.querySelectorAll(searchSelector))
- {
- if (re.test(element.textContent))
- {
- let closest = element.closest(selector);
- if (closest)
- hideElement(closest);
- }
- }
- })
- .observe(document, {childList: true, characterData: true, subtree: true});
+ hideIfMatches(
+ (element, closest) => re.test(getVisibleContent(element, closest)),
+ selector, searchSelector);
}
-exports["hide-if-contains"] = hideIfContains;
+exports["hide-if-contains-visible-text"] = hideIfContainsVisibleText;
/**
* Hides any HTML element or one of its ancestors matching a CSS selector if
* the text content of the element contains a given string and, optionally, if
* the element's computed style contains a given string.
*
* @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
« no previous file with comments | « no previous file | test/browser/snippets.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld