| 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 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 237 | 237 |
| 238 const incompletePrefixRegexp = /[\s>+~]$/; | 238 const incompletePrefixRegexp = /[\s>+~]$/; |
| 239 | 239 |
| 240 function HasSelector(selectors) | 240 function HasSelector(selectors) |
| 241 { | 241 { |
| 242 this._innerSelectors = selectors; | 242 this._innerSelectors = selectors; |
| 243 } | 243 } |
| 244 | 244 |
| 245 HasSelector.prototype = { | 245 HasSelector.prototype = { |
| 246 requiresHiding: true, | 246 requiresHiding: true, |
| 247 dependsOnDOM: true, | |
|
Manish Jethani
2018/03/01 17:18:11
-abp-has and -abp-contains definitely, one hundred
| |
| 247 | 248 |
| 248 get dependsOnStyles() | 249 get dependsOnStyles() |
| 249 { | 250 { |
| 250 return this._innerSelectors.some(selector => selector.dependsOnStyles); | 251 return this._innerSelectors.some(selector => selector.dependsOnStyles); |
| 251 }, | 252 }, |
| 252 | 253 |
| 253 get dependsOnCharacterData() | 254 get dependsOnCharacterData() |
| 254 { | 255 { |
| 255 return this._innerSelectors.some( | 256 return this._innerSelectors.some( |
| 256 selector => selector.dependsOnCharacterData | 257 selector => selector.dependsOnCharacterData |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 299 } | 300 } |
| 300 }; | 301 }; |
| 301 | 302 |
| 302 function ContainsSelector(textContent) | 303 function ContainsSelector(textContent) |
| 303 { | 304 { |
| 304 this._regexp = makeRegExpParameter(textContent); | 305 this._regexp = makeRegExpParameter(textContent); |
| 305 } | 306 } |
| 306 | 307 |
| 307 ContainsSelector.prototype = { | 308 ContainsSelector.prototype = { |
| 308 requiresHiding: true, | 309 requiresHiding: true, |
| 310 dependsOnDOM: true, | |
| 309 dependsOnCharacterData: true, | 311 dependsOnCharacterData: true, |
| 310 | 312 |
| 311 *getSelectors(prefix, subtree, stylesheet) | 313 *getSelectors(prefix, subtree, stylesheet) |
| 312 { | 314 { |
| 313 for (let element of this.getElements(prefix, subtree, stylesheet)) | 315 for (let element of this.getElements(prefix, subtree, stylesheet)) |
| 314 yield [makeSelector(element, ""), subtree]; | 316 yield [makeSelector(element, ""), subtree]; |
| 315 }, | 317 }, |
| 316 | 318 |
| 317 *getElements(prefix, subtree, stylesheet) | 319 *getElements(prefix, subtree, stylesheet) |
| 318 { | 320 { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 376 yield [selector, subtree]; | 378 yield [selector, subtree]; |
| 377 } | 379 } |
| 378 }; | 380 }; |
| 379 | 381 |
| 380 function isSelectorHidingOnlyPattern(pattern) | 382 function isSelectorHidingOnlyPattern(pattern) |
| 381 { | 383 { |
| 382 return pattern.selectors.some(s => s.preferHideWithSelector) && | 384 return pattern.selectors.some(s => s.preferHideWithSelector) && |
| 383 !pattern.selectors.some(s => s.requiresHiding); | 385 !pattern.selectors.some(s => s.requiresHiding); |
| 384 } | 386 } |
| 385 | 387 |
| 388 function patternDependsOnStyles(pattern) | |
| 389 { | |
| 390 return pattern.selectors.some(s => s.dependsOnStyles); | |
| 391 } | |
| 392 | |
| 393 function patternDependsOnDOM(pattern) | |
| 394 { | |
| 395 return pattern.selectors.some(s => s.dependsOnDOM); | |
| 396 } | |
| 397 | |
| 398 function patternDependsOnBothStylesAndDOM(pattern) | |
| 399 { | |
| 400 return pattern.selectors.some(s => s.dependsOnStyles && s.dependsOnDOM); | |
| 401 } | |
| 402 | |
| 403 function shouldProcessPattern(pattern, stylesheets, mutations) | |
| 404 { | |
| 405 if (!stylesheets && !mutations) | |
| 406 // Process everything. | |
| 407 return true; | |
| 408 | |
| 409 if (!stylesheets && !patternDependsOnDOM(pattern)) | |
| 410 return false; | |
| 411 | |
| 412 if (!mutations && !patternDependsOnStyles(pattern)) | |
| 413 return false; | |
| 414 | |
| 415 return true; | |
| 416 } | |
| 417 | |
| 386 function shouldObserveAttributes(patterns) | 418 function shouldObserveAttributes(patterns) |
| 387 { | 419 { |
| 388 // Observe changes to attributes if either there's a plain selector that | 420 // Observe changes to attributes if either there's a plain selector that |
| 389 // looks like an ID selector, class selector, or attribute selector in one of | 421 // looks like an ID selector, class selector, or attribute selector in one of |
| 390 // the patterns (e.g. "a[href='https://example.com/']") | 422 // the patterns (e.g. "a[href='https://example.com/']") |
| 391 // or there's a properties selector nested inside a has selector | 423 // or there's a properties selector nested inside a has selector |
| 392 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") | 424 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") |
| 393 return patterns.some( | 425 return patterns.some( |
| 394 pattern => pattern.selectors.some( | 426 pattern => pattern.selectors.some( |
| 395 selector => selector.maybeDependsOnAttributes || | 427 selector => selector.maybeDependsOnAttributes || |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 495 return selectors; | 527 return selectors; |
| 496 }, | 528 }, |
| 497 | 529 |
| 498 /** | 530 /** |
| 499 * Processes the current document and applies all rules to it. | 531 * Processes the current document and applies all rules to it. |
| 500 * @param {CSSStyleSheet[]} [stylesheets] | 532 * @param {CSSStyleSheet[]} [stylesheets] |
| 501 * The list of new stylesheets that have been added to the document and | 533 * The list of new stylesheets that have been added to the document and |
| 502 * made reprocessing necessary. This parameter shouldn't be passed in for | 534 * made reprocessing necessary. This parameter shouldn't be passed in for |
| 503 * the initial processing, all of document's stylesheets will be considered | 535 * the initial processing, all of document's stylesheets will be considered |
| 504 * then and all rules, including the ones not dependent on styles. | 536 * then and all rules, including the ones not dependent on styles. |
| 537 * @param {MutationRecord[]} [mutations] | |
| 538 * The list of DOM mutations that have been applied to the document and | |
| 539 * made reprocessing necessary. This parameter shouldn't be passed in for | |
| 540 * the initial processing, the entire document will be considered | |
| 541 * then and all rules, including the ones not dependent on the DOM. | |
| 505 * @param {function} [done] | 542 * @param {function} [done] |
| 506 * Callback to call when done. | 543 * Callback to call when done. |
| 507 */ | 544 */ |
| 508 _addSelectors(stylesheets, done) | 545 _addSelectors(stylesheets, mutations, done) |
| 509 { | 546 { |
| 510 let selectors = []; | 547 let selectors = []; |
| 511 let selectorFilters = []; | 548 let selectorFilters = []; |
| 512 | 549 |
| 513 let elements = []; | 550 let elements = []; |
| 514 let elementFilters = []; | 551 let elementFilters = []; |
| 515 | 552 |
| 516 let cssStyles = []; | 553 let cssStyles = []; |
| 517 | 554 |
| 518 let stylesheetOnlyChange = !!stylesheets; | 555 let patterns = null; |
| 519 if (!stylesheets) | 556 |
| 557 // If neither any style sheets nor any DOM mutations have been specified, | |
| 558 // do full processing. | |
| 559 if (!stylesheets && !mutations) | |
| 560 { | |
| 561 patterns = this.patterns.slice(); | |
| 562 stylesheets = this.document.styleSheets; | |
| 563 } | |
| 564 else | |
| 565 { | |
| 566 patterns = this.patterns.filter( | |
| 567 pattern => shouldProcessPattern(pattern, stylesheets, mutations) | |
| 568 ); | |
| 569 } | |
| 570 | |
| 571 // If there are any DOM mutations and any of the patterns depends on both | |
| 572 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the | |
| 573 // rules in every style sheet in the document, because we need to run | |
| 574 // querySelectorAll afterwards. On the other hand, if we only have patterns | |
| 575 // that depend on either styles or DOM both not both | |
| 576 // (e.g. -abp-properties or -abp-contains), we can skip this part. | |
| 577 if (mutations && patterns.some(patternDependsOnBothStylesAndDOM)) | |
| 520 stylesheets = this.document.styleSheets; | 578 stylesheets = this.document.styleSheets; |
| 521 | 579 |
| 522 for (let stylesheet of stylesheets) | 580 for (let stylesheet of stylesheets || []) |
| 523 { | 581 { |
| 524 // Explicitly ignore third-party stylesheets to ensure consistent behavior | 582 // Explicitly ignore third-party stylesheets to ensure consistent behavior |
| 525 // between Firefox and Chrome. | 583 // between Firefox and Chrome. |
| 526 if (!this.isSameOrigin(stylesheet)) | 584 if (!this.isSameOrigin(stylesheet)) |
| 527 continue; | 585 continue; |
| 528 | 586 |
| 529 let rules = stylesheet.cssRules; | 587 let rules = stylesheet.cssRules; |
| 530 if (!rules) | 588 if (!rules) |
| 531 continue; | 589 continue; |
| 532 | 590 |
| 533 for (let rule of rules) | 591 for (let rule of rules) |
| 534 { | 592 { |
| 535 if (rule.type != rule.STYLE_RULE) | 593 if (rule.type != rule.STYLE_RULE) |
| 536 continue; | 594 continue; |
| 537 | 595 |
| 538 cssStyles.push(stringifyStyle(rule)); | 596 cssStyles.push(stringifyStyle(rule)); |
| 539 } | 597 } |
| 540 } | 598 } |
| 541 | 599 |
| 542 let patterns = this.patterns.slice(); | |
| 543 let pattern = null; | 600 let pattern = null; |
| 544 let generator = null; | 601 let generator = null; |
| 545 | 602 |
| 546 let processPatterns = () => | 603 let processPatterns = () => |
| 547 { | 604 { |
| 548 let cycleStart = performance.now(); | 605 let cycleStart = performance.now(); |
| 549 | 606 |
| 550 if (!pattern) | 607 if (!pattern) |
| 551 { | 608 { |
| 552 if (!patterns.length) | 609 if (!patterns.length) |
| 553 { | 610 { |
| 554 this.addSelectorsFunc(selectors, selectorFilters); | 611 this.addSelectorsFunc(selectors, selectorFilters); |
| 555 this.hideElemsFunc(elements, elementFilters); | 612 this.hideElemsFunc(elements, elementFilters); |
| 556 if (typeof done == "function") | 613 if (typeof done == "function") |
| 557 done(); | 614 done(); |
| 558 return; | 615 return; |
| 559 } | 616 } |
| 560 | 617 |
| 561 pattern = patterns.shift(); | 618 pattern = patterns.shift(); |
| 562 | 619 |
| 563 if (stylesheetOnlyChange && | |
|
Manish Jethani
2018/03/01 17:18:11
This is taken care of in shouldProcessPattern now.
| |
| 564 !pattern.selectors.some(selector => selector.dependsOnStyles)) | |
| 565 { | |
| 566 pattern = null; | |
| 567 return processPatterns(); | |
| 568 } | |
| 569 generator = evaluate(pattern.selectors, 0, "", | 620 generator = evaluate(pattern.selectors, 0, "", |
| 570 this.document, cssStyles); | 621 this.document, cssStyles); |
| 571 } | 622 } |
| 572 for (let selector of generator) | 623 for (let selector of generator) |
| 573 { | 624 { |
| 574 if (selector != null) | 625 if (selector != null) |
| 575 { | 626 { |
| 576 if (isSelectorHidingOnlyPattern(pattern)) | 627 if (isSelectorHidingOnlyPattern(pattern)) |
| 577 { | 628 { |
| 578 selectors.push(selector); | 629 selectors.push(selector); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 613 }, | 664 }, |
| 614 | 665 |
| 615 _filteringInProgress: false, | 666 _filteringInProgress: false, |
| 616 _lastInvocation: -MIN_INVOCATION_INTERVAL, | 667 _lastInvocation: -MIN_INVOCATION_INTERVAL, |
| 617 _scheduledProcessing: null, | 668 _scheduledProcessing: null, |
| 618 | 669 |
| 619 /** | 670 /** |
| 620 * Re-run filtering either immediately or queued. | 671 * Re-run filtering either immediately or queued. |
| 621 * @param {CSSStyleSheet[]} [stylesheets] | 672 * @param {CSSStyleSheet[]} [stylesheets] |
| 622 * new stylesheets to be processed. This parameter should be omitted | 673 * new stylesheets to be processed. This parameter should be omitted |
| 623 * for DOM modification (full reprocessing required). | 674 * for full reprocessing. |
| 675 * @param {MutationRecord[]} [mutations] | |
| 676 * new DOM mutations to be processed. This parameter should be omitted | |
| 677 * for full reprocessing. | |
| 624 */ | 678 */ |
| 625 queueFiltering(stylesheets) | 679 queueFiltering(stylesheets, mutations) |
|
Manish Jethani
2018/03/01 17:18:11
The addition of the mutations parameter here is ma
| |
| 626 { | 680 { |
| 627 let completion = () => | 681 let completion = () => |
| 628 { | 682 { |
| 629 this._lastInvocation = performance.now(); | 683 this._lastInvocation = performance.now(); |
| 630 this._filteringInProgress = false; | 684 this._filteringInProgress = false; |
| 631 if (this._scheduledProcessing) | 685 if (this._scheduledProcessing) |
| 632 { | 686 { |
| 633 let newStylesheets = this._scheduledProcessing.stylesheets; | 687 let newStylesheets = this._scheduledProcessing.stylesheets; |
| 688 let newMutations = this._scheduledProcessing.mutations; | |
| 634 this._scheduledProcessing = null; | 689 this._scheduledProcessing = null; |
| 635 this.queueFiltering(newStylesheets); | 690 this.queueFiltering(newStylesheets, newMutations); |
| 636 } | 691 } |
| 637 }; | 692 }; |
| 638 | 693 |
| 639 if (this._scheduledProcessing) | 694 if (this._scheduledProcessing) |
| 640 { | 695 { |
| 641 if (!stylesheets) | 696 if (!stylesheets && !mutations) |
| 697 { | |
| 642 this._scheduledProcessing.stylesheets = null; | 698 this._scheduledProcessing.stylesheets = null; |
| 643 else if (this._scheduledProcessing.stylesheets) | 699 this._scheduledProcessing.mutations = null; |
| 644 this._scheduledProcessing.stylesheets.push(...stylesheets); | 700 } |
| 701 else | |
| 702 { | |
| 703 if (stylesheets) | |
| 704 { | |
| 705 if (!this._scheduledProcessing.stylesheets) | |
| 706 this._scheduledProcessing.stylesheets = []; | |
| 707 this._scheduledProcessing.stylesheets.push(...stylesheets); | |
| 708 } | |
| 709 if (mutations) | |
| 710 { | |
| 711 if (!this._scheduledProcessing.mutations) | |
| 712 this._scheduledProcessing.mutations = []; | |
| 713 this._scheduledProcessing.mutations.push(...mutations); | |
| 714 } | |
| 715 } | |
| 645 } | 716 } |
| 646 else if (this._filteringInProgress) | 717 else if (this._filteringInProgress) |
| 647 { | 718 { |
| 648 this._scheduledProcessing = {stylesheets}; | 719 this._scheduledProcessing = {stylesheets, mutations}; |
| 649 } | 720 } |
| 650 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) | 721 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) |
| 651 { | 722 { |
| 652 this._scheduledProcessing = {stylesheets}; | 723 this._scheduledProcessing = {stylesheets, mutations}; |
| 653 setTimeout(() => | 724 setTimeout(() => |
| 654 { | 725 { |
| 655 let newStylesheets = this._scheduledProcessing.stylesheets; | 726 let newStylesheets = this._scheduledProcessing.stylesheets; |
| 727 let newMutations = this._scheduledProcessing.mutations; | |
| 656 this._filteringInProgress = true; | 728 this._filteringInProgress = true; |
| 657 this._scheduledProcessing = null; | 729 this._scheduledProcessing = null; |
| 658 this._addSelectors(newStylesheets, completion); | 730 this._addSelectors(newStylesheets, newMutations, completion); |
| 659 }, | 731 }, |
| 660 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); | 732 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); |
| 661 } | 733 } |
| 662 else if (this.document.readyState == "loading") | 734 else if (this.document.readyState == "loading") |
| 663 { | 735 { |
| 664 this._scheduledProcessing = {stylesheets}; | 736 this._scheduledProcessing = {stylesheets, mutations}; |
| 665 let handler = () => | 737 let handler = () => |
| 666 { | 738 { |
| 667 document.removeEventListener("DOMContentLoaded", handler); | 739 document.removeEventListener("DOMContentLoaded", handler); |
| 668 let newStylesheets = this._scheduledProcessing.stylesheets; | 740 let newStylesheets = this._scheduledProcessing.stylesheets; |
| 741 let newMutations = this._scheduledProcessing.mutations; | |
| 669 this._filteringInProgress = true; | 742 this._filteringInProgress = true; |
| 670 this._scheduledProcessing = null; | 743 this._scheduledProcessing = null; |
| 671 this._addSelectors(newStylesheets, completion); | 744 this._addSelectors(newStylesheets, newMutations, completion); |
| 672 }; | 745 }; |
| 673 document.addEventListener("DOMContentLoaded", handler); | 746 document.addEventListener("DOMContentLoaded", handler); |
| 674 } | 747 } |
| 675 else | 748 else |
| 676 { | 749 { |
| 677 this._filteringInProgress = true; | 750 this._filteringInProgress = true; |
| 678 this._addSelectors(stylesheets, completion); | 751 this._addSelectors(stylesheets, mutations, completion); |
| 679 } | 752 } |
| 680 }, | 753 }, |
| 681 | 754 |
| 682 onLoad(event) | 755 onLoad(event) |
| 683 { | 756 { |
| 684 let stylesheet = event.target.sheet; | 757 let stylesheet = event.target.sheet; |
| 685 if (stylesheet) | 758 if (stylesheet) |
| 686 this.queueFiltering([stylesheet]); | 759 this.queueFiltering([stylesheet]); |
| 687 }, | 760 }, |
| 688 | 761 |
| 689 observe(mutations) | 762 observe(mutations) |
| 690 { | 763 { |
| 691 this.queueFiltering(); | 764 this.queueFiltering(null, mutations); |
| 692 }, | 765 }, |
| 693 | 766 |
| 694 apply(patterns) | 767 apply(patterns) |
| 695 { | 768 { |
| 696 this.patterns = []; | 769 this.patterns = []; |
| 697 for (let pattern of patterns) | 770 for (let pattern of patterns) |
| 698 { | 771 { |
| 699 let selectors = this.parseSelector(pattern.selector); | 772 let selectors = this.parseSelector(pattern.selector); |
| 700 if (selectors != null && selectors.length > 0) | 773 if (selectors != null && selectors.length > 0) |
| 701 this.patterns.push({selectors, text: pattern.text}); | 774 this.patterns.push({selectors, text: pattern.text}); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 712 characterData: shouldObserveCharacterData(this.patterns), | 785 characterData: shouldObserveCharacterData(this.patterns), |
| 713 subtree: true | 786 subtree: true |
| 714 } | 787 } |
| 715 ); | 788 ); |
| 716 this.document.addEventListener("load", this.onLoad.bind(this), true); | 789 this.document.addEventListener("load", this.onLoad.bind(this), true); |
| 717 } | 790 } |
| 718 } | 791 } |
| 719 }; | 792 }; |
| 720 | 793 |
| 721 exports.ElemHideEmulation = ElemHideEmulation; | 794 exports.ElemHideEmulation = ElemHideEmulation; |
| OLD | NEW |