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

Delta Between Two Patch Sets: lib/content/elemHideEmulation.js

Issue 29714601: Issue 6437 - Skip elements not affected by DOM mutations (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Rebase Created May 11, 2018, 12:45 p.m.
Right Patch Set: Rename debug mode to test mode Created May 18, 2018, 3:41 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | test/browser/elemHideEmulation.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 "use strict"; 18 "use strict";
19 19
20 const {textToRegExp, filterToRegExp, splitSelector} = require("../common"); 20 const {textToRegExp, filterToRegExp, splitSelector} = require("../common");
21 const {indexOf} = require("../coreUtils"); 21 const {indexOf} = require("../coreUtils");
22 22
23 let MIN_INVOCATION_INTERVAL = 3000; 23 let MIN_INVOCATION_INTERVAL = 3000;
24 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50; 24 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50;
25 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i; 25 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i;
26 26
27 let testInfo = null;
28
29 function setTestMode()
30 {
31 testInfo = {
32 lastProcessedElements: new Set()
33 };
34 }
35
36 exports.setTestMode = setTestMode;
37
38 function getTestInfo()
39 {
40 return testInfo;
41 }
42
43 exports.getTestInfo = getTestInfo;
hub 2018/05/24 19:19:09 I'm a bit skeptical with that way or test, includi
Manish Jethani 2018/05/25 07:21:25 We can work on improving our test framework, but f
44
27 function getCachedPropertyValue(object, name, defaultValueFunc = () => {}) 45 function getCachedPropertyValue(object, name, defaultValueFunc = () => {})
28 { 46 {
29 let value = object[name]; 47 let value = object[name];
30 if (typeof value == "undefined") 48 if (typeof value == "undefined")
31 Object.defineProperty(object, name, {value: value = defaultValueFunc()}); 49 Object.defineProperty(object, name, {value: value = defaultValueFunc()});
32 return value; 50 return value;
33 } 51 }
34 52
35 /** Return position of node from parent. 53 /** Return position of node from parent.
36 * @param {Node} node the node to find the position of. 54 * @param {Node} node the node to find the position of.
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 // document.querySelectorAll() call which didn't produce any results, make 236 // document.querySelectorAll() call which didn't produce any results, make
219 // sure there is at least one point where execution can pause. 237 // sure there is at least one point where execution can pause.
220 yield null; 238 yield null;
221 } 239 }
222 240
223 function PlainSelector(selector) 241 function PlainSelector(selector)
224 { 242 {
225 this._selector = selector; 243 this._selector = selector;
226 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector); 244 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector);
227 this.dependsOnDOM = this.maybeDependsOnAttributes; 245 this.dependsOnDOM = this.maybeDependsOnAttributes;
246 this.maybeContainsSiblingCombinators = /[~+]/.test(selector);
228 } 247 }
229 248
230 PlainSelector.prototype = { 249 PlainSelector.prototype = {
231 /** 250 /**
232 * Generator function returning a pair of selector 251 * Generator function returning a pair of selector
233 * string and subtree. 252 * string and subtree.
234 * @param {string} prefix the prefix for the selector. 253 * @param {string} prefix the prefix for the selector.
235 * @param {Node} subtree the subtree we work on. 254 * @param {Node} subtree the subtree we work on.
236 * @param {StringifiedStyle[]} styles the stringified style objects. 255 * @param {StringifiedStyle[]} styles the stringified style objects.
237 * @param {Node[]} [targets] the nodes we are interested in. 256 * @param {Node[]} [targets] the nodes we are interested in.
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 let iter = evaluate(this._innerSelectors, 0, "", element, styles, 324 let iter = evaluate(this._innerSelectors, 0, "", element, styles,
306 targets); 325 targets);
307 for (let selector of iter) 326 for (let selector of iter)
308 { 327 {
309 if (selector == null) 328 if (selector == null)
310 yield null; 329 yield null;
311 else if (scopedQuerySelector(element, selector)) 330 else if (scopedQuerySelector(element, selector))
312 yield element; 331 yield element;
313 } 332 }
314 yield null; 333 yield null;
334
335 if (testInfo)
336 testInfo.lastProcessedElements.add(element);
315 } 337 }
316 } 338 }
317 } 339 }
318 }; 340 };
319 341
320 function ContainsSelector(textContent) 342 function ContainsSelector(textContent)
321 { 343 {
322 this._regexp = makeRegExpParameter(textContent); 344 this._regexp = makeRegExpParameter(textContent);
323 } 345 }
324 346
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
359 target.contains(element))) 381 target.contains(element)))
360 { 382 {
361 yield null; 383 yield null;
362 continue; 384 continue;
363 } 385 }
364 386
365 if (this._regexp && this._regexp.test(element.textContent)) 387 if (this._regexp && this._regexp.test(element.textContent))
366 yield element; 388 yield element;
367 else 389 else
368 yield null; 390 yield null;
391
392 if (testInfo)
393 testInfo.lastProcessedElements.add(element);
369 } 394 }
370 } 395 }
371 } 396 }
372 }; 397 };
373 398
374 function PropsSelector(propertyExpression) 399 function PropsSelector(propertyExpression)
375 { 400 {
376 let regexpString; 401 let regexpString;
377 if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && 402 if (propertyExpression.length >= 2 && propertyExpression[0] == "/" &&
378 propertyExpression[propertyExpression.length - 1] == "/") 403 propertyExpression[propertyExpression.length - 1] == "/")
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 get dependsOnCharacterData() 491 get dependsOnCharacterData()
467 { 492 {
468 // Observe changes to character data only if there's a contains selector in 493 // Observe changes to character data only if there's a contains selector in
469 // one of the patterns. 494 // one of the patterns.
470 return getCachedPropertyValue( 495 return getCachedPropertyValue(
471 this, "_dependsOnCharacterData", 496 this, "_dependsOnCharacterData",
472 () => this.selectors.some(selector => selector.dependsOnCharacterData) 497 () => this.selectors.some(selector => selector.dependsOnCharacterData)
473 ); 498 );
474 }, 499 },
475 500
501 get maybeContainsSiblingCombinators()
502 {
503 return getCachedPropertyValue(
504 this, "_maybeContainsSiblingCombinators",
505 () => this.selectors.some(selector =>
506 selector.maybeContainsSiblingCombinators)
507 );
508 },
509
476 matchesMutationTypes(mutationTypes) 510 matchesMutationTypes(mutationTypes)
477 { 511 {
478 let mutationTypeMatchMap = getCachedPropertyValue( 512 let mutationTypeMatchMap = getCachedPropertyValue(
479 this, "_mutationTypeMatchMap", 513 this, "_mutationTypeMatchMap",
480 () => new Map([ 514 () => new Map([
481 // All types of DOM-dependent patterns are affected by mutations of 515 // All types of DOM-dependent patterns are affected by mutations of
482 // type "childList". 516 // type "childList".
483 ["childList", true], 517 ["childList", true],
484 ["attributes", this.maybeDependsOnAttributes], 518 ["attributes", this.maybeDependsOnAttributes],
485 ["characterData", this.dependsOnCharacterData] 519 ["characterData", this.dependsOnCharacterData]
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
521 let targets = new Set(); 555 let targets = new Set();
522 556
523 for (let mutation of mutations) 557 for (let mutation of mutations)
524 { 558 {
525 if (mutation.type == "childList") 559 if (mutation.type == "childList")
526 { 560 {
527 // When new nodes are added, we're interested in the added nodes rather 561 // When new nodes are added, we're interested in the added nodes rather
528 // than the parent. 562 // than the parent.
529 for (let node of mutation.addedNodes) 563 for (let node of mutation.addedNodes)
530 targets.add(node); 564 targets.add(node);
531
532 // Ideally we would also be interested in removed nodes, but since we
533 // never unhide an element once hidden we can simply ignore any removed
534 // nodes. Note that this will change once we start using CSS selectors
535 // for -abp-has and -abp-contains, i.e. we'll have to remove the
536 // selectors for any removed nodes.
537 } 565 }
538 else 566 else
539 { 567 {
540 targets.add(mutation.target); 568 targets.add(mutation.target);
541 } 569 }
542 } 570 }
543 571
544 return [...targets]; 572 return [...targets];
545 } 573 }
546 574
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
664 * @param {MutationRecord[]} [mutations] 692 * @param {MutationRecord[]} [mutations]
665 * The list of DOM mutations that have been applied to the document and 693 * The list of DOM mutations that have been applied to the document and
666 * made reprocessing necessary. This parameter shouldn't be passed in for 694 * made reprocessing necessary. This parameter shouldn't be passed in for
667 * the initial processing, the entire document will be considered 695 * the initial processing, the entire document will be considered
668 * then and all rules, including the ones not dependent on the DOM. 696 * then and all rules, including the ones not dependent on the DOM.
669 * @param {function} [done] 697 * @param {function} [done]
670 * Callback to call when done. 698 * Callback to call when done.
671 */ 699 */
672 _addSelectors(stylesheets, mutations, done) 700 _addSelectors(stylesheets, mutations, done)
673 { 701 {
702 if (testInfo)
703 testInfo.lastProcessedElements.clear();
704
674 let patterns = filterPatterns(this.patterns, {stylesheets, mutations}); 705 let patterns = filterPatterns(this.patterns, {stylesheets, mutations});
675 706
676 let selectors = []; 707 let selectors = [];
677 let selectorFilters = []; 708 let selectorFilters = [];
678 709
679 let elements = []; 710 let elements = [];
680 let elementFilters = []; 711 let elementFilters = [];
681 712
682 let cssStyles = []; 713 let cssStyles = [];
683 714
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
745 this.addSelectorsFunc(selectors, selectorFilters); 776 this.addSelectorsFunc(selectors, selectorFilters);
746 if (elements.length > 0) 777 if (elements.length > 0)
747 this.hideElemsFunc(elements, elementFilters); 778 this.hideElemsFunc(elements, elementFilters);
748 if (typeof done == "function") 779 if (typeof done == "function")
749 done(); 780 done();
750 return; 781 return;
751 } 782 }
752 783
753 pattern = patterns.shift(); 784 pattern = patterns.shift();
754 785
786 let evaluationTargets = targets;
787
788 // If the pattern appears to contain any sibling combinators, we can't
789 // easily optimize based on the mutation targets. Since this is a
790 // special case, skip the optimization. By setting it to null here we
791 // make sure we process the entire DOM.
792 if (pattern.maybeContainsSiblingCombinators)
793 evaluationTargets = null;
794
795 // Ignore mutation targets when using style sheets, because we may have
796 // to update all the CSS selectors.
797 if (!this.useInlineStyles)
798 evaluationTargets = null;
799
755 generator = evaluate(pattern.selectors, 0, "", 800 generator = evaluate(pattern.selectors, 0, "",
756 this.document, cssStyles, targets); 801 this.document, cssStyles, evaluationTargets);
757 } 802 }
758 for (let selector of generator) 803 for (let selector of generator)
759 { 804 {
760 if (selector != null) 805 if (selector != null)
761 { 806 {
762 if (!this.useInlineStyles) 807 if (!this.useInlineStyles)
763 { 808 {
764 selectors.push(selector); 809 selectors.push(selector);
765 selectorFilters.push(pattern.text); 810 selectorFilters.push(pattern.text);
766 } 811 }
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
886 931
887 onLoad(event) 932 onLoad(event)
888 { 933 {
889 let stylesheet = event.target.sheet; 934 let stylesheet = event.target.sheet;
890 if (stylesheet) 935 if (stylesheet)
891 this.queueFiltering([stylesheet]); 936 this.queueFiltering([stylesheet]);
892 }, 937 },
893 938
894 observe(mutations) 939 observe(mutations)
895 { 940 {
941 if (testInfo)
942 {
943 // In test mode, filter out any mutations likely done by us
944 // (i.e. style="display: none !important"). This makes it easier to
945 // observe how the code responds to DOM mutations.
946 mutations = mutations.filter(
947 ({type, attributeName, target: {style: newValue}, oldValue}) =>
948 !(type == "attributes" && attributeName == "style" &&
949 newValue.display == "none" && oldValue.display != "none")
950 );
951
952 if (mutations.length == 0)
953 return;
954 }
hub 2018/05/24 19:19:09 ... and this :-/ There should be a better way to
Manish Jethani 2018/05/25 07:21:25 OK, do you have any suggestions? This is only done
955
896 this.queueFiltering(null, mutations); 956 this.queueFiltering(null, mutations);
897 }, 957 },
898 958
899 apply(patterns) 959 apply(patterns)
900 { 960 {
901 this.patterns = []; 961 this.patterns = [];
902 for (let pattern of patterns) 962 for (let pattern of patterns)
903 { 963 {
904 let selectors = this.parseSelector(pattern.selector); 964 let selectors = this.parseSelector(pattern.selector);
905 if (selectors != null && selectors.length > 0) 965 if (selectors != null && selectors.length > 0)
(...skipping 11 matching lines...) Expand all
917 characterData: shouldObserveCharacterData(this.patterns), 977 characterData: shouldObserveCharacterData(this.patterns),
918 subtree: true 978 subtree: true
919 } 979 }
920 ); 980 );
921 this.document.addEventListener("load", this.onLoad.bind(this), true); 981 this.document.addEventListener("load", this.onLoad.bind(this), true);
922 } 982 }
923 } 983 }
924 }; 984 };
925 985
926 exports.ElemHideEmulation = ElemHideEmulation; 986 exports.ElemHideEmulation = ElemHideEmulation;
LEFTRIGHT

Powered by Google App Engine
This is Rietveld