| Left: | ||
| Right: | 
| LEFT | RIGHT | 
|---|---|
| 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 Loading... | |
| 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) | |
| 425 searchSelector = selector; | |
| 426 | |
| 427 let re = toRegExp(search); | 454 let re = toRegExp(search); | 
| 428 | 455 | 
| 429 new MutationObserver(() => | 456 hideIfMatches(element => re.test(element.textContent), | 
| 430 { | 457 selector, searchSelector); | 
| 431 for (let element of document.querySelectorAll(searchSelector)) | 458 } | 
| 432 { | 459 | 
| 433 if (re.test(element.textContent)) | 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 let parent = element.parentElement; | |
| 528 if (!parent) | |
| 529 return true; | |
| 530 | |
| 531 return isVisible(parent, null, closest); | |
| 532 } | |
| 533 | |
| 534 /** | |
| 535 * Returns the visible text content from an element and its descendants. | |
| 536 * @param {Element} element The element whose visible text we want. | |
| 537 * @param {Element} closest The closest parent to reach while checking | |
| 538 * for visibility. | |
| 539 * @returns {string} The text that is visible. | |
| 540 */ | |
| 541 function getVisibleContent(element, closest) | |
| 542 { | |
| 543 let style = window.getComputedStyle(element); | |
| 544 if (!isVisible(element, style, closest)) | |
| 545 return ""; | |
| 546 | |
| 547 let text = ""; | |
| 548 for (let node of element.childNodes) | |
| 549 { | |
| 550 switch (node.nodeType) | |
| 434 { | 551 { | 
| 435 let closest = element.closest(selector); | 552 case Node.ELEMENT_NODE: | 
| 436 if (closest) | 553 text += getVisibleContent(node, element); | 
| 437 hideElement(closest); | 554 break; | 
| 555 case Node.TEXT_NODE: | |
| 556 if (isTextVisible(element, style)) | |
| 557 text += node.nodeValue; | |
| 558 break; | |
| 438 } | 559 } | 
| 439 } | 560 } | 
| 440 }) | 561 return text; | 
| 441 .observe(document, {childList: true, characterData: true, subtree: true}); | 562 } | 
| 442 } | 563 | 
| 443 | 564 let re = toRegExp(search); | 
| 444 exports["hide-if-contains"] = hideIfContains; | 565 | 
| 566 hideIfMatches( | |
| 567 (element, closest) => re.test(getVisibleContent(element, closest)), | |
| 568 selector, searchSelector); | |
| 569 } | |
| 570 | |
| 571 exports["hide-if-contains-visible-text"] = hideIfContainsVisibleText; | |
| 445 | 572 | 
| 446 /** | 573 /** | 
| 447 * Hides any HTML element or one of its ancestors matching a CSS selector if | 574 * 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 | 575 * the text content of the element contains a given string and, optionally, if | 
| 449 * the element's computed style contains a given string. | 576 * the element's computed style contains a given string. | 
| 450 * | 577 * | 
| 451 * @param {string} search The string to look for in HTML elements. If the | 578 * @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 | 579 * string begins and ends with a slash (<code>/</code>), the text in between | 
| 453 * is treated as a regular expression. | 580 * is treated as a regular expression. | 
| 454 * @param {string} selector The CSS selector that an HTML element must match | 581 * @param {string} selector The CSS selector that an HTML element must match | 
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 671 function wrapPropertyAccess(object, property, descriptor) | 798 function wrapPropertyAccess(object, property, descriptor) | 
| 672 { | 799 { | 
| 673 let dotIndex = property.indexOf("."); | 800 let dotIndex = property.indexOf("."); | 
| 674 if (dotIndex == -1) | 801 if (dotIndex == -1) | 
| 675 { | 802 { | 
| 676 // simple property case. | 803 // simple property case. | 
| 677 let currentDescriptor = Object.getOwnPropertyDescriptor(object, property); | 804 let currentDescriptor = Object.getOwnPropertyDescriptor(object, property); | 
| 678 if (currentDescriptor && !currentDescriptor.configurable) | 805 if (currentDescriptor && !currentDescriptor.configurable) | 
| 679 return; | 806 return; | 
| 680 | 807 | 
| 681 descriptor.configurable = true; | 808 // Keep it configurable because the same property can be wrapped via | 
| 682 Object.defineProperty(object, property, descriptor); | 809 // multiple snippet filters (#7373). | 
| 810 Object.defineProperty(object, property, | |
| 811 Object.assign({}, descriptor, {configurable: true})); | |
| 683 return; | 812 return; | 
| 684 } | 813 } | 
| 685 | 814 | 
| 686 let name = property.slice(0, dotIndex); | 815 let name = property.slice(0, dotIndex); | 
| 687 property = property.slice(dotIndex + 1); | 816 property = property.slice(dotIndex + 1); | 
| 688 let value = object[name]; | 817 let value = object[name]; | 
| 689 if (value && (typeof value == "object" || typeof value == "function")) | 818 if (value && (typeof value == "object" || typeof value == "function")) | 
| 690 wrapPropertyAccess(value, property, descriptor); | 819 wrapPropertyAccess(value, property, descriptor); | 
| 691 | 820 | 
| 692 let currentDescriptor = Object.getOwnPropertyDescriptor(object, name); | 821 let currentDescriptor = Object.getOwnPropertyDescriptor(object, name); | 
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 798 * slash (<code>/</code>), the text in between is treated as a regular | 927 * slash (<code>/</code>), the text in between is treated as a regular | 
| 799 * expression. | 928 * expression. | 
| 800 */ | 929 */ | 
| 801 function abortCurrentInlineScript(api, search = null) | 930 function abortCurrentInlineScript(api, search = null) | 
| 802 { | 931 { | 
| 803 let re = search ? toRegExp(search) : null; | 932 let re = search ? toRegExp(search) : null; | 
| 804 | 933 | 
| 805 let rid = randomId(); | 934 let rid = randomId(); | 
| 806 let us = document.currentScript; | 935 let us = document.currentScript; | 
| 807 | 936 | 
| 808 | |
| 809 let object = window; | 937 let object = window; | 
| 810 let path = api.split("."); | 938 let path = api.split("."); | 
| 811 let name = path.pop(); | 939 let name = path.pop(); | 
| 812 | 940 | 
| 813 for (let node of path) | 941 for (let node of path) | 
| 814 { | 942 { | 
| 815 object = object[node]; | 943 object = object[node]; | 
| 816 | 944 | 
| 817 if (!object || !(typeof object == "object" || typeof object == "function")) | 945 if (!object || !(typeof object == "object" || typeof object == "function")) | 
| 818 return; | 946 return; | 
| 819 } | 947 } | 
| 820 | 948 | 
| 821 let prevGetter; | 949 let {get: prevGetter, set: prevSetter} = | 
| 822 let prevSetter; | 950 Object.getOwnPropertyDescriptor(object, name) || {}; | 
| 823 let desc = Object.getOwnPropertyDescriptor(object, name); | |
| 824 if (desc) | |
| 825 { | |
| 826 if (typeof desc.get == "function") | |
| 827 prevGetter = desc.get; | |
| 828 if (typeof desc.set == "function") | |
| 829 prevSetter = desc.set; | |
| 830 } | |
| 831 | 951 | 
| 832 let currentValue = object[name]; | 952 let currentValue = object[name]; | 
| 833 | 953 | 
| 834 let abort = () => | 954 let abort = () => | 
| 835 { | 955 { | 
| 836 let element = document.currentScript; | 956 let element = document.currentScript; | 
| 837 if (element instanceof HTMLScriptElement && element.src == "" && | 957 if (element instanceof HTMLScriptElement && element.src == "" && | 
| 838 element != us && (!re || re.test(element.textContent))) | 958 element != us && (!re || re.test(element.textContent))) | 
| 839 { | 959 { | 
| 840 throw new ReferenceError(rid); | 960 throw new ReferenceError(rid); | 
| 841 } | 961 } | 
| 842 }; | 962 }; | 
| 843 | 963 | 
| 844 let descriptor = { | 964 let descriptor = { | 
| 845 get() | 965 get() | 
| 846 { | 966 { | 
| 847 abort(); | 967 abort(); | 
| 968 | |
| 848 if (prevGetter) | 969 if (prevGetter) | 
| 849 return prevGetter.call(this); | 970 return prevGetter.call(this); | 
| 850 | 971 | 
| 851 return currentValue; | 972 return currentValue; | 
| 852 }, | 973 }, | 
| 853 set(value) | 974 set(value) | 
| 854 { | 975 { | 
| 855 abort(); | 976 abort(); | 
| 977 | |
| 856 if (prevSetter) | 978 if (prevSetter) | 
| 857 prevSetter.call(this, value); | 979 prevSetter.call(this, value); | 
| 858 | 980 else | 
| 859 currentValue = value; | 981 currentValue = value; | 
| 860 } | 982 } | 
| 861 }; | 983 }; | 
| 862 | 984 | 
| 863 wrapPropertyAccess(object, name, descriptor); | 985 wrapPropertyAccess(object, name, descriptor); | 
| 864 | 986 | 
| 865 overrideOnError(rid); | 987 overrideOnError(rid); | 
| 866 } | 988 } | 
| 867 | 989 | 
| 868 exports["abort-current-inline-script"] = | 990 exports["abort-current-inline-script"] = | 
| 869 makeInjector(abortCurrentInlineScript, wrapPropertyAccess, toRegExp, | 991 makeInjector(abortCurrentInlineScript, wrapPropertyAccess, toRegExp, | 
| (...skipping 28 matching lines...) Expand all Loading... | |
| 898 | 1020 | 
| 899 args[0] = url.href; | 1021 args[0] = url.href; | 
| 900 } | 1022 } | 
| 901 | 1023 | 
| 902 return fetch_.apply(this, args); | 1024 return fetch_.apply(this, args); | 
| 903 }; | 1025 }; | 
| 904 } | 1026 } | 
| 905 | 1027 | 
| 906 exports["strip-fetch-query-parameter"] = makeInjector(stripFetchQueryParameter, | 1028 exports["strip-fetch-query-parameter"] = makeInjector(stripFetchQueryParameter, | 
| 907 toRegExp, regexEscape); | 1029 toRegExp, regexEscape); | 
| 908 | |
| 
 
