| Left: | ||
| Right: |
| OLD | NEW |
|---|---|
| 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 18 matching lines...) Expand all Loading... | |
| 29 */ | 29 */ |
| 30 function positionInParent(node) | 30 function positionInParent(node) |
| 31 { | 31 { |
| 32 let {children} = node.parentNode; | 32 let {children} = node.parentNode; |
| 33 for (let i = 0; i < children.length; i++) | 33 for (let i = 0; i < children.length; i++) |
| 34 if (children[i] == node) | 34 if (children[i] == node) |
| 35 return i + 1; | 35 return i + 1; |
| 36 return 0; | 36 return 0; |
| 37 } | 37 } |
| 38 | 38 |
| 39 function getCachedPropertyValue(object, name, defaultValueFunc = () => {}) | |
| 40 { | |
| 41 if (!(name in object)) | |
| 42 Object.defineProperty(object, name, {value: defaultValueFunc()}); | |
| 43 return object[name]; | |
|
hub
2018/03/07 23:11:30
here I would rather do something like:
let value
Manish Jethani
2018/03/08 15:29:58
Thanks, this is a good idea.
Done.
(I'm using st
| |
| 44 } | |
| 45 | |
| 39 function makeSelector(node, selector) | 46 function makeSelector(node, selector) |
| 40 { | 47 { |
| 41 if (node == null) | 48 if (node == null) |
| 42 return null; | 49 return null; |
| 43 if (!node.parentElement) | 50 if (!node.parentElement) |
| 44 { | 51 { |
| 45 let newSelector = ":root"; | 52 let newSelector = ":root"; |
| 46 if (selector) | 53 if (selector) |
| 47 newSelector += " > " + selector; | 54 newSelector += " > " + selector; |
| 48 return newSelector; | 55 return newSelector; |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 372 } | 379 } |
| 373 }, | 380 }, |
| 374 | 381 |
| 375 *getSelectors(prefix, subtree, styles) | 382 *getSelectors(prefix, subtree, styles) |
| 376 { | 383 { |
| 377 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) | 384 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) |
| 378 yield [selector, subtree]; | 385 yield [selector, subtree]; |
| 379 } | 386 } |
| 380 }; | 387 }; |
| 381 | 388 |
| 382 function isSelectorHidingOnlyPattern(pattern) | 389 function Pattern(selectors, text) |
| 383 { | 390 { |
| 384 return pattern.selectors.some(s => s.preferHideWithSelector) && | 391 this.selectors = selectors; |
| 385 !pattern.selectors.some(s => s.requiresHiding); | 392 this.text = text; |
| 386 } | 393 } |
| 387 | 394 |
| 388 function patternDependsOnStyles(pattern) | 395 Pattern.prototype = { |
| 389 { | 396 isSelectorHidingOnlyPattern() |
| 390 return pattern.selectors.some(s => s.dependsOnStyles); | 397 { |
| 391 } | 398 return getCachedPropertyValue( |
| 399 this, "_selectorHidingOnlyPattern", | |
| 400 () => this.selectors.some(s => s.preferHideWithSelector) && | |
| 401 !this.selectors.some(s => s.requiresHiding) | |
| 402 ); | |
| 403 }, | |
| 392 | 404 |
| 393 function patternDependsOnDOM(pattern) | 405 get dependsOnStyles() |
| 394 { | 406 { |
| 395 return pattern.selectors.some(s => s.dependsOnDOM); | 407 return getCachedPropertyValue( |
| 396 } | 408 this, "_dependsOnStyles", () => this.selectors.some(s => s.dependsOnStyles ) |
| 409 ); | |
| 410 }, | |
| 397 | 411 |
| 398 function patternDependsOnStylesAndDOM(pattern) | 412 get dependsOnDOM() |
| 399 { | 413 { |
| 400 return pattern.selectors.some(s => s.dependsOnStyles && s.dependsOnDOM); | 414 return getCachedPropertyValue( |
| 401 } | 415 this, "_dependsOnDOM", () => this.selectors.some(s => s.dependsOnDOM) |
| 416 ); | |
| 417 }, | |
| 402 | 418 |
| 403 function patternMaybeDependsOnAttributes(pattern) | 419 get dependsOnStylesAndDOM() |
| 404 { | 420 { |
| 405 // Observe changes to attributes if either there's a plain selector that | 421 return getCachedPropertyValue( |
| 406 // looks like an ID selector, class selector, or attribute selector in one of | 422 this, "_dependsOnStylesAndDOM", |
| 407 // the patterns (e.g. "a[href='https://example.com/']") | 423 () => this.selectors.some(s => s.dependsOnStyles && s.dependsOnDOM) |
| 408 // or there's a properties selector nested inside a has selector | 424 ); |
| 409 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") | 425 }, |
| 410 return pattern.selectors.some( | |
| 411 selector => selector.maybeDependsOnAttributes || | |
| 412 (selector instanceof HasSelector && | |
| 413 selector.dependsOnStyles) | |
| 414 ); | |
| 415 } | |
| 416 | 426 |
| 417 function patternDependsOnCharacterData(pattern) | 427 get maybeDependsOnAttributes() |
| 418 { | 428 { |
| 419 // Observe changes to character data only if there's a contains selector in | 429 // Observe changes to attributes if either there's a plain selector that |
| 420 // one of the patterns. | 430 // looks like an ID selector, class selector, or attribute selector in one |
| 421 return pattern.selectors.some(selector => selector.dependsOnCharacterData); | 431 // of the patterns (e.g. "a[href='https://example.com/']") or there's a |
| 422 } | 432 // properties selector nested inside a has selector |
| 433 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") | |
| 434 return getCachedPropertyValue( | |
| 435 this, "_maybeDependsOnAttributes", | |
| 436 () => this.selectors.some( | |
| 437 selector => selector.maybeDependsOnAttributes || | |
| 438 (selector instanceof HasSelector && | |
| 439 selector.dependsOnStyles) | |
| 440 ) | |
| 441 ); | |
| 442 }, | |
| 423 | 443 |
| 424 function patternMatchesMutationTypes(pattern, mutationTypes) | 444 get dependsOnCharacterData() |
| 425 { | 445 { |
| 426 return mutationTypes.has("childList") || | 446 // Observe changes to character data only if there's a contains selector in |
| 427 (mutationTypes.has("attributes") && | 447 // one of the patterns. |
| 428 patternMaybeDependsOnAttributes(pattern)) || | 448 return getCachedPropertyValue( |
| 429 (mutationTypes.has("characterData") && | 449 this, "_dependsOnCharacterData", |
| 430 patternDependsOnCharacterData(pattern)); | 450 () => this.selectors.some(selector => selector.dependsOnCharacterData) |
| 431 } | 451 ); |
| 452 }, | |
| 453 | |
| 454 matchesMutationTypes(mutationTypes) | |
| 455 { | |
| 456 return mutationTypes.has("childList") || | |
| 457 (mutationTypes.has("attributes") && | |
| 458 this.maybeDependsOnAttributes) || | |
| 459 (mutationTypes.has("characterData") && | |
| 460 this.dependsOnCharacterData); | |
| 461 } | |
| 462 }; | |
| 432 | 463 |
| 433 function extractMutationTypes(mutations) | 464 function extractMutationTypes(mutations) |
| 434 { | 465 { |
| 435 let types = new Set(); | 466 let types = new Set(); |
| 436 | 467 |
| 437 for (let mutation of mutations) | 468 for (let mutation of mutations) |
| 438 { | 469 { |
| 439 types.add(mutation.type); | 470 types.add(mutation.type); |
| 440 | 471 |
| 441 // There are only 3 types of mutations: "attributes", "characterData", and | 472 // There are only 3 types of mutations: "attributes", "characterData", and |
| 442 // "childList". | 473 // "childList". |
| 443 if (types.size == 3) | 474 if (types.size == 3) |
| 444 break; | 475 break; |
| 445 } | 476 } |
| 446 | 477 |
| 447 return types; | 478 return types; |
| 448 } | 479 } |
| 449 | 480 |
| 450 function filterPatterns(patterns, {stylesheets, mutations}) | 481 function filterPatterns(patterns, {stylesheets, mutations}) |
| 451 { | 482 { |
| 452 if (!stylesheets && !mutations) | 483 if (!stylesheets && !mutations) |
| 453 return patterns.slice(); | 484 return patterns.slice(); |
| 454 | 485 |
| 455 let mutationTypes = mutations ? extractMutationTypes(mutations) : null; | 486 let mutationTypes = mutations ? extractMutationTypes(mutations) : null; |
| 456 | 487 |
| 457 return patterns.filter( | 488 return patterns.filter( |
| 458 pattern => (stylesheets && patternDependsOnStyles(pattern)) || | 489 pattern => (stylesheets && pattern.dependsOnStyles) || |
| 459 (mutations && patternDependsOnDOM(pattern) && | 490 (mutations && pattern.dependsOnDOM && |
| 460 patternMatchesMutationTypes(pattern, mutationTypes)) | 491 pattern.matchesMutationTypes(mutationTypes)) |
| 461 ); | 492 ); |
| 462 } | 493 } |
| 463 | 494 |
| 464 function shouldObserveAttributes(patterns) | 495 function shouldObserveAttributes(patterns) |
| 465 { | 496 { |
| 466 return patterns.some(patternMaybeDependsOnAttributes); | 497 return patterns.some(pattern => pattern.maybeDependsOnAttributes); |
| 467 } | 498 } |
| 468 | 499 |
| 469 function shouldObserveCharacterData(patterns) | 500 function shouldObserveCharacterData(patterns) |
| 470 { | 501 { |
| 471 return patterns.some(patternDependsOnCharacterData); | 502 return patterns.some(pattern => pattern.dependsOnCharacterData); |
| 472 } | 503 } |
| 473 | 504 |
| 474 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) | 505 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) |
| 475 { | 506 { |
| 476 this.document = document; | 507 this.document = document; |
| 477 this.addSelectorsFunc = addSelectorsFunc; | 508 this.addSelectorsFunc = addSelectorsFunc; |
| 478 this.hideElemsFunc = hideElemsFunc; | 509 this.hideElemsFunc = hideElemsFunc; |
| 479 this.observer = new MutationObserver(this.observe.bind(this)); | 510 this.observer = new MutationObserver(this.observe.bind(this)); |
| 480 } | 511 } |
| 481 | 512 |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 587 // do full processing. | 618 // do full processing. |
| 588 if (!stylesheets && !mutations) | 619 if (!stylesheets && !mutations) |
| 589 stylesheets = this.document.styleSheets; | 620 stylesheets = this.document.styleSheets; |
| 590 | 621 |
| 591 // If there are any DOM mutations and any of the patterns depends on both | 622 // If there are any DOM mutations and any of the patterns depends on both |
| 592 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the | 623 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the |
| 593 // rules in every style sheet in the document, because we need to run | 624 // rules in every style sheet in the document, because we need to run |
| 594 // querySelectorAll afterwards. On the other hand, if we only have patterns | 625 // querySelectorAll afterwards. On the other hand, if we only have patterns |
| 595 // that depend on either styles or DOM both not both | 626 // that depend on either styles or DOM both not both |
| 596 // (e.g. -abp-properties or -abp-contains), we can skip this part. | 627 // (e.g. -abp-properties or -abp-contains), we can skip this part. |
| 597 if (mutations && patterns.some(patternDependsOnStylesAndDOM)) | 628 if (mutations && patterns.some(pattern => pattern.dependsOnStylesAndDOM)) |
| 629 { | |
| 598 stylesheets = this.document.styleSheets; | 630 stylesheets = this.document.styleSheets; |
| 631 } | |
| 599 | 632 |
| 600 for (let stylesheet of stylesheets || []) | 633 for (let stylesheet of stylesheets || []) |
| 601 { | 634 { |
| 602 // Explicitly ignore third-party stylesheets to ensure consistent behavior | 635 // Explicitly ignore third-party stylesheets to ensure consistent behavior |
| 603 // between Firefox and Chrome. | 636 // between Firefox and Chrome. |
| 604 if (!this.isSameOrigin(stylesheet)) | 637 if (!this.isSameOrigin(stylesheet)) |
| 605 continue; | 638 continue; |
| 606 | 639 |
| 607 let rules = stylesheet.cssRules; | 640 let rules = stylesheet.cssRules; |
| 608 if (!rules) | 641 if (!rules) |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 637 | 670 |
| 638 pattern = patterns.shift(); | 671 pattern = patterns.shift(); |
| 639 | 672 |
| 640 generator = evaluate(pattern.selectors, 0, "", | 673 generator = evaluate(pattern.selectors, 0, "", |
| 641 this.document, cssStyles); | 674 this.document, cssStyles); |
| 642 } | 675 } |
| 643 for (let selector of generator) | 676 for (let selector of generator) |
| 644 { | 677 { |
| 645 if (selector != null) | 678 if (selector != null) |
| 646 { | 679 { |
| 647 if (isSelectorHidingOnlyPattern(pattern)) | 680 if (pattern.isSelectorHidingOnlyPattern()) |
| 648 { | 681 { |
| 649 selectors.push(selector); | 682 selectors.push(selector); |
| 650 selectorFilters.push(pattern.text); | 683 selectorFilters.push(pattern.text); |
| 651 } | 684 } |
| 652 else | 685 else |
| 653 { | 686 { |
| 654 for (let element of this.document.querySelectorAll(selector)) | 687 for (let element of this.document.querySelectorAll(selector)) |
| 655 { | 688 { |
| 656 elements.push(element); | 689 elements.push(element); |
| 657 elementFilters.push(pattern.text); | 690 elementFilters.push(pattern.text); |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 780 this.queueFiltering(null, mutations); | 813 this.queueFiltering(null, mutations); |
| 781 }, | 814 }, |
| 782 | 815 |
| 783 apply(patterns) | 816 apply(patterns) |
| 784 { | 817 { |
| 785 this.patterns = []; | 818 this.patterns = []; |
| 786 for (let pattern of patterns) | 819 for (let pattern of patterns) |
| 787 { | 820 { |
| 788 let selectors = this.parseSelector(pattern.selector); | 821 let selectors = this.parseSelector(pattern.selector); |
| 789 if (selectors != null && selectors.length > 0) | 822 if (selectors != null && selectors.length > 0) |
| 790 this.patterns.push({selectors, text: pattern.text}); | 823 this.patterns.push(new Pattern(selectors, pattern.text)); |
| 791 } | 824 } |
| 792 | 825 |
| 793 if (this.patterns.length > 0) | 826 if (this.patterns.length > 0) |
| 794 { | 827 { |
| 795 this.queueFiltering(); | 828 this.queueFiltering(); |
| 796 this.observer.observe( | 829 this.observer.observe( |
| 797 this.document, | 830 this.document, |
| 798 { | 831 { |
| 799 childList: true, | 832 childList: true, |
| 800 attributes: shouldObserveAttributes(this.patterns), | 833 attributes: shouldObserveAttributes(this.patterns), |
| 801 characterData: shouldObserveCharacterData(this.patterns), | 834 characterData: shouldObserveCharacterData(this.patterns), |
| 802 subtree: true | 835 subtree: true |
| 803 } | 836 } |
| 804 ); | 837 ); |
| 805 this.document.addEventListener("load", this.onLoad.bind(this), true); | 838 this.document.addEventListener("load", this.onLoad.bind(this), true); |
| 806 } | 839 } |
| 807 } | 840 } |
| 808 }; | 841 }; |
| 809 | 842 |
| 810 exports.ElemHideEmulation = ElemHideEmulation; | 843 exports.ElemHideEmulation = ElemHideEmulation; |
| OLD | NEW |