Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/content/elemHideEmulation.js

Issue 29712655: Issue 6437 - Skip styles-only patterns on DOM mutations (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Created March 1, 2018, 5:11 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 | no next file » | 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 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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;
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld