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

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

Issue 29559743: Issue 5650 - Apply emulation filters to shadow DOMs Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Created Sept. 29, 2017, 11:21 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 22 matching lines...) Expand all
33 for (let i = 0; i < children.length; i++) 33 for (let i = 0; i < children.length; i++)
34 if (children[i] == node) 34 if (children[i] == node)
35 return i + 1; 35 return i + 1;
36 return 0; 36 return 0;
37 } 37 }
38 38
39 function makeSelector(node, selector) 39 function makeSelector(node, selector)
40 { 40 {
41 if (node == null) 41 if (node == null)
42 return null; 42 return null;
43 if (!node.parentElement) 43
44 // If this is the topmost element in a shadow DOM, climb up one more level
45 // and then use a ":host" prefix.
46 if (!node.parentElement && !(node.parentNode instanceof ShadowRoot))
Manish Jethani 2017/09/29 23:31:30 BODY has a parent element (HTML), but the topmost
44 { 47 {
45 let newSelector = ":root"; 48 let newSelector = node instanceof ShadowRoot ? ":host" : ":root";
46 if (selector) 49 if (selector)
47 newSelector += " > " + selector; 50 newSelector += " > " + selector;
48 return newSelector; 51 return newSelector;
49 } 52 }
50 let idx = positionInParent(node); 53 let idx = positionInParent(node);
51 if (idx > 0) 54 if (idx > 0)
52 { 55 {
53 let newSelector = `${node.tagName}:nth-child(${idx})`; 56 let newSelector = `${node.tagName}:nth-child(${idx})`;
54 if (selector) 57 if (selector)
55 newSelector += " > " + selector; 58 newSelector += " > " + selector;
56 return makeSelector(node.parentElement, newSelector); 59 return makeSelector(node.parentElement || node.parentNode, newSelector);
57 } 60 }
58 61
59 return selector; 62 return selector;
60 } 63 }
61 64
62 function parseSelectorContent(content, startIndex) 65 function parseSelectorContent(content, startIndex)
63 { 66 {
64 let parens = 1; 67 let parens = 1;
65 let quote = null; 68 let quote = null;
66 let i = startIndex; 69 let i = startIndex;
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 301
299 function isSelectorHidingOnlyPattern(pattern) 302 function isSelectorHidingOnlyPattern(pattern)
300 { 303 {
301 return pattern.selectors.some(s => s.preferHideWithSelector) && 304 return pattern.selectors.some(s => s.preferHideWithSelector) &&
302 !pattern.selectors.some(s => s.requiresHiding); 305 !pattern.selectors.some(s => s.requiresHiding);
303 } 306 }
304 307
305 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) 308 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc)
306 { 309 {
307 this.document = document; 310 this.document = document;
311 this.root = document;
Manish Jethani 2017/09/29 23:31:30 Now we have a root property that can be a document
308 this.addSelectorsFunc = addSelectorsFunc; 312 this.addSelectorsFunc = addSelectorsFunc;
309 this.hideElemsFunc = hideElemsFunc; 313 this.hideElemsFunc = hideElemsFunc;
310 this.observer = new MutationObserver(this.observe.bind(this)); 314 this.observer = new MutationObserver(this.observe.bind(this));
315 this.shadowEmulations = new WeakMap();
Manish Jethani 2017/09/29 23:31:30 Using WeakMap here, though I suspect the shadow ro
311 } 316 }
312 317
313 ElemHideEmulation.prototype = { 318 ElemHideEmulation.prototype = {
314 isSameOrigin(stylesheet) 319 isSameOrigin(stylesheet)
315 { 320 {
316 try 321 try
317 { 322 {
318 return new URL(stylesheet.href).origin == this.document.location.origin; 323 return new URL(stylesheet.href).origin == this.document.location.origin;
319 } 324 }
320 catch (e) 325 catch (e)
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 let selectors = []; 407 let selectors = [];
403 let selectorFilters = []; 408 let selectorFilters = [];
404 409
405 let elements = []; 410 let elements = [];
406 let elementFilters = []; 411 let elementFilters = [];
407 412
408 let cssStyles = []; 413 let cssStyles = [];
409 414
410 let stylesheetOnlyChange = !!stylesheets; 415 let stylesheetOnlyChange = !!stylesheets;
411 if (!stylesheets) 416 if (!stylesheets)
412 stylesheets = this.document.styleSheets; 417 stylesheets = this.root.styleSheets;
413 418
414 for (let stylesheet of stylesheets) 419 for (let stylesheet of stylesheets)
415 { 420 {
416 // Explicitly ignore third-party stylesheets to ensure consistent behavior 421 // Explicitly ignore third-party stylesheets to ensure consistent behavior
417 // between Firefox and Chrome. 422 // between Firefox and Chrome.
418 if (!this.isSameOrigin(stylesheet)) 423 if (!this.isSameOrigin(stylesheet))
419 continue; 424 continue;
420 425
421 let rules = stylesheet.cssRules; 426 let rules = stylesheet.cssRules;
422 if (!rules) 427 if (!rules)
(...skipping 29 matching lines...) Expand all
452 457
453 pattern = patterns.shift(); 458 pattern = patterns.shift();
454 459
455 if (stylesheetOnlyChange && 460 if (stylesheetOnlyChange &&
456 !pattern.selectors.some(selector => selector.dependsOnStyles)) 461 !pattern.selectors.some(selector => selector.dependsOnStyles))
457 { 462 {
458 pattern = null; 463 pattern = null;
459 return processPatterns(); 464 return processPatterns();
460 } 465 }
461 generator = evaluate(pattern.selectors, 0, "", 466 generator = evaluate(pattern.selectors, 0, "",
462 this.document, cssStyles); 467 this.root, cssStyles);
463 } 468 }
464 for (let selector of generator) 469 for (let selector of generator)
465 { 470 {
466 if (selector != null) 471 if (selector != null)
467 { 472 {
468 if (isSelectorHidingOnlyPattern(pattern)) 473 if (isSelectorHidingOnlyPattern(pattern))
469 { 474 {
470 selectors.push(selector); 475 selectors.push(selector);
471 selectorFilters.push(pattern.text); 476 selectorFilters.push(pattern.text);
472 } 477 }
473 else 478 else
474 { 479 {
475 for (let element of this.document.querySelectorAll(selector)) 480 for (let element of this.root.querySelectorAll(selector))
476 { 481 {
477 elements.push(element); 482 elements.push(element);
478 elementFilters.push(pattern.text); 483 elementFilters.push(pattern.text);
479 } 484 }
480 } 485 }
481 } 486 }
482 if (performance.now() - cycleStart > MAX_SYNCHRONOUS_PROCESSING_TIME) 487 if (performance.now() - cycleStart > MAX_SYNCHRONOUS_PROCESSING_TIME)
483 { 488 {
484 setTimeout(processPatterns, 0); 489 setTimeout(processPatterns, 0);
485 return; 490 return;
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 } 563 }
559 }, 564 },
560 565
561 onLoad(event) 566 onLoad(event)
562 { 567 {
563 let stylesheet = event.target.sheet; 568 let stylesheet = event.target.sheet;
564 if (stylesheet) 569 if (stylesheet)
565 this.queueFiltering([stylesheet]); 570 this.queueFiltering([stylesheet]);
566 }, 571 },
567 572
573 onShadowAttached(event)
574 {
575 // The shadow root won't be available if it's a closed shadow root. Even
576 // though we override Element.attachShadow to create an open shadow root,
577 // it is possible that some other extension has overridden it before us to
578 // create a closed shadow root.
579 let shadowRoot = event.target.shadowRoot;
580 if (!shadowRoot)
581 return;
582
583 this.addShadowRoot(shadowRoot);
584 },
585
586 addShadowRoot(shadowRoot)
587 {
588 if (!this.shadowEmulations.has(shadowRoot))
589 {
590 let emulation = new ElemHideEmulation(this.addSelectorsFunc,
591 this.hideElemsFunc);
592 this.shadowEmulations.set(shadowRoot, emulation);
593 emulation.root = shadowRoot;
594 emulation.apply(this.patterns, true);
595 }
596 },
597
598 findShadowRoots(node)
599 {
600 let shadowRoot = node.shadowRoot;
601 if (shadowRoot)
602 this.addShadowRoot(shadowRoot);
603
604 for (let child of node.children)
Manish Jethani 2017/09/29 23:43:56 We could use a generator and setTimeout here to ke
605 this.findShadowRoots(child);
606 },
607
568 observe(mutations) 608 observe(mutations)
569 { 609 {
610 for (let mutation of mutations)
Manish Jethani 2017/09/29 23:31:30 We listen for "shadowAttached" on the document, bu
611 {
612 // Find any preattached shadows.
613 for (let node of mutation.addedNodes)
614 this.findShadowRoots(node);
615 }
616
570 this.queueFiltering(); 617 this.queueFiltering();
571 }, 618 },
572 619
573 apply(patterns) 620 apply(patterns, parsed)
574 { 621 {
575 this.patterns = []; 622 if (parsed)
576 for (let pattern of patterns)
577 { 623 {
578 let selectors = this.parseSelector(pattern.selector); 624 this.patterns = patterns;
579 if (selectors != null && selectors.length > 0) 625 }
580 this.patterns.push({selectors, text: pattern.text}); 626 else
627 {
628 this.patterns = [];
629 for (let pattern of patterns)
630 {
631 let selectors = this.parseSelector(pattern.selector);
632 if (selectors != null && selectors.length > 0)
633 this.patterns.push({selectors, text: pattern.text});
634 }
581 } 635 }
582 636
583 if (this.patterns.length > 0) 637 if (this.patterns.length > 0)
584 { 638 {
585 this.queueFiltering(); 639 this.queueFiltering();
586 this.observer.observe( 640 this.observer.observe(
587 this.document, 641 this.root,
588 { 642 {
589 childList: true, 643 childList: true,
590 attributes: true, 644 attributes: true,
591 characterData: true, 645 characterData: true,
592 subtree: true 646 subtree: true
593 } 647 }
594 ); 648 );
595 this.document.addEventListener("load", this.onLoad.bind(this), true); 649
650 if (this.root == this.document)
Manish Jethani 2017/09/29 23:31:30 Listen for these events only if this is the main i
651 {
652 this.document.addEventListener("load", this.onLoad.bind(this), true);
653 this.document.addEventListener("shadowAttached",
654 this.onShadowAttached.bind(this), true);
655 }
596 } 656 }
597 } 657 }
598 }; 658 };
599 659
600 exports.ElemHideEmulation = ElemHideEmulation; 660 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