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: Fixed a few nits. Unfactored test code. Created April 18, 2019, 5:48 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') | test/browser/snippets.js » ('J')
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,147 @@
}
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))
+ {
+ if (match(element))
+ {
+ let closest = element.closest(selector);
+ if (closest)
+ hideElement(closest);
+ }
+ }
+ };
+ new MutationObserver(callback)
+ .observe(document, {childList: true, characterData: true, subtree: true});
a.giammarchi 2019/04/26 13:33:58 I might make a similar observation we had before (
hub 2019/04/27 17:32:05 The current code is just refactoring of the other
a.giammarchi 2019/04/29 07:27:25 Acknowledged.
hub 2019/04/29 15:20:55 BTW I filed https://gitlab.com/eyeo/adblockplus/ad
+ 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.
a.giammarchi 2019/04/26 13:33:58 typo? did you mean `falsy` ?
hub 2019/04/27 17:32:05 Both are acceptable, and we use `falsey` elsewhere
a.giammarchi 2019/04/29 07:27:25 Acknowledged.
+ * @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;
+ if (style.getPropertyValue("color") ==
a.giammarchi 2019/04/26 13:33:57 shouldn't `color` be checked against `transparent`
hub 2019/04/27 17:32:05 good point. I knew there were things I'm missing.
+ style.getPropertyValue("background-color"))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Returns the visible text content from an element and its descendants.
+ * @param {Element} element The element whose visible text we want.
+ * @returns {string} The text that is visible.
+ */
+ function getVisibleContent(element)
+ {
+ let style = window.getComputedStyle(element);
+ if (style.getPropertyValue("display") == "none")
+ return "";
+ let visibility = style.getPropertyValue("visibility");
+ if (visibility == "hidden" || visibility == "collapse")
+ return "";
+
a.giammarchi 2019/04/26 13:51:06 shouldn't `clientWidth` and `clientHeight` be also
hub 2019/04/27 17:32:05 Good point. I'll look into it.
+ let text = "";
+ for (let node of element.childNodes)
+ {
+ switch (node.nodeType)
+ {
+ case Node.ELEMENT_NODE:
+ text += getVisibleContent(node);
+ break;
+ case Node.TEXT_NODE:
+ if (isTextVisible(element, style))
+ text += node.nodeValue;
+ break;
+ }
+ }
+ return text;
+ }
let re = toRegExp(search);
a.giammarchi 2019/04/26 13:33:58 this feature is overall OK but I have one concern:
hub 2019/04/27 17:32:05 Whichever effect applies to the text with CSS, the
a.giammarchi 2019/04/29 07:27:25 If I understand correctly, we might check for "Ads
hub 2019/04/29 13:03:47 I see where this is going. Good point. Because th
a.giammarchi 2019/04/29 15:31:10 Acknowledged.
- 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 => re.test(getVisibleContent(element)),
+ 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') | test/browser/snippets.js » ('J')

Powered by Google App Engine
This is Rietveld