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

Side by Side 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.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/browser/snippets.js » ('j') | test/browser/snippets.js » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH 3 * Copyright (C) 2006-present eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 } 401 }
402 }); 402 });
403 } 403 }
404 404
405 exports["hide-if-shadow-contains"] = makeInjector(hideIfShadowContains, 405 exports["hide-if-shadow-contains"] = makeInjector(hideIfShadowContains,
406 toRegExp, regexEscape, 406 toRegExp, regexEscape,
407 hideElement); 407 hideElement);
408 408
409 /** 409 /**
410 * Hides any HTML element or one of its ancestors matching a CSS selector if 410 * Hides any HTML element or one of its ancestors matching a CSS selector if
411 * it matches the provided condition.
412 *
413 * @param {function} match The function that provides the matching condition.
414 * @param {string} selector The CSS selector that an HTML element must match
415 * for it to be hidden.
416 * @param {?string} [searchSelector] The CSS selector that an HTML element
417 * containing the given string must match. Defaults to the value of the
418 * <code>selector</code> argument.
419 */
420 function hideIfMatches(match, selector, searchSelector)
421 {
422 if (searchSelector == null)
423 searchSelector = selector;
424
425 let callback = () =>
426 {
427 for (let element of document.querySelectorAll(searchSelector))
428 {
429 if (match(element))
430 {
431 let closest = element.closest(selector);
432 if (closest)
433 hideElement(closest);
434 }
435 }
436 };
437 new MutationObserver(callback)
438 .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
439 callback();
440 }
441
442 /**
443 * Hides any HTML element or one of its ancestors matching a CSS selector if
411 * the text content of the element contains a given string. 444 * the text content of the element contains a given string.
412 * 445 *
413 * @param {string} search The string to look for in HTML elements. If the 446 * @param {string} search The string to look for in HTML elements. If the
414 * string begins and ends with a slash (<code>/</code>), the text in between 447 * string begins and ends with a slash (<code>/</code>), the text in between
415 * is treated as a regular expression. 448 * is treated as a regular expression.
416 * @param {string} selector The CSS selector that an HTML element must match 449 * @param {string} selector The CSS selector that an HTML element must match
417 * for it to be hidden. 450 * for it to be hidden.
418 * @param {string?} [searchSelector] The CSS selector that an HTML element 451 * @param {?string} [searchSelector] The CSS selector that an HTML element
419 * containing the given string must match. Defaults to the value of the 452 * containing the given string must match. Defaults to the value of the
420 * <code>selector</code> argument. 453 * <code>selector</code> argument.
421 */ 454 */
422 function hideIfContains(search, selector = "*", searchSelector = null) 455 function hideIfContains(search, selector = "*", searchSelector = null)
423 { 456 {
424 if (searchSelector == null) 457 let re = toRegExp(search);
425 searchSelector = selector; 458
459 hideIfMatches(element => re.test(element.textContent),
460 selector, searchSelector);
461 }
462
463 exports["hide-if-contains"] = hideIfContains;
464
465 /**
466 * Hides any HTML element matching a CSS selector if the visible text content
467 * of the element contains a given string.
468 *
469 * @param {string} search The string to match to the visible text. Is considered
470 * visible text that isn't hidden by CSS properties or other means.
471 * If the string begins and ends with a slash (<code>/</code>), the
472 * text in between is treated as a regular expression.
473 * @param {string} selector The CSS selector that an HTML element must match
474 * for it to be hidden.
475 * @param {?string} [searchSelector] The CSS selector that an HTML element
476 * containing the given string must match. Defaults to the value of the
477 * <code>selector</code> argument.
478 */
479 function hideIfContainsVisibleText(search, selector, searchSelector = null)
480 {
481 /**
482 * Determines if the text inside the element is visible.
483 * @param {Element} element The element we are checking.
484 * @param {?CSSStyleDeclaration} [style] The computed style of element. If
485 * 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.
486 * @returns {bool} Whether the text is visible.
487 */
488 function isTextVisible(element, style)
489 {
490 if (!style)
491 style = window.getComputedStyle(element);
492
493 if (style.getPropertyValue("opacity") == "0")
494 return false;
495 if (style.getPropertyValue("font-size") == "0px")
496 return false;
497 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.
498 style.getPropertyValue("background-color"))
499 return false;
500
501 return true;
502 }
503
504 /**
505 * Returns the visible text content from an element and its descendants.
506 * @param {Element} element The element whose visible text we want.
507 * @returns {string} The text that is visible.
508 */
509 function getVisibleContent(element)
510 {
511 let style = window.getComputedStyle(element);
512 if (style.getPropertyValue("display") == "none")
513 return "";
514 let visibility = style.getPropertyValue("visibility");
515 if (visibility == "hidden" || visibility == "collapse")
516 return "";
517
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.
518 let text = "";
519 for (let node of element.childNodes)
520 {
521 switch (node.nodeType)
522 {
523 case Node.ELEMENT_NODE:
524 text += getVisibleContent(node);
525 break;
526 case Node.TEXT_NODE:
527 if (isTextVisible(element, style))
528 text += node.nodeValue;
529 break;
530 }
531 }
532 return text;
533 }
426 534
427 let re = toRegExp(search); 535 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.
428 536
429 new MutationObserver(() => 537 hideIfMatches(element => re.test(getVisibleContent(element)),
430 { 538 selector, searchSelector);
431 for (let element of document.querySelectorAll(searchSelector))
432 {
433 if (re.test(element.textContent))
434 {
435 let closest = element.closest(selector);
436 if (closest)
437 hideElement(closest);
438 }
439 }
440 })
441 .observe(document, {childList: true, characterData: true, subtree: true});
442 } 539 }
443 540
444 exports["hide-if-contains"] = hideIfContains; 541 exports["hide-if-contains-visible-text"] = hideIfContainsVisibleText;
445 542
446 /** 543 /**
447 * Hides any HTML element or one of its ancestors matching a CSS selector if 544 * Hides any HTML element or one of its ancestors matching a CSS selector if
448 * the text content of the element contains a given string and, optionally, if 545 * the text content of the element contains a given string and, optionally, if
449 * the element's computed style contains a given string. 546 * the element's computed style contains a given string.
450 * 547 *
451 * @param {string} search The string to look for in HTML elements. If the 548 * @param {string} search The string to look for in HTML elements. If the
452 * string begins and ends with a slash (<code>/</code>), the text in between 549 * string begins and ends with a slash (<code>/</code>), the text in between
453 * is treated as a regular expression. 550 * is treated as a regular expression.
454 * @param {string} selector The CSS selector that an HTML element must match 551 * @param {string} selector The CSS selector that an HTML element must match
(...skipping 438 matching lines...) Expand 10 before | Expand all | Expand 10 after
893 990
894 args[0] = url.href; 991 args[0] = url.href;
895 } 992 }
896 993
897 return fetch_.apply(this, args); 994 return fetch_.apply(this, args);
898 }; 995 };
899 } 996 }
900 997
901 exports["strip-fetch-query-parameter"] = makeInjector(stripFetchQueryParameter, 998 exports["strip-fetch-query-parameter"] = makeInjector(stripFetchQueryParameter,
902 toRegExp, regexEscape); 999 toRegExp, regexEscape);
OLDNEW
« 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