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: Properly handle visibility Created April 30, 2019, 4:07 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') | no next file with comments »
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 let closest = element.closest(selector);
430 if (closest && match(element, closest))
431 hideElement(closest);
432 }
433 };
434 new MutationObserver(callback)
435 .observe(document, {childList: true, characterData: true, subtree: true});
436 callback();
437 }
438
439 /**
440 * 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. 441 * the text content of the element contains a given string.
412 * 442 *
413 * @param {string} search The string to look for in HTML elements. If the 443 * @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 444 * string begins and ends with a slash (<code>/</code>), the text in between
415 * is treated as a regular expression. 445 * is treated as a regular expression.
416 * @param {string} selector The CSS selector that an HTML element must match 446 * @param {string} selector The CSS selector that an HTML element must match
417 * for it to be hidden. 447 * for it to be hidden.
418 * @param {string?} [searchSelector] The CSS selector that an HTML element 448 * @param {?string} [searchSelector] The CSS selector that an HTML element
419 * containing the given string must match. Defaults to the value of the 449 * containing the given string must match. Defaults to the value of the
420 * <code>selector</code> argument. 450 * <code>selector</code> argument.
421 */ 451 */
422 function hideIfContains(search, selector = "*", searchSelector = null) 452 function hideIfContains(search, selector = "*", searchSelector = null)
423 { 453 {
424 if (searchSelector == null) 454 let re = toRegExp(search);
425 searchSelector = selector; 455
456 hideIfMatches(element => re.test(element.textContent),
457 selector, searchSelector);
458 }
459
460 exports["hide-if-contains"] = hideIfContains;
461
462 /**
463 * Hides any HTML element matching a CSS selector if the visible text content
464 * of the element contains a given string.
465 *
466 * @param {string} search The string to match to the visible text. Is considered
467 * visible text that isn't hidden by CSS properties or other means.
468 * If the string begins and ends with a slash (<code>/</code>), the
469 * text in between is treated as a regular expression.
470 * @param {string} selector The CSS selector that an HTML element must match
471 * for it to be hidden.
472 * @param {?string} [searchSelector] The CSS selector that an HTML element
473 * containing the given string must match. Defaults to the value of the
474 * <code>selector</code> argument.
475 */
476 function hideIfContainsVisibleText(search, selector, searchSelector = null)
477 {
478 /**
479 * Determines if the text inside the element is visible.
480 * @param {Element} element The element we are checking.
481 * @param {?CSSStyleDeclaration} style The computed style of element. If
482 * falsey it will be queried.
483 * @returns {bool} Whether the text is visible.
484 */
485 function isTextVisible(element, style)
486 {
487 if (!style)
488 style = window.getComputedStyle(element);
489
490 if (style.getPropertyValue("opacity") == "0")
491 return false;
492 if (style.getPropertyValue("font-size") == "0px")
493 return false;
494
495 let color = style.getPropertyValue("color");
496 // if color is transparent...
497 if (color == "rgba(0, 0, 0, 0)")
498 return false;
499 if (style.getPropertyValue("background-color") == color)
500 return false;
501
502 return true;
503 }
504
505 /**
506 * Check if an element is visible
507 * @param {Element} element The element to check visibility of/
508 * @param {?CSSStyleDeclaration} style The computed style of element. If
509 * falsey it will be queried.
510 * @param {?Element} closest The closest parent to reach.
511 * @return {bool} Whether the element is visible.
512 */
513 function isVisible(element, style, closest)
514 {
515 if (!style)
516 style = window.getComputedStyle(element);
517
518 if (style.getPropertyValue("display") == "none")
519 return false;
520 let visibility = style.getPropertyValue("visibility");
521 if (visibility == "hidden" || visibility == "collapse")
522 return false;
523
524 if (!closest || element == closest)
525 return true;
526
527 return isVisible(element.parentNode, null, closest);
a.giammarchi 2019/05/08 09:43:47 I have bad feelings about this for at least two re
hub 2019/05/08 17:03:42 I'll stop if parent is null.
a.giammarchi 2019/05/09 08:58:59 where does this happen? 'cause all I see is that y
hub 2019/05/09 12:06:26 It is actually better if I actually properly submi
528 }
529
530 /**
531 * Returns the visible text content from an element and its descendants.
532 * @param {Element} element The element whose visible text we want.
533 * @param {Element} closest The closest parent to reach while checking
534 * for visibility.
535 * @returns {string} The text that is visible.
536 */
537 function getVisibleContent(element, closest)
538 {
539 let style = window.getComputedStyle(element);
540 if (!isVisible(element, style, closest))
541 return "";
542
543 let text = "";
544 for (let node of element.childNodes)
545 {
546 switch (node.nodeType)
547 {
548 case Node.ELEMENT_NODE:
549 text += getVisibleContent(node, element);
550 break;
551 case Node.TEXT_NODE:
552 if (isTextVisible(element, style))
553 text += node.nodeValue;
554 break;
555 }
556 }
557 return text;
558 }
426 559
427 let re = toRegExp(search); 560 let re = toRegExp(search);
428 561
429 new MutationObserver(() => 562 hideIfMatches(
430 { 563 (element, closest) => re.test(getVisibleContent(element, closest)),
431 for (let element of document.querySelectorAll(searchSelector)) 564 selector, 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 } 565 }
443 566
444 exports["hide-if-contains"] = hideIfContains; 567 exports["hide-if-contains-visible-text"] = hideIfContainsVisibleText;
445 568
446 /** 569 /**
447 * Hides any HTML element or one of its ancestors matching a CSS selector if 570 * 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 571 * the text content of the element contains a given string and, optionally, if
449 * the element's computed style contains a given string. 572 * the element's computed style contains a given string.
450 * 573 *
451 * @param {string} search The string to look for in HTML elements. If the 574 * @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 575 * string begins and ends with a slash (<code>/</code>), the text in between
453 * is treated as a regular expression. 576 * is treated as a regular expression.
454 * @param {string} selector The CSS selector that an HTML element must match 577 * @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 1016
894 args[0] = url.href; 1017 args[0] = url.href;
895 } 1018 }
896 1019
897 return fetch_.apply(this, args); 1020 return fetch_.apply(this, args);
898 }; 1021 };
899 } 1022 }
900 1023
901 exports["strip-fetch-query-parameter"] = makeInjector(stripFetchQueryParameter, 1024 exports["strip-fetch-query-parameter"] = makeInjector(stripFetchQueryParameter,
902 toRegExp, regexEscape); 1025 toRegExp, regexEscape);
OLDNEW
« 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