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

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

Issue 29714601: Issue 6437 - Skip elements not affected by DOM mutations (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Created March 5, 2018, 6:05 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 176 matching lines...) Expand 10 before | Expand all | Expand 10 after
187 try 187 try
188 { 188 {
189 return new RegExp(pattern, flags); 189 return new RegExp(pattern, flags);
190 } 190 }
191 catch (e) 191 catch (e)
192 { 192 {
193 } 193 }
194 return null; 194 return null;
195 } 195 }
196 196
197 function* evaluate(chain, index, prefix, subtree, styles) 197 function* evaluate(chain, index, prefix, subtree, styles, targets)
198 { 198 {
199 if (index >= chain.length) 199 if (index >= chain.length)
200 { 200 {
201 yield prefix; 201 yield prefix;
202 return; 202 return;
203 } 203 }
204 for (let [selector, element] of 204 for (let [selector, element] of
205 chain[index].getSelectors(prefix, subtree, styles)) 205 chain[index].getSelectors(prefix, subtree, styles, targets))
206 { 206 {
207 if (selector == null) 207 if (selector == null)
208 yield null; 208 yield null;
209 else 209 else
210 yield* evaluate(chain, index + 1, selector, element, styles); 210 yield* evaluate(chain, index + 1, selector, element, styles, targets);
211 } 211 }
212 // Just in case the getSelectors() generator above had to run some heavy 212 // Just in case the getSelectors() generator above had to run some heavy
213 // document.querySelectorAll() call which didn't produce any results, make 213 // document.querySelectorAll() call which didn't produce any results, make
214 // sure there is at least one point where execution can pause. 214 // sure there is at least one point where execution can pause.
215 yield null; 215 yield null;
216 } 216 }
217 217
218 function PlainSelector(selector) 218 function PlainSelector(selector)
219 { 219 {
220 this._selector = selector; 220 this._selector = selector;
221 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector); 221 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector);
222 } 222 }
223 223
224 PlainSelector.prototype = { 224 PlainSelector.prototype = {
225 /** 225 /**
226 * Generator function returning a pair of selector 226 * Generator function returning a pair of selector
227 * string and subtree. 227 * string and subtree.
228 * @param {string} prefix the prefix for the selector. 228 * @param {string} prefix the prefix for the selector.
229 * @param {Node} subtree the subtree we work on. 229 * @param {Node} subtree the subtree we work on.
230 * @param {StringifiedStyle[]} styles the stringified style objects. 230 * @param {StringifiedStyle[]} styles the stringified style objects.
231 * @param {Node[]} [targets] the nodes we are interested in.
231 */ 232 */
232 *getSelectors(prefix, subtree, styles) 233 *getSelectors(prefix, subtree, styles, targets)
233 { 234 {
234 yield [prefix + this._selector, subtree]; 235 yield [prefix + this._selector, subtree];
235 } 236 }
236 }; 237 };
237 238
238 const incompletePrefixRegexp = /[\s>+~]$/; 239 const incompletePrefixRegexp = /[\s>+~]$/;
239 240
240 function HasSelector(selectors) 241 function HasSelector(selectors)
241 { 242 {
242 this._innerSelectors = selectors; 243 this._innerSelectors = selectors;
(...skipping 15 matching lines...) Expand all
258 ); 259 );
259 }, 260 },
260 261
261 get maybeDependsOnAttributes() 262 get maybeDependsOnAttributes()
262 { 263 {
263 return this._innerSelectors.some( 264 return this._innerSelectors.some(
264 selector => selector.maybeDependsOnAttributes 265 selector => selector.maybeDependsOnAttributes
265 ); 266 );
266 }, 267 },
267 268
268 *getSelectors(prefix, subtree, styles) 269 *getSelectors(prefix, subtree, styles, targets)
269 { 270 {
270 for (let element of this.getElements(prefix, subtree, styles)) 271 for (let element of this.getElements(prefix, subtree, styles, targets))
271 yield [makeSelector(element, ""), element]; 272 yield [makeSelector(element, ""), element];
272 }, 273 },
273 274
274 /** 275 /**
275 * Generator function returning selected elements. 276 * Generator function returning selected elements.
276 * @param {string} prefix the prefix for the selector. 277 * @param {string} prefix the prefix for the selector.
277 * @param {Node} subtree the subtree we work on. 278 * @param {Node} subtree the subtree we work on.
278 * @param {StringifiedStyle[]} styles the stringified style objects. 279 * @param {StringifiedStyle[]} styles the stringified style objects.
280 * @param {Node[]} [targets] the nodes we are interested in.
279 */ 281 */
280 *getElements(prefix, subtree, styles) 282 *getElements(prefix, subtree, styles, targets)
281 { 283 {
282 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? 284 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
283 prefix + "*" : prefix; 285 prefix + "*" : prefix;
284 let elements = scopedQuerySelectorAll(subtree, actualPrefix); 286 let elements = scopedQuerySelectorAll(subtree, actualPrefix);
285 if (elements) 287 if (elements)
286 { 288 {
287 for (let element of elements) 289 for (let element of elements)
288 { 290 {
289 let iter = evaluate(this._innerSelectors, 0, "", element, styles); 291 // If the element is neither an ancestor nor a descendant of one of the
292 // targets, we can skip it.
293 if (targets && !targets.some(target => element.contains(target) ||
294 target.contains(element)))
295 {
296 yield null;
297 continue;
298 }
299
300 let iter = evaluate(this._innerSelectors, 0, "", element, styles,
301 targets);
290 for (let selector of iter) 302 for (let selector of iter)
291 { 303 {
292 if (selector == null) 304 if (selector == null)
293 yield null; 305 yield null;
294 else if (scopedQuerySelector(element, selector)) 306 else if (scopedQuerySelector(element, selector))
295 yield element; 307 yield element;
296 } 308 }
297 yield null; 309 yield null;
298 } 310 }
299 } 311 }
300 } 312 }
301 }; 313 };
302 314
303 function ContainsSelector(textContent) 315 function ContainsSelector(textContent)
304 { 316 {
305 this._regexp = makeRegExpParameter(textContent); 317 this._regexp = makeRegExpParameter(textContent);
306 } 318 }
307 319
308 ContainsSelector.prototype = { 320 ContainsSelector.prototype = {
309 requiresHiding: true, 321 requiresHiding: true,
310 dependsOnDOM: true, 322 dependsOnDOM: true,
311 dependsOnCharacterData: true, 323 dependsOnCharacterData: true,
312 324
313 *getSelectors(prefix, subtree, styles) 325 *getSelectors(prefix, subtree, styles, targets)
314 { 326 {
315 for (let element of this.getElements(prefix, subtree, styles)) 327 for (let element of this.getElements(prefix, subtree, styles, targets))
316 yield [makeSelector(element, ""), subtree]; 328 yield [makeSelector(element, ""), subtree];
317 }, 329 },
318 330
319 *getElements(prefix, subtree, styles) 331 *getElements(prefix, subtree, styles, targets)
320 { 332 {
321 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? 333 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
322 prefix + "*" : prefix; 334 prefix + "*" : prefix;
323 335
324 let elements = scopedQuerySelectorAll(subtree, actualPrefix); 336 let elements = scopedQuerySelectorAll(subtree, actualPrefix);
325 if (elements) 337 if (elements)
326 { 338 {
327 for (let element of elements) 339 for (let element of elements)
328 { 340 {
341 if (targets && !targets.some(target => element.contains(target) ||
342 target.contains(element)))
343 {
344 yield null;
345 continue;
346 }
347
329 if (this._regexp && this._regexp.test(element.textContent)) 348 if (this._regexp && this._regexp.test(element.textContent))
330 yield element; 349 yield element;
331 else 350 else
332 yield null; 351 yield null;
333 } 352 }
334 } 353 }
335 } 354 }
336 }; 355 };
337 356
338 function PropsSelector(propertyExpression) 357 function PropsSelector(propertyExpression)
(...skipping 26 matching lines...) Expand all
365 { 384 {
366 subSelector = subSelector.substr(1); 385 subSelector = subSelector.substr(1);
367 } 386 }
368 let idx = subSelector.lastIndexOf("::"); 387 let idx = subSelector.lastIndexOf("::");
369 if (idx != -1) 388 if (idx != -1)
370 subSelector = subSelector.substr(0, idx); 389 subSelector = subSelector.substr(0, idx);
371 yield prefix + subSelector; 390 yield prefix + subSelector;
372 } 391 }
373 }, 392 },
374 393
375 *getSelectors(prefix, subtree, styles) 394 *getSelectors(prefix, subtree, styles, targets)
376 { 395 {
377 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) 396 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp))
378 yield [selector, subtree]; 397 yield [selector, subtree];
379 } 398 }
380 }; 399 };
381 400
382 function isSelectorHidingOnlyPattern(pattern) 401 function isSelectorHidingOnlyPattern(pattern)
383 { 402 {
384 return pattern.selectors.some(s => s.preferHideWithSelector) && 403 return pattern.selectors.some(s => s.preferHideWithSelector) &&
385 !pattern.selectors.some(s => s.requiresHiding); 404 !pattern.selectors.some(s => s.requiresHiding);
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 459
441 // There are only 3 types of mutations: "attributes", "characterData", and 460 // There are only 3 types of mutations: "attributes", "characterData", and
442 // "childList". 461 // "childList".
443 if (types.size == 3) 462 if (types.size == 3)
444 break; 463 break;
445 } 464 }
446 465
447 return types; 466 return types;
448 } 467 }
449 468
469 function extractMutationTargets(mutations)
470 {
471 if (!mutations)
472 return null;
473
474 let targets = new Set();
475
476 for (let mutation of mutations)
477 {
478 if (mutation.type == "childList")
479 {
480 // When new nodes are added, we're interested in the added nodes rather
481 // than the parent.
482 for (let node of mutation.addedNodes)
483 targets.add(node);
484
485 // Ideally we would also be interested in removed nodes, but since we
486 // never unhide an element once hidden we can simply ignore any removed
487 // nodes. Note that this will change once we start using CSS selectors
488 // for -abp-has and -abp-contains, i.e. we'll have to remove the
489 // selectors for any removed nodes.
490 }
491 else
492 {
493 targets.add(mutation.target);
494 }
495 }
496
497 return [...targets];
498 }
499
450 function filterPatterns(patterns, {stylesheets, mutations}) 500 function filterPatterns(patterns, {stylesheets, mutations})
451 { 501 {
452 if (!stylesheets && !mutations) 502 if (!stylesheets && !mutations)
453 return patterns.slice(); 503 return patterns.slice();
454 504
455 let mutationTypes = mutations ? extractMutationTypes(mutations) : null; 505 let mutationTypes = mutations ? extractMutationTypes(mutations) : null;
456 506
457 return patterns.filter( 507 return patterns.filter(
458 pattern => (stylesheets && patternDependsOnStyles(pattern)) || 508 pattern => (stylesheets && patternDependsOnStyles(pattern)) ||
459 (mutations && patternDependsOnDOM(pattern) && 509 (mutations && patternDependsOnDOM(pattern) &&
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
610 660
611 for (let rule of rules) 661 for (let rule of rules)
612 { 662 {
613 if (rule.type != rule.STYLE_RULE) 663 if (rule.type != rule.STYLE_RULE)
614 continue; 664 continue;
615 665
616 cssStyles.push(stringifyStyle(rule)); 666 cssStyles.push(stringifyStyle(rule));
617 } 667 }
618 } 668 }
619 669
670 let targets = extractMutationTargets(mutations);
671
620 let pattern = null; 672 let pattern = null;
621 let generator = null; 673 let generator = null;
622 674
623 let processPatterns = () => 675 let processPatterns = () =>
624 { 676 {
625 let cycleStart = performance.now(); 677 let cycleStart = performance.now();
626 678
627 if (!pattern) 679 if (!pattern)
628 { 680 {
629 if (!patterns.length) 681 if (!patterns.length)
630 { 682 {
631 this.addSelectorsFunc(selectors, selectorFilters); 683 this.addSelectorsFunc(selectors, selectorFilters);
632 this.hideElemsFunc(elements, elementFilters); 684 this.hideElemsFunc(elements, elementFilters);
633 if (typeof done == "function") 685 if (typeof done == "function")
634 done(); 686 done();
635 return; 687 return;
636 } 688 }
637 689
638 pattern = patterns.shift(); 690 pattern = patterns.shift();
639 691
640 generator = evaluate(pattern.selectors, 0, "", 692 generator = evaluate(pattern.selectors, 0, "",
641 this.document, cssStyles); 693 this.document, cssStyles, targets);
642 } 694 }
643 for (let selector of generator) 695 for (let selector of generator)
644 { 696 {
645 if (selector != null) 697 if (selector != null)
646 { 698 {
647 if (isSelectorHidingOnlyPattern(pattern)) 699 if (isSelectorHidingOnlyPattern(pattern))
648 { 700 {
649 selectors.push(selector); 701 selectors.push(selector);
650 selectorFilters.push(pattern.text); 702 selectorFilters.push(pattern.text);
651 } 703 }
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
801 characterData: shouldObserveCharacterData(this.patterns), 853 characterData: shouldObserveCharacterData(this.patterns),
802 subtree: true 854 subtree: true
803 } 855 }
804 ); 856 );
805 this.document.addEventListener("load", this.onLoad.bind(this), true); 857 this.document.addEventListener("load", this.onLoad.bind(this), true);
806 } 858 }
807 } 859 }
808 }; 860 };
809 861
810 exports.ElemHideEmulation = ElemHideEmulation; 862 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