Manish Jethani
2019/04/17 13:54:26
Nit: Maybe a good idea to group all the `hide-if-c
 
hub
2019/04/17 17:25:06
yes
 
 | |
| 909 /** | |
| 910 * Hide an element matching a selector if it contains a specified visible text. | |
| 
 
Manish Jethani
2019/04/17 13:54:25
Nits or consistency:
 s/Hide/Hides/
 s/an element
 
hub
2019/04/17 17:25:06
Done.
 
 | |
| 911 * | |
| 912 * @param {string} search the text to match to the visible text. Is considered | |
| 913 * visible text that isn't hidden by CSS properties or other means. | |
| 914 * If the string begins and ends with a slash (<code>/</code>), the | |
| 915 * text in between is treated as a regular expression. | |
| 916 * @param {string} hideSelector the selector for the element that must be | |
| 917 * hidden. | |
| 918 * @param {?string} [innerSelector] the selector relative to the element that | |
| 919 * will select the text content to match. | |
| 920 */ | |
| 921 function hideIfContainsVisibleText(search, hideSelector, innerSelector = null) | |
| 
 
Manish Jethani
2019/04/09 12:31:23
I know the use case here is to target a node based
 
hub
2019/04/10 16:16:21
I think this defeat the design of that snippet tha
 
Manish Jethani
2019/04/10 16:39:23
Instead of hard-coding the styles that would ident
 
Manish Jethani
2019/04/10 17:15:17
By the way, if you think we should add a `hide-if-
 
