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: Rebase Created March 20, 2018, 10:04 a.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 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
195 try 195 try
196 { 196 {
197 return new RegExp(pattern, flags); 197 return new RegExp(pattern, flags);
198 } 198 }
199 catch (e) 199 catch (e)
200 { 200 {
201 } 201 }
202 return null; 202 return null;
203 } 203 }
204 204
205 function* evaluate(chain, index, prefix, subtree, styles) 205 function* evaluate(chain, index, prefix, subtree, styles, targets)
206 { 206 {
207 if (index >= chain.length) 207 if (index >= chain.length)
208 { 208 {
209 yield prefix; 209 yield prefix;
210 return; 210 return;
211 } 211 }
212 for (let [selector, element] of 212 for (let [selector, element] of
213 chain[index].getSelectors(prefix, subtree, styles)) 213 chain[index].getSelectors(prefix, subtree, styles, targets))
214 { 214 {
215 if (selector == null) 215 if (selector == null)
216 yield null; 216 yield null;
217 else 217 else
218 yield* evaluate(chain, index + 1, selector, element, styles); 218 yield* evaluate(chain, index + 1, selector, element, styles, targets);
219 } 219 }
220 // Just in case the getSelectors() generator above had to run some heavy 220 // Just in case the getSelectors() generator above had to run some heavy
221 // document.querySelectorAll() call which didn't produce any results, make 221 // document.querySelectorAll() call which didn't produce any results, make
222 // sure there is at least one point where execution can pause. 222 // sure there is at least one point where execution can pause.
223 yield null; 223 yield null;
224 } 224 }
225 225
226 function PlainSelector(selector) 226 function PlainSelector(selector)
227 { 227 {
228 this._selector = selector; 228 this._selector = selector;
229 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector); 229 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector);
230 } 230 }
231 231
232 PlainSelector.prototype = { 232 PlainSelector.prototype = {
233 /** 233 /**
234 * Generator function returning a pair of selector 234 * Generator function returning a pair of selector
235 * string and subtree. 235 * string and subtree.
236 * @param {string} prefix the prefix for the selector. 236 * @param {string} prefix the prefix for the selector.
237 * @param {Node} subtree the subtree we work on. 237 * @param {Node} subtree the subtree we work on.
238 * @param {StringifiedStyle[]} styles the stringified style objects. 238 * @param {StringifiedStyle[]} styles the stringified style objects.
239 * @param {Node[]} [targets] the nodes we are interested in.
239 */ 240 */
240 *getSelectors(prefix, subtree, styles) 241 *getSelectors(prefix, subtree, styles, targets)
241 { 242 {
242 yield [prefix + this._selector, subtree]; 243 yield [prefix + this._selector, subtree];
243 } 244 }
244 }; 245 };
245 246
246 const incompletePrefixRegexp = /[\s>+~]$/; 247 const incompletePrefixRegexp = /[\s>+~]$/;
247 248
248 function HasSelector(selectors) 249 function HasSelector(selectors)
249 { 250 {
250 this._innerSelectors = selectors; 251 this._innerSelectors = selectors;
(...skipping 15 matching lines...) Expand all
266 ); 267 );
267 }, 268 },
268 269
269 get maybeDependsOnAttributes() 270 get maybeDependsOnAttributes()
270 { 271 {
271 return this._innerSelectors.some( 272 return this._innerSelectors.some(
272 selector => selector.maybeDependsOnAttributes 273 selector => selector.maybeDependsOnAttributes
273 ); 274 );
274 }, 275 },
275 276
276 *getSelectors(prefix, subtree, styles) 277 *getSelectors(prefix, subtree, styles, targets)
277 { 278 {
278 for (let element of this.getElements(prefix, subtree, styles)) 279 for (let element of this.getElements(prefix, subtree, styles, targets))
279 yield [makeSelector(element, ""), element]; 280 yield [makeSelector(element, ""), element];
280 }, 281 },
281 282
282 /** 283 /**
283 * Generator function returning selected elements. 284 * Generator function returning selected elements.
284 * @param {string} prefix the prefix for the selector. 285 * @param {string} prefix the prefix for the selector.
285 * @param {Node} subtree the subtree we work on. 286 * @param {Node} subtree the subtree we work on.
286 * @param {StringifiedStyle[]} styles the stringified style objects. 287 * @param {StringifiedStyle[]} styles the stringified style objects.
288 * @param {Node[]} [targets] the nodes we are interested in.
287 */ 289 */
288 *getElements(prefix, subtree, styles) 290 *getElements(prefix, subtree, styles, targets)
289 { 291 {
290 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? 292 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
291 prefix + "*" : prefix; 293 prefix + "*" : prefix;
292 let elements = scopedQuerySelectorAll(subtree, actualPrefix); 294 let elements = scopedQuerySelectorAll(subtree, actualPrefix);
293 if (elements) 295 if (elements)
294 { 296 {
295 for (let element of elements) 297 for (let element of elements)
296 { 298 {
297 let iter = evaluate(this._innerSelectors, 0, "", element, styles); 299 // If the element is neither an ancestor nor a descendant of one of the
300 // targets, we can skip it.
301 if (targets && !targets.some(target => element.contains(target) ||
302 target.contains(element)))
303 {
304 yield null;
305 continue;
306 }
307
308 let iter = evaluate(this._innerSelectors, 0, "", element, styles,
309 targets);
298 for (let selector of iter) 310 for (let selector of iter)
299 { 311 {
300 if (selector == null) 312 if (selector == null)
301 yield null; 313 yield null;
302 else if (scopedQuerySelector(element, selector)) 314 else if (scopedQuerySelector(element, selector))
303 yield element; 315 yield element;
304 } 316 }
305 yield null; 317 yield null;
306 } 318 }
307 } 319 }
308 } 320 }
309 }; 321 };
310 322
311 function ContainsSelector(textContent) 323 function ContainsSelector(textContent)
312 { 324 {
313 this._regexp = makeRegExpParameter(textContent); 325 this._regexp = makeRegExpParameter(textContent);
314 } 326 }
315 327
316 ContainsSelector.prototype = { 328 ContainsSelector.prototype = {
317 requiresHiding: true, 329 requiresHiding: true,
318 dependsOnDOM: true, 330 dependsOnDOM: true,
319 dependsOnCharacterData: true, 331 dependsOnCharacterData: true,
320 332
321 *getSelectors(prefix, subtree, styles) 333 *getSelectors(prefix, subtree, styles, targets)
322 { 334 {
323 for (let element of this.getElements(prefix, subtree, styles)) 335 for (let element of this.getElements(prefix, subtree, styles, targets))
324 yield [makeSelector(element, ""), subtree]; 336 yield [makeSelector(element, ""), subtree];
325 }, 337 },
326 338
327 *getElements(prefix, subtree, styles) 339 *getElements(prefix, subtree, styles, targets)
328 { 340 {
329 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? 341 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
330 prefix + "*" : prefix; 342 prefix + "*" : prefix;
331 343
332 let elements = scopedQuerySelectorAll(subtree, actualPrefix); 344 let elements = scopedQuerySelectorAll(subtree, actualPrefix);
333 if (elements) 345 if (elements)
334 { 346 {
335 for (let element of elements) 347 for (let element of elements)
336 { 348 {
349 if (targets && !targets.some(target => element.contains(target) ||
Manish Jethani 2018/03/20 10:21:57 By the way this may not seem like such a great ide
350 target.contains(element)))
351 {
352 yield null;
353 continue;
354 }
355
337 if (this._regexp && this._regexp.test(element.textContent)) 356 if (this._regexp && this._regexp.test(element.textContent))
338 yield element; 357 yield element;
339 else 358 else
340 yield null; 359 yield null;
341 } 360 }
342 } 361 }
343 } 362 }
344 }; 363 };
345 364
346 function PropsSelector(propertyExpression) 365 function PropsSelector(propertyExpression)
(...skipping 26 matching lines...) Expand all
373 { 392 {
374 subSelector = subSelector.substr(1); 393 subSelector = subSelector.substr(1);
375 } 394 }
376 let idx = subSelector.lastIndexOf("::"); 395 let idx = subSelector.lastIndexOf("::");
377 if (idx != -1) 396 if (idx != -1)
378 subSelector = subSelector.substr(0, idx); 397 subSelector = subSelector.substr(0, idx);
379 yield prefix + subSelector; 398 yield prefix + subSelector;
380 } 399 }
381 }, 400 },
382 401
383 *getSelectors(prefix, subtree, styles) 402 *getSelectors(prefix, subtree, styles, targets)
384 { 403 {
385 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) 404 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp))
386 yield [selector, subtree]; 405 yield [selector, subtree];
387 } 406 }
388 }; 407 };
389 408
390 function Pattern(selectors, text) 409 function Pattern(selectors, text)
391 { 410 {
392 this.selectors = selectors; 411 this.selectors = selectors;
393 this.text = text; 412 this.text = text;
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 { 468 {
450 // Observe changes to character data only if there's a contains selector in 469 // Observe changes to character data only if there's a contains selector in
451 // one of the patterns. 470 // one of the patterns.
452 return getCachedPropertyValue( 471 return getCachedPropertyValue(
453 this, "_dependsOnCharacterData", 472 this, "_dependsOnCharacterData",
454 () => this.selectors.some(selector => selector.dependsOnCharacterData) 473 () => this.selectors.some(selector => selector.dependsOnCharacterData)
455 ); 474 );
456 } 475 }
457 }; 476 };
458 477
478 function extractMutationTargets(mutations)
479 {
480 if (!mutations)
481 return null;
482
483 let targets = new Set();
484
485 for (let mutation of mutations)
486 {
487 if (mutation.type == "childList")
488 {
489 // When new nodes are added, we're interested in the added nodes rather
490 // than the parent.
491 for (let node of mutation.addedNodes)
492 targets.add(node);
493
494 // Ideally we would also be interested in removed nodes, but since we
495 // never unhide an element once hidden we can simply ignore any removed
496 // nodes. Note that this will change once we start using CSS selectors
497 // for -abp-has and -abp-contains, i.e. we'll have to remove the
498 // selectors for any removed nodes.
499 }
500 else
501 {
502 targets.add(mutation.target);
503 }
504 }
505
506 return [...targets];
507 }
508
459 function filterPatterns(patterns, {stylesheets, mutations}) 509 function filterPatterns(patterns, {stylesheets, mutations})
460 { 510 {
461 if (!stylesheets && !mutations) 511 if (!stylesheets && !mutations)
462 return patterns.slice(); 512 return patterns.slice();
463 513
464 return patterns.filter( 514 return patterns.filter(
465 pattern => (stylesheets && pattern.dependsOnStyles) || 515 pattern => (stylesheets && pattern.dependsOnStyles) ||
466 (mutations && pattern.dependsOnDOM) 516 (mutations && pattern.dependsOnDOM)
467 ); 517 );
468 } 518 }
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
616 666
617 for (let rule of rules) 667 for (let rule of rules)
618 { 668 {
619 if (rule.type != rule.STYLE_RULE) 669 if (rule.type != rule.STYLE_RULE)
620 continue; 670 continue;
621 671
622 cssStyles.push(stringifyStyle(rule)); 672 cssStyles.push(stringifyStyle(rule));
623 } 673 }
624 } 674 }
625 675
676 let targets = extractMutationTargets(mutations);
677
626 let pattern = null; 678 let pattern = null;
627 let generator = null; 679 let generator = null;
628 680
629 let processPatterns = () => 681 let processPatterns = () =>
630 { 682 {
631 let cycleStart = performance.now(); 683 let cycleStart = performance.now();
632 684
633 if (!pattern) 685 if (!pattern)
634 { 686 {
635 if (!patterns.length) 687 if (!patterns.length)
636 { 688 {
637 if (selectors.length > 0) 689 if (selectors.length > 0)
638 this.addSelectorsFunc(selectors, selectorFilters); 690 this.addSelectorsFunc(selectors, selectorFilters);
639 if (elements.length > 0) 691 if (elements.length > 0)
640 this.hideElemsFunc(elements, elementFilters); 692 this.hideElemsFunc(elements, elementFilters);
641 if (typeof done == "function") 693 if (typeof done == "function")
642 done(); 694 done();
643 return; 695 return;
644 } 696 }
645 697
646 pattern = patterns.shift(); 698 pattern = patterns.shift();
647 699
648 generator = evaluate(pattern.selectors, 0, "", 700 generator = evaluate(pattern.selectors, 0, "",
649 this.document, cssStyles); 701 this.document, cssStyles, targets);
650 } 702 }
651 for (let selector of generator) 703 for (let selector of generator)
652 { 704 {
653 if (selector != null) 705 if (selector != null)
654 { 706 {
655 if (pattern.isSelectorHidingOnlyPattern()) 707 if (pattern.isSelectorHidingOnlyPattern())
656 { 708 {
657 selectors.push(selector); 709 selectors.push(selector);
658 selectorFilters.push(pattern.text); 710 selectorFilters.push(pattern.text);
659 } 711 }
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
809 characterData: shouldObserveCharacterData(this.patterns), 861 characterData: shouldObserveCharacterData(this.patterns),
810 subtree: true 862 subtree: true
811 } 863 }
812 ); 864 );
813 this.document.addEventListener("load", this.onLoad.bind(this), true); 865 this.document.addEventListener("load", this.onLoad.bind(this), true);
814 } 866 }
815 } 867 }
816 }; 868 };
817 869
818 exports.ElemHideEmulation = ElemHideEmulation; 870 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