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 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
467 function shouldObserveAttributes(patterns) | 467 function shouldObserveAttributes(patterns) |
468 { | 468 { |
469 return patterns.some(pattern => pattern.maybeDependsOnAttributes); | 469 return patterns.some(pattern => pattern.maybeDependsOnAttributes); |
470 } | 470 } |
471 | 471 |
472 function shouldObserveCharacterData(patterns) | 472 function shouldObserveCharacterData(patterns) |
473 { | 473 { |
474 return patterns.some(pattern => pattern.dependsOnCharacterData); | 474 return patterns.some(pattern => pattern.dependsOnCharacterData); |
475 } | 475 } |
476 | 476 |
477 function updateSelectorsForMutation(mutation, selectorSet, selectors, | |
Manish Jethani
2018/03/21 04:45:44
Hopefully the inline comments are helpful.
| |
478 selectorFilters, root) | |
479 { | |
480 let {target} = mutation; | |
481 | |
482 // Existing selectors aren't affected by non-childList mutations. | |
483 if (mutation.type != "childList" || !root.contains(target)) | |
484 return false; | |
485 | |
486 // Find the mutation index, i.e. the index in the parent element where the | |
487 // mutation has occurred. | |
488 let index = 0; | |
489 let ref = mutation.previousSibling; | |
490 if (ref) | |
491 { | |
492 if (!(ref instanceof Element)) | |
493 ref = ref.previousElementSibling; | |
494 | |
495 if (ref) | |
496 index = indexOf(ref.parentNode.children, ref) + 1; | |
497 } | |
498 | |
499 // Count the number of elements added and removed. Both can be non-zero in | |
500 // some cases, e.g. when Element.innerHTML is set on the parent element to | |
501 // overwrite all the existing child nodes. | |
502 let numAddedElements = 0; | |
503 let numRemovedElements = 0; | |
504 | |
505 for (let node of mutation.addedNodes) | |
506 { | |
507 if (node instanceof Element) | |
508 numAddedElements++; | |
509 } | |
510 | |
511 for (let node of mutation.removedNodes) | |
512 { | |
513 if (node instanceof Element) | |
514 numRemovedElements++; | |
515 } | |
516 | |
517 let changed = false; | |
518 | |
519 // Go through all the selectors and find any that match the mutation target. | |
520 let targetSelector = makeSelector(target) + " > "; | |
521 for (let i = 0; i < selectors.length; i++) | |
522 { | |
523 let selector = selectors[i]; | |
524 if (selector.startsWith(targetSelector)) | |
525 { | |
526 // If there's a match, extract the one-based child index. | |
527 let subSelector = selector.substring(targetSelector.length); | |
528 let match = /^[^:]*:nth-child\((\d+)\)/.exec(subSelector); | |
529 let nthIndex = match ? +match[1] : 0; | |
530 | |
531 // If the child being targetted by the selector is before the mutation | |
532 // index (i.e. the one-based child index is not affected), do nothing. | |
533 if (nthIndex <= index) | |
534 continue; | |
535 | |
536 nthIndex -= numRemovedElements; | |
537 | |
538 // After adjusting for the number of removed elements, if the one-based | |
539 // child index appears to be lower than the mutation index, it means the | |
540 // child got removed. In that case we must delete the selector. | |
541 if (nthIndex <= index) | |
542 { | |
543 selectorSet.delete(selector); | |
544 | |
545 selectors.splice(i, 1); | |
546 selectorFilters.splice(i, 1); | |
547 i--; | |
548 | |
549 changed = true; | |
550 | |
551 continue; | |
552 } | |
553 | |
554 nthIndex += numAddedElements; | |
555 | |
556 // If the one-based child index has changed, we must update the selector. | |
557 if (nthIndex != +match[1]) | |
558 { | |
559 selectorSet.delete(selector); | |
560 selector = targetSelector + subSelector.replace(match[1], nthIndex); | |
561 selectorSet.add(selector); | |
562 selectors[i] = selector; | |
563 | |
564 changed = true; | |
565 } | |
566 } | |
567 } | |
568 | |
569 return changed; | |
570 } | |
571 | |
477 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) | 572 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) |
478 { | 573 { |
479 this.document = document; | 574 this.document = document; |
480 this.addSelectorsFunc = addSelectorsFunc; | 575 this.addSelectorsFunc = addSelectorsFunc; |
481 this.hideElemsFunc = hideElemsFunc; | 576 this.hideElemsFunc = hideElemsFunc; |
482 this.observer = new MutationObserver(this.observe.bind(this)); | 577 this.observer = new MutationObserver(this.observe.bind(this)); |
483 this.useInlineStyles = true; | 578 this.useInlineStyles = true; |
579 this.selectorSet = new Set(); | |
Manish Jethani
2018/03/21 04:45:44
selectorSet here is mainly for faster lookups and
| |
580 this.selectors = []; | |
581 this.selectorFilters = []; | |
484 } | 582 } |
485 | 583 |
486 ElemHideEmulation.prototype = { | 584 ElemHideEmulation.prototype = { |
487 isSameOrigin(stylesheet) | 585 isSameOrigin(stylesheet) |
488 { | 586 { |
489 try | 587 try |
490 { | 588 { |
491 return new URL(stylesheet.href).origin == this.document.location.origin; | 589 return new URL(stylesheet.href).origin == this.document.location.origin; |
492 } | 590 } |
493 catch (e) | 591 catch (e) |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
572 * made reprocessing necessary. This parameter shouldn't be passed in for | 670 * made reprocessing necessary. This parameter shouldn't be passed in for |
573 * the initial processing, the entire document will be considered | 671 * the initial processing, the entire document will be considered |
574 * then and all rules, including the ones not dependent on the DOM. | 672 * then and all rules, including the ones not dependent on the DOM. |
575 * @param {function} [done] | 673 * @param {function} [done] |
576 * Callback to call when done. | 674 * Callback to call when done. |
577 */ | 675 */ |
578 _addSelectors(stylesheets, mutations, done) | 676 _addSelectors(stylesheets, mutations, done) |
579 { | 677 { |
580 let patterns = filterPatterns(this.patterns, {stylesheets, mutations}); | 678 let patterns = filterPatterns(this.patterns, {stylesheets, mutations}); |
581 | 679 |
680 if (patterns.length == 0) | |
Manish Jethani
2018/03/21 04:45:44
Minor optimization, definitely don't need to proce
| |
681 return; | |
682 | |
582 let selectors = []; | 683 let selectors = []; |
583 let selectorFilters = []; | 684 let selectorFilters = []; |
584 | 685 |
585 let elements = []; | 686 let elements = []; |
586 let elementFilters = []; | 687 let elementFilters = []; |
587 | 688 |
588 let cssStyles = []; | 689 let cssStyles = []; |
589 | 690 |
590 // If neither any style sheets nor any DOM mutations have been specified, | 691 // If neither any style sheets nor any DOM mutations have been specified, |
591 // do full processing. | 692 // do full processing. |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
626 | 727 |
627 let processPatterns = () => | 728 let processPatterns = () => |
628 { | 729 { |
629 let cycleStart = performance.now(); | 730 let cycleStart = performance.now(); |
630 | 731 |
631 if (!pattern) | 732 if (!pattern) |
632 { | 733 { |
633 if (!patterns.length) | 734 if (!patterns.length) |
634 { | 735 { |
635 if (selectors.length > 0) | 736 if (selectors.length > 0) |
636 this.addSelectorsFunc(selectors, selectorFilters); | 737 { |
738 for (let selector of selectors) | |
739 this.selectorSet.add(selector); | |
740 | |
741 this.selectors.push(...selectors); | |
742 this.selectorFilters.push(...selectorFilters); | |
743 | |
744 this.addSelectorsFunc(this.selectors, | |
745 this.selectorFilters); | |
746 } | |
747 | |
637 if (elements.length > 0) | 748 if (elements.length > 0) |
638 this.hideElemsFunc(elements, elementFilters); | 749 this.hideElemsFunc(elements, elementFilters); |
750 | |
639 if (typeof done == "function") | 751 if (typeof done == "function") |
640 done(); | 752 done(); |
641 return; | 753 return; |
642 } | 754 } |
643 | 755 |
644 pattern = patterns.shift(); | 756 pattern = patterns.shift(); |
645 | 757 |
646 generator = evaluate(pattern.selectors, 0, "", | 758 generator = evaluate(pattern.selectors, 0, "", |
647 this.document, cssStyles); | 759 this.document, cssStyles); |
648 } | 760 } |
649 for (let selector of generator) | 761 for (let selector of generator) |
650 { | 762 { |
651 if (selector != null) | 763 if (selector != null) |
652 { | 764 { |
653 if (!this.useInlineStyles || | 765 if (!this.useInlineStyles || |
654 pattern.isSelectorHidingOnlyPattern()) | 766 pattern.isSelectorHidingOnlyPattern()) |
655 { | 767 { |
656 selectors.push(selector); | 768 if (!this.selectorSet.has(selector)) |
Manish Jethani
2018/03/21 04:45:44
The reason we need this Set object is that we don'
| |
657 selectorFilters.push(pattern.text); | 769 { |
770 selectors.push(selector); | |
771 selectorFilters.push(pattern.text); | |
772 } | |
658 } | 773 } |
659 else | 774 else |
660 { | 775 { |
661 for (let element of this.document.querySelectorAll(selector)) | 776 for (let element of this.document.querySelectorAll(selector)) |
662 { | 777 { |
663 elements.push(element); | 778 elements.push(element); |
664 elementFilters.push(pattern.text); | 779 elementFilters.push(pattern.text); |
665 } | 780 } |
666 } | 781 } |
667 } | 782 } |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
777 | 892 |
778 onLoad(event) | 893 onLoad(event) |
779 { | 894 { |
780 let stylesheet = event.target.sheet; | 895 let stylesheet = event.target.sheet; |
781 if (stylesheet) | 896 if (stylesheet) |
782 this.queueFiltering([stylesheet]); | 897 this.queueFiltering([stylesheet]); |
783 }, | 898 }, |
784 | 899 |
785 observe(mutations) | 900 observe(mutations) |
786 { | 901 { |
902 let selectorsChanged = false; | |
903 for (let mutation of mutations) | |
904 { | |
905 if (updateSelectorsForMutation(mutation, this.selectorSet, this.selectors, | |
906 this.selectorFilters, this.document)) | |
907 { | |
908 selectorsChanged = true; | |
909 } | |
910 } | |
911 | |
912 if (selectorsChanged) | |
Manish Jethani
2018/03/21 04:45:44
Call addSelectorsFunc immediately so the UI is upd
| |
913 this.addSelectorsFunc(this.selectors, this.selectorFilters); | |
914 | |
787 this.queueFiltering(null, mutations); | 915 this.queueFiltering(null, mutations); |
788 }, | 916 }, |
789 | 917 |
790 apply(patterns) | 918 apply(patterns) |
791 { | 919 { |
792 this.patterns = []; | 920 this.patterns = []; |
793 for (let pattern of patterns) | 921 for (let pattern of patterns) |
794 { | 922 { |
795 let selectors = this.parseSelector(pattern.selector); | 923 let selectors = this.parseSelector(pattern.selector); |
796 if (selectors != null && selectors.length > 0) | 924 if (selectors != null && selectors.length > 0) |
(...skipping 11 matching lines...) Expand all Loading... | |
808 characterData: shouldObserveCharacterData(this.patterns), | 936 characterData: shouldObserveCharacterData(this.patterns), |
809 subtree: true | 937 subtree: true |
810 } | 938 } |
811 ); | 939 ); |
812 this.document.addEventListener("load", this.onLoad.bind(this), true); | 940 this.document.addEventListener("load", this.onLoad.bind(this), true); |
813 } | 941 } |
814 } | 942 } |
815 }; | 943 }; |
816 | 944 |
817 exports.ElemHideEmulation = ElemHideEmulation; | 945 exports.ElemHideEmulation = ElemHideEmulation; |
OLD | NEW |