hub
2019/04/10 19:40:05
I'd be ok, despite being more complicated, to allo
 
hub
2019/04/10 19:40:05
I don't think it make sense to merge the code of t
 
Manish Jethani
2019/04/10 19:48:20
We don't have to invent anything new for the synta
 
Manish Jethani
2019/04/10 19:48:20
What I'm saying is that the entire functionality t
 
Manish Jethani
2019/04/10 19:58:52
To give an example of what I'm saying, let's say t
 
Manish Jethani
2019/04/17 13:54:25
Nit: parameter names for consistency with other sn
 
hub
2019/04/17 17:25:06
They have a different meaning, but.... (see below)
 
 | |
| 922 { | |
| 923 /** | |
| 924 * Determine if the text inside the element is visible. | |
| 
 
Manish Jethani
2019/04/17 13:54:25
Nit: s/Determine/Determines/
 
hub
2019/04/17 17:25:06
Done.
 
 | |
| 925 * @param {Element} element the leaf element we are checking. | |
| 
 
Manish Jethani
2019/04/17 13:54:26
Nit: So far in this file the doc string starts wit
 
hub
2019/04/17 17:25:06
Done.
 
 | |
| 926 * @param {?CSSStyleDeclaration} [style] the computed style of element. If | |
| 927 * falsey it will be queried. | |
| 928 * @returns {bool} whether the text is visible. | |
| 929 */ | |
| 930 function isTextVisible(element, style) | |
| 931 { | |
| 932 if (!style) | |
| 933 style = window.getComputedStyle(element); | |
| 
 
Manish Jethani
2019/04/17 13:54:26
There's a local `getComputedStyle()` which covers
 
hub
2019/04/17 17:25:06
I only see `getComputedCSSText()` which calls`getC
 
Manish Jethani
2019/04/17 18:33:28
Sorry, my bad.
Ack.
 
 | |
| 934 | |
| 935 if (style.getPropertyValue("opacity") == "0") | |
| 936 return false; | |
| 937 if (style.getPropertyValue("font-size") == "0px") | |
| 938 return false; | |
| 939 if (style.getPropertyValue("color") == | |
| 940 style.getPropertyValue("background-color")) | |
| 941 return false; | |
| 942 | |
| 943 return true; | |
| 944 } | |
| 945 | |
| 946 /** | |
| 947 * Returns the visible text content from an element and its children. | |
| 948 * @param {Element} element the element whose visible text we want. | |
| 949 * @returns {string} the text that is visible. | |
| 950 */ | |
| 951 function getVisibleContent(element) | |
| 952 { | |
| 953 let style = window.getComputedStyle(element); | |
| 954 if (style.getPropertyValue("display") == "none") | |
| 955 return ""; | |
| 956 let visibility = style.getPropertyValue("visibility"); | |
| 957 if (visibility == "hidden" || visibility == "collapse") | |
| 958 return ""; | |
| 959 | |
| 960 let text = ""; | |
| 961 for (let node of element.childNodes) | |
| 962 { | |
| 963 switch (node.nodeType) | |
| 964 { | |
| 965 case Node.ELEMENT_NODE: | |
| 966 text += getVisibleContent(node); | |
| 967 break; | |
| 968 case Node.TEXT_NODE: | |
| 969 if (isTextVisible(element, style)) | |
| 970 text += node.nodeValue; | |
| 971 break; | |
| 972 } | |
| 973 } | |
| 974 return text; | |
| 975 } | |
| 976 | |
| 977 let re = toRegExp(search); | |
| 978 | |
| 979 new MutationObserver(() => | |
| 980 { | |
| 981 for (let element of document.querySelectorAll(hideSelector)) | |
| 982 { | |
| 983 let inners = | |
| 984 innerSelector ? element.querySelectorAll(innerSelector) : [element]; | |
| 
 
Manish Jethani
2019/04/17 13:54:26
Here there's a second call to `querySelectorAll()`
 
hub
2019/04/17 17:25:06
Let's rework this in the next patch. (see comment
 
Manish Jethani
2019/04/17 18:33:28
Just so I get this right, we'll have a follow-up p
 
hub
2019/04/17 18:40:34
The current patch already refactor :-)
 
 | |
| 985 for (let inner of inners) | |
| 986 { | |
| 987 let content = getVisibleContent(inner); | |
| 988 if (re.test(content)) | |
| 989 hideElement(element); | |
| 990 } | |
| 991 } | |
| 992 }) | |
| 993 .observe(document, {childList: true, characterData: true, subtree: true}); | |
| 994 } | |
| 995 | |
| 996 exports["hide-if-contains-visible-text"] = | |
| 997 makeInjector(hideIfContainsVisibleText, hideElement); | |
| LEFT | RIGHT |