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

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

Issue 29723684: Issue 6382 - Catch InvalidAccessError exception when checking styles. (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Created March 15, 2018, 9:43 p.m.
Right Patch Set: Link to the code to explain the exception Created April 18, 2018, 6:47 p.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 | no next file » | 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 22
22 let MIN_INVOCATION_INTERVAL = 3000; 23 let MIN_INVOCATION_INTERVAL = 3000;
23 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50; 24 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50;
24 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i; 25 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i;
26
27 function getCachedPropertyValue(object, name, defaultValueFunc = () => {})
28 {
29 let value = object[name];
30 if (typeof value == "undefined")
31 Object.defineProperty(object, name, {value: value = defaultValueFunc()});
32 return value;
33 }
25 34
26 /** Return position of node from parent. 35 /** Return position of node from parent.
27 * @param {Node} node the node to find the position of. 36 * @param {Node} node the node to find the position of.
28 * @return {number} One-based index like for :nth-child(), or 0 on error. 37 * @return {number} One-based index like for :nth-child(), or 0 on error.
29 */ 38 */
30 function positionInParent(node) 39 function positionInParent(node)
31 { 40 {
32 let {children} = node.parentNode; 41 return indexOf(node.parentNode.children, node) + 1;
33 for (let i = 0; i < children.length; i++) 42 }
34 if (children[i] == node) 43
35 return i + 1; 44 function makeSelector(node, selector = "")
36 return 0;
37 }
38
39 function makeSelector(node, selector)
40 { 45 {
41 if (node == null) 46 if (node == null)
42 return null; 47 return null;
43 if (!node.parentElement) 48 if (!node.parentElement)
44 { 49 {
45 let newSelector = ":root"; 50 let newSelector = ":root";
46 if (selector) 51 if (selector)
47 newSelector += " > " + selector; 52 newSelector += " > " + selector;
48 return newSelector; 53 return newSelector;
49 } 54 }
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 242
238 const incompletePrefixRegexp = /[\s>+~]$/; 243 const incompletePrefixRegexp = /[\s>+~]$/;
239 244
240 function HasSelector(selectors) 245 function HasSelector(selectors)
241 { 246 {
242 this._innerSelectors = selectors; 247 this._innerSelectors = selectors;
243 } 248 }
244 249
245 HasSelector.prototype = { 250 HasSelector.prototype = {
246 requiresHiding: true, 251 requiresHiding: true,
252 dependsOnDOM: true,
247 253
248 get dependsOnStyles() 254 get dependsOnStyles()
249 { 255 {
250 return this._innerSelectors.some(selector => selector.dependsOnStyles); 256 return this._innerSelectors.some(selector => selector.dependsOnStyles);
251 }, 257 },
252 258
253 get dependsOnCharacterData() 259 get dependsOnCharacterData()
254 { 260 {
255 return this._innerSelectors.some( 261 return this._innerSelectors.some(
256 selector => selector.dependsOnCharacterData 262 selector => selector.dependsOnCharacterData
257 ); 263 );
258 }, 264 },
259 265
260 get maybeDependsOnAttributes() 266 get maybeDependsOnAttributes()
261 { 267 {
262 return this._innerSelectors.some( 268 return this._innerSelectors.some(
263 selector => selector.maybeDependsOnAttributes 269 selector => selector.maybeDependsOnAttributes
264 ); 270 );
265 }, 271 },
266 272
267 *getSelectors(prefix, subtree, styles) 273 *getSelectors(prefix, subtree, styles)
268 { 274 {
269 for (let element of this.getElements(prefix, subtree, styles)) 275 for (let element of this.getElements(prefix, subtree, styles))
270 yield [makeSelector(element, ""), element]; 276 yield [makeSelector(element), element];
271 }, 277 },
272 278
273 /** 279 /**
274 * Generator function returning selected elements. 280 * Generator function returning selected elements.
275 * @param {string} prefix the prefix for the selector. 281 * @param {string} prefix the prefix for the selector.
276 * @param {Node} subtree the subtree we work on. 282 * @param {Node} subtree the subtree we work on.
277 * @param {StringifiedStyle[]} styles the stringified style objects. 283 * @param {StringifiedStyle[]} styles the stringified style objects.
278 */ 284 */
279 *getElements(prefix, subtree, styles) 285 *getElements(prefix, subtree, styles)
280 { 286 {
(...skipping 18 matching lines...) Expand all
299 } 305 }
300 }; 306 };
301 307
302 function ContainsSelector(textContent) 308 function ContainsSelector(textContent)
303 { 309 {
304 this._regexp = makeRegExpParameter(textContent); 310 this._regexp = makeRegExpParameter(textContent);
305 } 311 }
306 312
307 ContainsSelector.prototype = { 313 ContainsSelector.prototype = {
308 requiresHiding: true, 314 requiresHiding: true,
315 dependsOnDOM: true,
309 dependsOnCharacterData: true, 316 dependsOnCharacterData: true,
310 317
311 *getSelectors(prefix, subtree, styles) 318 *getSelectors(prefix, subtree, styles)
312 { 319 {
313 for (let element of this.getElements(prefix, subtree, styles)) 320 for (let element of this.getElements(prefix, subtree, styles))
314 yield [makeSelector(element, ""), subtree]; 321 yield [makeSelector(element), subtree];
315 }, 322 },
316 323
317 *getElements(prefix, subtree, styles) 324 *getElements(prefix, subtree, styles)
318 { 325 {
319 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? 326 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
320 prefix + "*" : prefix; 327 prefix + "*" : prefix;
321 328
322 let elements = scopedQuerySelectorAll(subtree, actualPrefix); 329 let elements = scopedQuerySelectorAll(subtree, actualPrefix);
323 if (elements) 330 if (elements)
324 { 331 {
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 } 377 }
371 }, 378 },
372 379
373 *getSelectors(prefix, subtree, styles) 380 *getSelectors(prefix, subtree, styles)
374 { 381 {
375 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) 382 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp))
376 yield [selector, subtree]; 383 yield [selector, subtree];
377 } 384 }
378 }; 385 };
379 386
380 function isSelectorHidingOnlyPattern(pattern) 387 function Pattern(selectors, text)
381 { 388 {
382 return pattern.selectors.some(s => s.preferHideWithSelector) && 389 this.selectors = selectors;
383 !pattern.selectors.some(s => s.requiresHiding); 390 this.text = text;
391 }
392
393 Pattern.prototype = {
394 isSelectorHidingOnlyPattern()
395 {
396 return getCachedPropertyValue(
397 this, "_selectorHidingOnlyPattern",
398 () => this.selectors.some(selector => selector.preferHideWithSelector) &&
399 !this.selectors.some(selector => selector.requiresHiding)
400 );
401 },
402
403 get dependsOnStyles()
404 {
405 return getCachedPropertyValue(
406 this, "_dependsOnStyles",
407 () => this.selectors.some(selector => selector.dependsOnStyles)
408 );
409 },
410
411 get dependsOnDOM()
412 {
413 return getCachedPropertyValue(
414 this, "_dependsOnDOM",
415 () => this.selectors.some(selector => selector.dependsOnDOM)
416 );
417 },
418
419 get dependsOnStylesAndDOM()
420 {
421 return getCachedPropertyValue(
422 this, "_dependsOnStylesAndDOM",
423 () => this.selectors.some(selector => selector.dependsOnStyles &&
424 selector.dependsOnDOM)
425 );
426 },
427
428 get maybeDependsOnAttributes()
429 {
430 // Observe changes to attributes if either there's a plain selector that
431 // looks like an ID selector, class selector, or attribute selector in one
432 // of the patterns (e.g. "a[href='https://example.com/']")
433 // or there's a properties selector nested inside a has selector
434 // (e.g. "div:-abp-has(:-abp-properties(color: blue))")
435 return getCachedPropertyValue(
436 this, "_maybeDependsOnAttributes",
437 () => this.selectors.some(
438 selector => selector.maybeDependsOnAttributes ||
439 (selector instanceof HasSelector &&
440 selector.dependsOnStyles)
441 )
442 );
443 },
444
445 get dependsOnCharacterData()
446 {
447 // Observe changes to character data only if there's a contains selector in
448 // one of the patterns.
449 return getCachedPropertyValue(
450 this, "_dependsOnCharacterData",
451 () => this.selectors.some(selector => selector.dependsOnCharacterData)
452 );
453 }
454 };
455
456 function filterPatterns(patterns, {stylesheets, mutations})
457 {
458 if (!stylesheets && !mutations)
459 return patterns.slice();
460
461 return patterns.filter(
462 pattern => (stylesheets && pattern.dependsOnStyles) ||
463 (mutations && pattern.dependsOnDOM)
464 );
384 } 465 }
385 466
386 function shouldObserveAttributes(patterns) 467 function shouldObserveAttributes(patterns)
387 { 468 {
388 // Observe changes to attributes if either there's a plain selector that 469 return patterns.some(pattern => pattern.maybeDependsOnAttributes);
389 // looks like an ID selector, class selector, or attribute selector in one of
390 // the patterns (e.g. "a[href='https://example.com/']")
391 // or there's a properties selector nested inside a has selector
392 // (e.g. "div:-abp-has(:-abp-properties(color: blue))")
393 return patterns.some(
394 pattern => pattern.selectors.some(
395 selector => selector.maybeDependsOnAttributes ||
396 (selector instanceof HasSelector &&
397 selector.dependsOnStyles)
398 )
399 );
400 } 470 }
401 471
402 function shouldObserveCharacterData(patterns) 472 function shouldObserveCharacterData(patterns)
403 { 473 {
404 // Observe changes to character data only if there's a contains selector in 474 return patterns.some(pattern => pattern.dependsOnCharacterData);
405 // one of the patterns.
406 return patterns.some(
407 pattern => pattern.selectors.some(
408 selector => selector.dependsOnCharacterData
409 )
410 );
411 } 475 }
412 476
413 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) 477 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc)
414 { 478 {
415 this.document = document; 479 this.document = document;
416 this.addSelectorsFunc = addSelectorsFunc; 480 this.addSelectorsFunc = addSelectorsFunc;
417 this.hideElemsFunc = hideElemsFunc; 481 this.hideElemsFunc = hideElemsFunc;
418 this.observer = new MutationObserver(this.observe.bind(this)); 482 this.observer = new MutationObserver(this.observe.bind(this));
483 this.useInlineStyles = true;
419 } 484 }
420 485
421 ElemHideEmulation.prototype = { 486 ElemHideEmulation.prototype = {
422 isSameOrigin(stylesheet) 487 isSameOrigin(stylesheet)
423 { 488 {
424 try 489 try
425 { 490 {
426 return new URL(stylesheet.href).origin == this.document.location.origin; 491 return new URL(stylesheet.href).origin == this.document.location.origin;
427 } 492 }
428 catch (e) 493 catch (e)
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
495 return selectors; 560 return selectors;
496 }, 561 },
497 562
498 /** 563 /**
499 * Processes the current document and applies all rules to it. 564 * Processes the current document and applies all rules to it.
500 * @param {CSSStyleSheet[]} [stylesheets] 565 * @param {CSSStyleSheet[]} [stylesheets]
501 * The list of new stylesheets that have been added to the document and 566 * 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 567 * made reprocessing necessary. This parameter shouldn't be passed in for
503 * the initial processing, all of document's stylesheets will be considered 568 * the initial processing, all of document's stylesheets will be considered
504 * then and all rules, including the ones not dependent on styles. 569 * then and all rules, including the ones not dependent on styles.
570 * @param {MutationRecord[]} [mutations]
571 * The list of DOM mutations that have been applied to the document and
572 * made reprocessing necessary. This parameter shouldn't be passed in for
573 * the initial processing, the entire document will be considered
574 * then and all rules, including the ones not dependent on the DOM.
505 * @param {function} [done] 575 * @param {function} [done]
506 * Callback to call when done. 576 * Callback to call when done.
507 */ 577 */
508 _addSelectors(stylesheets, done) 578 _addSelectors(stylesheets, mutations, done)
509 { 579 {
580 let patterns = filterPatterns(this.patterns, {stylesheets, mutations});
581
510 let selectors = []; 582 let selectors = [];
511 let selectorFilters = []; 583 let selectorFilters = [];
512 584
513 let elements = []; 585 let elements = [];
514 let elementFilters = []; 586 let elementFilters = [];
515 587
516 let cssStyles = []; 588 let cssStyles = [];
517 589
518 let stylesheetOnlyChange = !!stylesheets; 590 // If neither any style sheets nor any DOM mutations have been specified,
519 if (!stylesheets) 591 // do full processing.
592 if (!stylesheets && !mutations)
520 stylesheets = this.document.styleSheets; 593 stylesheets = this.document.styleSheets;
521 594
522 for (let stylesheet of stylesheets) 595 // If there are any DOM mutations and any of the patterns depends on both
596 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the
597 // rules in every style sheet in the document, because we need to run
598 // querySelectorAll afterwards. On the other hand, if we only have patterns
599 // that depend on either styles or DOM both not both
600 // (e.g. -abp-properties or -abp-contains), we can skip this part.
601 if (mutations && patterns.some(pattern => pattern.dependsOnStylesAndDOM))
602 stylesheets = this.document.styleSheets;
603
604 for (let stylesheet of stylesheets || [])
523 { 605 {
524 // Explicitly ignore third-party stylesheets to ensure consistent behavior 606 // Explicitly ignore third-party stylesheets to ensure consistent behavior
525 // between Firefox and Chrome. 607 // between Firefox and Chrome.
526 if (!this.isSameOrigin(stylesheet)) 608 if (!this.isSameOrigin(stylesheet))
527 continue; 609 continue;
528 610
611 let rules;
529 try 612 try
530 { 613 {
531 let rules = stylesheet.cssRules; 614 rules = stylesheet.cssRules;
kzar 2018/03/19 15:52:07 If this is the only line that could cause the exce
hub 2018/03/20 14:53:53 Done.
532 if (!rules)
533 continue;
534
535 for (let rule of rules)
536 {
537 if (rule.type != rule.STYLE_RULE)
538 continue;
539
540 cssStyles.push(stringifyStyle(rule));
541 }
542 } 615 }
543 catch (e) 616 catch (e)
544 { 617 {
545 // On Firefox, there is a chance that an InvalidAccessError 618 // On Firefox, there is a chance that an InvalidAccessError
kzar 2018/03/19 15:52:07 How come this happens? Is there a Firefox bug / di
hub 2018/03/20 16:16:02 It's not a bug. https://searchfox.org/mozilla-cen
kzar 2018/04/18 16:12:30 Acknowledged. In fact that link you gave is prett
hub 2018/04/18 18:48:23 Done.
546 // get thrown when accessing cssRules. Just skip the stylesheet 619 // get thrown when accessing cssRules. Just skip the stylesheet
547 // in that case. 620 // in that case.
548 // See https://issues.adblockplus.org/ticket/6382 621 // See https://searchfox.org/mozilla-central/rev/f65d7528e34ef1a7665b4a1 a7b7cdb1388fcd3aa/layout/style/StyleSheet.cpp#699
549 continue; 622 continue;
550 } 623 }
551 } 624
552 625 if (!rules)
553 let patterns = this.patterns.slice(); 626 continue;
627
628 for (let rule of rules)
629 {
630 if (rule.type != rule.STYLE_RULE)
631 continue;
632
633 cssStyles.push(stringifyStyle(rule));
634 }
635 }
636
554 let pattern = null; 637 let pattern = null;
555 let generator = null; 638 let generator = null;
556 639
557 let processPatterns = () => 640 let processPatterns = () =>
558 { 641 {
559 let cycleStart = performance.now(); 642 let cycleStart = performance.now();
560 643
561 if (!pattern) 644 if (!pattern)
562 { 645 {
563 if (!patterns.length) 646 if (!patterns.length)
564 { 647 {
565 this.addSelectorsFunc(selectors, selectorFilters); 648 if (selectors.length > 0)
566 this.hideElemsFunc(elements, elementFilters); 649 this.addSelectorsFunc(selectors, selectorFilters);
650 if (elements.length > 0)
651 this.hideElemsFunc(elements, elementFilters);
567 if (typeof done == "function") 652 if (typeof done == "function")
568 done(); 653 done();
569 return; 654 return;
570 } 655 }
571 656
572 pattern = patterns.shift(); 657 pattern = patterns.shift();
573 658
574 if (stylesheetOnlyChange &&
575 !pattern.selectors.some(selector => selector.dependsOnStyles))
576 {
577 pattern = null;
578 return processPatterns();
579 }
580 generator = evaluate(pattern.selectors, 0, "", 659 generator = evaluate(pattern.selectors, 0, "",
581 this.document, cssStyles); 660 this.document, cssStyles);
582 } 661 }
583 for (let selector of generator) 662 for (let selector of generator)
584 { 663 {
585 if (selector != null) 664 if (selector != null)
586 { 665 {
587 if (isSelectorHidingOnlyPattern(pattern)) 666 if (!this.useInlineStyles ||
667 pattern.isSelectorHidingOnlyPattern())
588 { 668 {
589 selectors.push(selector); 669 selectors.push(selector);
590 selectorFilters.push(pattern.text); 670 selectorFilters.push(pattern.text);
591 } 671 }
592 else 672 else
593 { 673 {
594 for (let element of this.document.querySelectorAll(selector)) 674 for (let element of this.document.querySelectorAll(selector))
595 { 675 {
596 elements.push(element); 676 elements.push(element);
597 elementFilters.push(pattern.text); 677 elementFilters.push(pattern.text);
(...skipping 26 matching lines...) Expand all
624 }, 704 },
625 705
626 _filteringInProgress: false, 706 _filteringInProgress: false,
627 _lastInvocation: -MIN_INVOCATION_INTERVAL, 707 _lastInvocation: -MIN_INVOCATION_INTERVAL,
628 _scheduledProcessing: null, 708 _scheduledProcessing: null,
629 709
630 /** 710 /**
631 * Re-run filtering either immediately or queued. 711 * Re-run filtering either immediately or queued.
632 * @param {CSSStyleSheet[]} [stylesheets] 712 * @param {CSSStyleSheet[]} [stylesheets]
633 * new stylesheets to be processed. This parameter should be omitted 713 * new stylesheets to be processed. This parameter should be omitted
634 * for DOM modification (full reprocessing required). 714 * for full reprocessing.
715 * @param {MutationRecord[]} [mutations]
716 * new DOM mutations to be processed. This parameter should be omitted
717 * for full reprocessing.
635 */ 718 */
636 queueFiltering(stylesheets) 719 queueFiltering(stylesheets, mutations)
637 { 720 {
638 let completion = () => 721 let completion = () =>
639 { 722 {
640 this._lastInvocation = performance.now(); 723 this._lastInvocation = performance.now();
641 this._filteringInProgress = false; 724 this._filteringInProgress = false;
642 if (this._scheduledProcessing) 725 if (this._scheduledProcessing)
643 { 726 {
644 let newStylesheets = this._scheduledProcessing.stylesheets; 727 let params = Object.assign({}, this._scheduledProcessing);
645 this._scheduledProcessing = null; 728 this._scheduledProcessing = null;
646 this.queueFiltering(newStylesheets); 729 this.queueFiltering(params.stylesheets, params.mutations);
647 } 730 }
648 }; 731 };
649 732
650 if (this._scheduledProcessing) 733 if (this._scheduledProcessing)
651 { 734 {
652 if (!stylesheets) 735 if (!stylesheets && !mutations)
653 this._scheduledProcessing.stylesheets = null; 736 {
654 else if (this._scheduledProcessing.stylesheets) 737 this._scheduledProcessing = {};
655 this._scheduledProcessing.stylesheets.push(...stylesheets); 738 }
739 else if (this._scheduledProcessing.stylesheets ||
740 this._scheduledProcessing.mutations)
741 {
742 if (stylesheets)
743 {
744 if (!this._scheduledProcessing.stylesheets)
745 this._scheduledProcessing.stylesheets = [];
746 this._scheduledProcessing.stylesheets.push(...stylesheets);
747 }
748 if (mutations)
749 {
750 if (!this._scheduledProcessing.mutations)
751 this._scheduledProcessing.mutations = [];
752 this._scheduledProcessing.mutations.push(...mutations);
753 }
754 }
656 } 755 }
657 else if (this._filteringInProgress) 756 else if (this._filteringInProgress)
658 { 757 {
659 this._scheduledProcessing = {stylesheets}; 758 this._scheduledProcessing = {stylesheets, mutations};
660 } 759 }
661 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) 760 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL)
662 { 761 {
663 this._scheduledProcessing = {stylesheets}; 762 this._scheduledProcessing = {stylesheets, mutations};
664 setTimeout(() => 763 setTimeout(() =>
665 { 764 {
666 let newStylesheets = this._scheduledProcessing.stylesheets; 765 let params = Object.assign({}, this._scheduledProcessing);
667 this._filteringInProgress = true; 766 this._filteringInProgress = true;
668 this._scheduledProcessing = null; 767 this._scheduledProcessing = null;
669 this._addSelectors(newStylesheets, completion); 768 this._addSelectors(params.stylesheets, params.mutations, completion);
670 }, 769 },
671 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); 770 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation));
672 } 771 }
673 else if (this.document.readyState == "loading") 772 else if (this.document.readyState == "loading")
674 { 773 {
675 this._scheduledProcessing = {stylesheets}; 774 this._scheduledProcessing = {stylesheets, mutations};
676 let handler = () => 775 let handler = () =>
677 { 776 {
678 document.removeEventListener("DOMContentLoaded", handler); 777 this.document.removeEventListener("DOMContentLoaded", handler);
679 let newStylesheets = this._scheduledProcessing.stylesheets; 778 let params = Object.assign({}, this._scheduledProcessing);
680 this._filteringInProgress = true; 779 this._filteringInProgress = true;
681 this._scheduledProcessing = null; 780 this._scheduledProcessing = null;
682 this._addSelectors(newStylesheets, completion); 781 this._addSelectors(params.stylesheets, params.mutations, completion);
683 }; 782 };
684 document.addEventListener("DOMContentLoaded", handler); 783 this.document.addEventListener("DOMContentLoaded", handler);
685 } 784 }
686 else 785 else
687 { 786 {
688 this._filteringInProgress = true; 787 this._filteringInProgress = true;
689 this._addSelectors(stylesheets, completion); 788 this._addSelectors(stylesheets, mutations, completion);
690 } 789 }
691 }, 790 },
692 791
693 onLoad(event) 792 onLoad(event)
694 { 793 {
695 let stylesheet = event.target.sheet; 794 let stylesheet = event.target.sheet;
696 if (stylesheet) 795 if (stylesheet)
697 this.queueFiltering([stylesheet]); 796 this.queueFiltering([stylesheet]);
698 }, 797 },
699 798
700 observe(mutations) 799 observe(mutations)
701 { 800 {
702 this.queueFiltering(); 801 this.queueFiltering(null, mutations);
703 }, 802 },
704 803
705 apply(patterns) 804 apply(patterns)
706 { 805 {
707 this.patterns = []; 806 this.patterns = [];
708 for (let pattern of patterns) 807 for (let pattern of patterns)
709 { 808 {
710 let selectors = this.parseSelector(pattern.selector); 809 let selectors = this.parseSelector(pattern.selector);
711 if (selectors != null && selectors.length > 0) 810 if (selectors != null && selectors.length > 0)
712 this.patterns.push({selectors, text: pattern.text}); 811 this.patterns.push(new Pattern(selectors, pattern.text));
713 } 812 }
714 813
715 if (this.patterns.length > 0) 814 if (this.patterns.length > 0)
716 { 815 {
717 this.queueFiltering(); 816 this.queueFiltering();
718 this.observer.observe( 817 this.observer.observe(
719 this.document, 818 this.document,
720 { 819 {
721 childList: true, 820 childList: true,
722 attributes: shouldObserveAttributes(this.patterns), 821 attributes: shouldObserveAttributes(this.patterns),
723 characterData: shouldObserveCharacterData(this.patterns), 822 characterData: shouldObserveCharacterData(this.patterns),
724 subtree: true 823 subtree: true
725 } 824 }
726 ); 825 );
727 this.document.addEventListener("load", this.onLoad.bind(this), true); 826 this.document.addEventListener("load", this.onLoad.bind(this), true);
728 } 827 }
729 } 828 }
730 }; 829 };
731 830
732 exports.ElemHideEmulation = ElemHideEmulation; 831 exports.ElemHideEmulation = ElemHideEmulation;
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld