Left: | ||
Right: |
LEFT | RIGHT |
---|---|
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 | 21 |
22 let MIN_INVOCATION_INTERVAL = 3000; | 22 let MIN_INVOCATION_INTERVAL = 3000; |
23 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50; | 23 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50; |
24 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i; | 24 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i; |
25 | 25 |
26 function getCachedPropertyValue(object, name, defaultValueFunc = () => {}) | |
27 { | |
28 let value = object[name]; | |
29 if (typeof value == "undefined") | |
30 Object.defineProperty(object, name, {value: value = defaultValueFunc()}); | |
31 return value; | |
32 } | |
33 | |
26 /** Return position of node from parent. | 34 /** Return position of node from parent. |
27 * @param {Node} node the node to find the position of. | 35 * @param {Node} node the node to find the position of. |
28 * @return {number} One-based index like for :nth-child(), or 0 on error. | 36 * @return {number} One-based index like for :nth-child(), or 0 on error. |
29 */ | 37 */ |
30 function positionInParent(node) | 38 function positionInParent(node) |
31 { | 39 { |
32 let {children} = node.parentNode; | 40 let {children} = node.parentNode; |
33 for (let i = 0; i < children.length; i++) | 41 for (let i = 0; i < children.length; i++) |
34 if (children[i] == node) | 42 if (children[i] == node) |
35 return i + 1; | 43 return i + 1; |
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
237 | 245 |
238 const incompletePrefixRegexp = /[\s>+~]$/; | 246 const incompletePrefixRegexp = /[\s>+~]$/; |
239 | 247 |
240 function HasSelector(selectors) | 248 function HasSelector(selectors) |
241 { | 249 { |
242 this._innerSelectors = selectors; | 250 this._innerSelectors = selectors; |
243 } | 251 } |
244 | 252 |
245 HasSelector.prototype = { | 253 HasSelector.prototype = { |
246 requiresHiding: true, | 254 requiresHiding: true, |
247 dependsOnDOM: true, | 255 dependsOnDOM: true, |
Manish Jethani
2018/03/01 17:18:11
-abp-has and -abp-contains definitely, one hundred
| |
248 | 256 |
249 get dependsOnStyles() | 257 get dependsOnStyles() |
250 { | 258 { |
251 return this._innerSelectors.some(selector => selector.dependsOnStyles); | 259 return this._innerSelectors.some(selector => selector.dependsOnStyles); |
252 }, | 260 }, |
253 | 261 |
254 get dependsOnCharacterData() | 262 get dependsOnCharacterData() |
255 { | 263 { |
256 return this._innerSelectors.some( | 264 return this._innerSelectors.some( |
257 selector => selector.dependsOnCharacterData | 265 selector => selector.dependsOnCharacterData |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
303 function ContainsSelector(textContent) | 311 function ContainsSelector(textContent) |
304 { | 312 { |
305 this._regexp = makeRegExpParameter(textContent); | 313 this._regexp = makeRegExpParameter(textContent); |
306 } | 314 } |
307 | 315 |
308 ContainsSelector.prototype = { | 316 ContainsSelector.prototype = { |
309 requiresHiding: true, | 317 requiresHiding: true, |
310 dependsOnDOM: true, | 318 dependsOnDOM: true, |
311 dependsOnCharacterData: true, | 319 dependsOnCharacterData: true, |
312 | 320 |
313 *getSelectors(prefix, subtree, stylesheet) | 321 *getSelectors(prefix, subtree, styles) |
314 { | 322 { |
315 for (let element of this.getElements(prefix, subtree, stylesheet)) | 323 for (let element of this.getElements(prefix, subtree, styles)) |
316 yield [makeSelector(element, ""), subtree]; | 324 yield [makeSelector(element, ""), subtree]; |
317 }, | 325 }, |
318 | 326 |
319 *getElements(prefix, subtree, stylesheet) | 327 *getElements(prefix, subtree, styles) |
320 { | 328 { |
321 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? | 329 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? |
322 prefix + "*" : prefix; | 330 prefix + "*" : prefix; |
323 | 331 |
324 let elements = scopedQuerySelectorAll(subtree, actualPrefix); | 332 let elements = scopedQuerySelectorAll(subtree, actualPrefix); |
325 if (elements) | 333 if (elements) |
326 { | 334 { |
327 for (let element of elements) | 335 for (let element of elements) |
328 { | 336 { |
329 if (this._regexp && this._regexp.test(element.textContent)) | 337 if (this._regexp && this._regexp.test(element.textContent)) |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
372 } | 380 } |
373 }, | 381 }, |
374 | 382 |
375 *getSelectors(prefix, subtree, styles) | 383 *getSelectors(prefix, subtree, styles) |
376 { | 384 { |
377 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) | 385 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) |
378 yield [selector, subtree]; | 386 yield [selector, subtree]; |
379 } | 387 } |
380 }; | 388 }; |
381 | 389 |
382 function isSelectorHidingOnlyPattern(pattern) | 390 function Pattern(selectors, text) |
383 { | 391 { |
384 return pattern.selectors.some(s => s.preferHideWithSelector) && | 392 this.selectors = selectors; |
385 !pattern.selectors.some(s => s.requiresHiding); | 393 this.text = text; |
386 } | 394 } |
387 | 395 |
388 function patternDependsOnStyles(pattern) | 396 Pattern.prototype = { |
389 { | 397 isSelectorHidingOnlyPattern() |
390 return pattern.selectors.some(s => s.dependsOnStyles); | 398 { |
391 } | 399 return getCachedPropertyValue( |
392 | 400 this, "_selectorHidingOnlyPattern", |
393 function patternDependsOnDOM(pattern) | 401 () => this.selectors.some(selector => selector.preferHideWithSelector) && |
394 { | 402 !this.selectors.some(selector => selector.requiresHiding) |
395 return pattern.selectors.some(s => s.dependsOnDOM); | 403 ); |
396 } | 404 }, |
397 | 405 |
398 function patternDependsOnBothStylesAndDOM(pattern) | 406 get dependsOnStyles() |
399 { | 407 { |
400 return pattern.selectors.some(s => s.dependsOnStyles && s.dependsOnDOM); | 408 return getCachedPropertyValue( |
401 } | 409 this, "_dependsOnStyles", |
402 | 410 () => this.selectors.some(selector => selector.dependsOnStyles) |
403 function shouldProcessPattern(pattern, stylesheets, mutations) | 411 ); |
412 }, | |
413 | |
414 get dependsOnDOM() | |
415 { | |
416 return getCachedPropertyValue( | |
417 this, "_dependsOnDOM", | |
418 () => this.selectors.some(selector => selector.dependsOnDOM) | |
419 ); | |
420 }, | |
421 | |
422 get dependsOnStylesAndDOM() | |
423 { | |
424 return getCachedPropertyValue( | |
425 this, "_dependsOnStylesAndDOM", | |
426 () => this.selectors.some(selector => selector.dependsOnStyles && | |
427 selector.dependsOnDOM) | |
428 ); | |
429 }, | |
430 | |
431 get maybeDependsOnAttributes() | |
432 { | |
433 // Observe changes to attributes if either there's a plain selector that | |
434 // looks like an ID selector, class selector, or attribute selector in one | |
435 // of the patterns (e.g. "a[href='https://example.com/']") | |
436 // or there's a properties selector nested inside a has selector | |
437 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") | |
438 return getCachedPropertyValue( | |
439 this, "_maybeDependsOnAttributes", | |
440 () => this.selectors.some( | |
441 selector => selector.maybeDependsOnAttributes || | |
442 (selector instanceof HasSelector && | |
443 selector.dependsOnStyles) | |
444 ) | |
445 ); | |
446 }, | |
447 | |
448 get dependsOnCharacterData() | |
449 { | |
450 // Observe changes to character data only if there's a contains selector in | |
451 // one of the patterns. | |
452 return getCachedPropertyValue( | |
453 this, "_dependsOnCharacterData", | |
454 () => this.selectors.some(selector => selector.dependsOnCharacterData) | |
455 ); | |
456 } | |
457 }; | |
458 | |
459 function filterPatterns(patterns, {stylesheets, mutations}) | |
404 { | 460 { |
405 if (!stylesheets && !mutations) | 461 if (!stylesheets && !mutations) |
406 // Process everything. | 462 return patterns.slice(); |
407 return true; | 463 |
408 | 464 return patterns.filter( |
409 if (!stylesheets && !patternDependsOnDOM(pattern)) | 465 pattern => (stylesheets && pattern.dependsOnStyles) || |
410 return false; | 466 (mutations && pattern.dependsOnDOM) |
411 | 467 ); |
412 if (!mutations && !patternDependsOnStyles(pattern)) | |
413 return false; | |
414 | |
415 return true; | |
416 } | 468 } |
417 | 469 |
418 function shouldObserveAttributes(patterns) | 470 function shouldObserveAttributes(patterns) |
419 { | 471 { |
420 // Observe changes to attributes if either there's a plain selector that | 472 return patterns.some(pattern => pattern.maybeDependsOnAttributes); |
421 // looks like an ID selector, class selector, or attribute selector in one of | |
422 // the patterns (e.g. "a[href='https://example.com/']") | |
423 // or there's a properties selector nested inside a has selector | |
424 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") | |
425 return patterns.some( | |
426 pattern => pattern.selectors.some( | |
427 selector => selector.maybeDependsOnAttributes || | |
428 (selector instanceof HasSelector && | |
429 selector.dependsOnStyles) | |
430 ) | |
431 ); | |
432 } | 473 } |
433 | 474 |
434 function shouldObserveCharacterData(patterns) | 475 function shouldObserveCharacterData(patterns) |
435 { | 476 { |
436 // Observe changes to character data only if there's a contains selector in | 477 return patterns.some(pattern => pattern.dependsOnCharacterData); |
437 // one of the patterns. | |
438 return patterns.some( | |
439 pattern => pattern.selectors.some( | |
440 selector => selector.dependsOnCharacterData | |
441 ) | |
442 ); | |
443 } | 478 } |
444 | 479 |
445 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) | 480 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) |
446 { | 481 { |
447 this.document = document; | 482 this.document = document; |
448 this.addSelectorsFunc = addSelectorsFunc; | 483 this.addSelectorsFunc = addSelectorsFunc; |
449 this.hideElemsFunc = hideElemsFunc; | 484 this.hideElemsFunc = hideElemsFunc; |
450 this.observer = new MutationObserver(this.observe.bind(this)); | 485 this.observer = new MutationObserver(this.observe.bind(this)); |
451 } | 486 } |
452 | 487 |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
537 * @param {MutationRecord[]} [mutations] | 572 * @param {MutationRecord[]} [mutations] |
538 * The list of DOM mutations that have been applied to the document and | 573 * The list of DOM mutations that have been applied to the document and |
539 * made reprocessing necessary. This parameter shouldn't be passed in for | 574 * made reprocessing necessary. This parameter shouldn't be passed in for |
540 * the initial processing, the entire document will be considered | 575 * the initial processing, the entire document will be considered |
541 * then and all rules, including the ones not dependent on the DOM. | 576 * then and all rules, including the ones not dependent on the DOM. |
542 * @param {function} [done] | 577 * @param {function} [done] |
543 * Callback to call when done. | 578 * Callback to call when done. |
544 */ | 579 */ |
545 _addSelectors(stylesheets, mutations, done) | 580 _addSelectors(stylesheets, mutations, done) |
546 { | 581 { |
582 let patterns = filterPatterns(this.patterns, {stylesheets, mutations}); | |
583 | |
547 let selectors = []; | 584 let selectors = []; |
548 let selectorFilters = []; | 585 let selectorFilters = []; |
549 | 586 |
550 let elements = []; | 587 let elements = []; |
551 let elementFilters = []; | 588 let elementFilters = []; |
552 | 589 |
553 let cssStyles = []; | 590 let cssStyles = []; |
554 | |
555 let patterns = null; | |
556 | 591 |
557 // If neither any style sheets nor any DOM mutations have been specified, | 592 // If neither any style sheets nor any DOM mutations have been specified, |
558 // do full processing. | 593 // do full processing. |
559 if (!stylesheets && !mutations) | 594 if (!stylesheets && !mutations) |
560 { | |
561 patterns = this.patterns.slice(); | |
562 stylesheets = this.document.styleSheets; | 595 stylesheets = this.document.styleSheets; |
563 } | |
564 else | |
565 { | |
566 patterns = this.patterns.filter( | |
567 pattern => shouldProcessPattern(pattern, stylesheets, mutations) | |
568 ); | |
569 } | |
570 | 596 |
571 // If there are any DOM mutations and any of the patterns depends on both | 597 // If there are any DOM mutations and any of the patterns depends on both |
572 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the | 598 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the |
573 // rules in every style sheet in the document, because we need to run | 599 // rules in every style sheet in the document, because we need to run |
574 // querySelectorAll afterwards. On the other hand, if we only have patterns | 600 // querySelectorAll afterwards. On the other hand, if we only have patterns |
575 // that depend on either styles or DOM both not both | 601 // that depend on either styles or DOM both not both |
576 // (e.g. -abp-properties or -abp-contains), we can skip this part. | 602 // (e.g. -abp-properties or -abp-contains), we can skip this part. |
577 if (mutations && patterns.some(patternDependsOnBothStylesAndDOM)) | 603 if (mutations && patterns.some(pattern => pattern.dependsOnStylesAndDOM)) |
578 stylesheets = this.document.styleSheets; | 604 stylesheets = this.document.styleSheets; |
579 | 605 |
580 for (let stylesheet of stylesheets || []) | 606 for (let stylesheet of stylesheets || []) |
581 { | 607 { |
582 // Explicitly ignore third-party stylesheets to ensure consistent behavior | 608 // Explicitly ignore third-party stylesheets to ensure consistent behavior |
583 // between Firefox and Chrome. | 609 // between Firefox and Chrome. |
584 if (!this.isSameOrigin(stylesheet)) | 610 if (!this.isSameOrigin(stylesheet)) |
585 continue; | 611 continue; |
586 | 612 |
587 let rules = stylesheet.cssRules; | 613 let rules = stylesheet.cssRules; |
(...skipping 13 matching lines...) Expand all Loading... | |
601 let generator = null; | 627 let generator = null; |
602 | 628 |
603 let processPatterns = () => | 629 let processPatterns = () => |
604 { | 630 { |
605 let cycleStart = performance.now(); | 631 let cycleStart = performance.now(); |
606 | 632 |
607 if (!pattern) | 633 if (!pattern) |
608 { | 634 { |
609 if (!patterns.length) | 635 if (!patterns.length) |
610 { | 636 { |
611 this.addSelectorsFunc(selectors, selectorFilters); | 637 if (selectors.length > 0) |
612 this.hideElemsFunc(elements, elementFilters); | 638 this.addSelectorsFunc(selectors, selectorFilters); |
639 if (elements.length > 0) | |
640 this.hideElemsFunc(elements, elementFilters); | |
613 if (typeof done == "function") | 641 if (typeof done == "function") |
614 done(); | 642 done(); |
615 return; | 643 return; |
616 } | 644 } |
617 | 645 |
618 pattern = patterns.shift(); | 646 pattern = patterns.shift(); |
619 | 647 |
620 generator = evaluate(pattern.selectors, 0, "", | 648 generator = evaluate(pattern.selectors, 0, "", |
621 this.document, cssStyles); | 649 this.document, cssStyles); |
622 } | 650 } |
623 for (let selector of generator) | 651 for (let selector of generator) |
624 { | 652 { |
625 if (selector != null) | 653 if (selector != null) |
626 { | 654 { |
627 if (isSelectorHidingOnlyPattern(pattern)) | 655 if (pattern.isSelectorHidingOnlyPattern()) |
628 { | 656 { |
629 selectors.push(selector); | 657 selectors.push(selector); |
630 selectorFilters.push(pattern.text); | 658 selectorFilters.push(pattern.text); |
631 } | 659 } |
632 else | 660 else |
633 { | 661 { |
634 for (let element of this.document.querySelectorAll(selector)) | 662 for (let element of this.document.querySelectorAll(selector)) |
635 { | 663 { |
636 elements.push(element); | 664 elements.push(element); |
637 elementFilters.push(pattern.text); | 665 elementFilters.push(pattern.text); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
669 | 697 |
670 /** | 698 /** |
671 * Re-run filtering either immediately or queued. | 699 * Re-run filtering either immediately or queued. |
672 * @param {CSSStyleSheet[]} [stylesheets] | 700 * @param {CSSStyleSheet[]} [stylesheets] |
673 * new stylesheets to be processed. This parameter should be omitted | 701 * new stylesheets to be processed. This parameter should be omitted |
674 * for full reprocessing. | 702 * for full reprocessing. |
675 * @param {MutationRecord[]} [mutations] | 703 * @param {MutationRecord[]} [mutations] |
676 * new DOM mutations to be processed. This parameter should be omitted | 704 * new DOM mutations to be processed. This parameter should be omitted |
677 * for full reprocessing. | 705 * for full reprocessing. |
678 */ | 706 */ |
679 queueFiltering(stylesheets, mutations) | 707 queueFiltering(stylesheets, mutations) |
Manish Jethani
2018/03/01 17:18:11
The addition of the mutations parameter here is ma
| |
680 { | 708 { |
681 let completion = () => | 709 let completion = () => |
682 { | 710 { |
683 this._lastInvocation = performance.now(); | 711 this._lastInvocation = performance.now(); |
684 this._filteringInProgress = false; | 712 this._filteringInProgress = false; |
685 if (this._scheduledProcessing) | 713 if (this._scheduledProcessing) |
686 { | 714 { |
687 let newStylesheets = this._scheduledProcessing.stylesheets; | 715 let params = Object.assign({}, this._scheduledProcessing); |
688 let newMutations = this._scheduledProcessing.mutations; | |
689 this._scheduledProcessing = null; | 716 this._scheduledProcessing = null; |
690 this.queueFiltering(newStylesheets, newMutations); | 717 this.queueFiltering(params.stylesheets, params.mutations); |
691 } | 718 } |
692 }; | 719 }; |
693 | 720 |
694 if (this._scheduledProcessing) | 721 if (this._scheduledProcessing) |
695 { | 722 { |
696 if (!stylesheets && !mutations) | 723 if (!stylesheets && !mutations) |
697 { | 724 { |
698 this._scheduledProcessing.stylesheets = null; | 725 this._scheduledProcessing = {}; |
699 this._scheduledProcessing.mutations = null; | |
700 } | 726 } |
701 else | 727 else |
702 { | 728 { |
703 if (stylesheets) | 729 if (stylesheets) |
704 { | 730 { |
705 if (!this._scheduledProcessing.stylesheets) | 731 if (!this._scheduledProcessing.stylesheets) |
706 this._scheduledProcessing.stylesheets = []; | 732 this._scheduledProcessing.stylesheets = []; |
707 this._scheduledProcessing.stylesheets.push(...stylesheets); | 733 this._scheduledProcessing.stylesheets.push(...stylesheets); |
708 } | 734 } |
709 if (mutations) | 735 if (mutations) |
710 { | 736 { |
711 if (!this._scheduledProcessing.mutations) | 737 if (!this._scheduledProcessing.mutations) |
712 this._scheduledProcessing.mutations = []; | 738 this._scheduledProcessing.mutations = []; |
713 this._scheduledProcessing.mutations.push(...mutations); | 739 this._scheduledProcessing.mutations.push(...mutations); |
714 } | 740 } |
715 } | 741 } |
716 } | 742 } |
717 else if (this._filteringInProgress) | 743 else if (this._filteringInProgress) |
718 { | 744 { |
719 this._scheduledProcessing = {stylesheets, mutations}; | 745 this._scheduledProcessing = {stylesheets, mutations}; |
720 } | 746 } |
721 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) | 747 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) |
722 { | 748 { |
723 this._scheduledProcessing = {stylesheets, mutations}; | 749 this._scheduledProcessing = {stylesheets, mutations}; |
724 setTimeout(() => | 750 setTimeout(() => |
725 { | 751 { |
726 let newStylesheets = this._scheduledProcessing.stylesheets; | 752 let params = Object.assign({}, this._scheduledProcessing); |
727 let newMutations = this._scheduledProcessing.mutations; | |
728 this._filteringInProgress = true; | 753 this._filteringInProgress = true; |
729 this._scheduledProcessing = null; | 754 this._scheduledProcessing = null; |
730 this._addSelectors(newStylesheets, newMutations, completion); | 755 this._addSelectors(params.stylesheets, params.mutations, completion); |
731 }, | 756 }, |
732 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); | 757 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); |
733 } | 758 } |
734 else if (this.document.readyState == "loading") | 759 else if (this.document.readyState == "loading") |
735 { | 760 { |
736 this._scheduledProcessing = {stylesheets, mutations}; | 761 this._scheduledProcessing = {stylesheets, mutations}; |
737 let handler = () => | 762 let handler = () => |
738 { | 763 { |
739 document.removeEventListener("DOMContentLoaded", handler); | 764 document.removeEventListener("DOMContentLoaded", handler); |
740 let newStylesheets = this._scheduledProcessing.stylesheets; | 765 let params = Object.assign({}, this._scheduledProcessing); |
741 let newMutations = this._scheduledProcessing.mutations; | |
742 this._filteringInProgress = true; | 766 this._filteringInProgress = true; |
743 this._scheduledProcessing = null; | 767 this._scheduledProcessing = null; |
744 this._addSelectors(newStylesheets, newMutations, completion); | 768 this._addSelectors(params.stylesheets, params.mutations, completion); |
745 }; | 769 }; |
746 document.addEventListener("DOMContentLoaded", handler); | 770 document.addEventListener("DOMContentLoaded", handler); |
747 } | 771 } |
748 else | 772 else |
749 { | 773 { |
750 this._filteringInProgress = true; | 774 this._filteringInProgress = true; |
751 this._addSelectors(stylesheets, mutations, completion); | 775 this._addSelectors(stylesheets, mutations, completion); |
752 } | 776 } |
753 }, | 777 }, |
754 | 778 |
755 onLoad(event) | 779 onLoad(event) |
756 { | 780 { |
757 let stylesheet = event.target.sheet; | 781 let stylesheet = event.target.sheet; |
758 if (stylesheet) | 782 if (stylesheet) |
759 this.queueFiltering([stylesheet]); | 783 this.queueFiltering([stylesheet]); |
760 }, | 784 }, |
761 | 785 |
762 observe(mutations) | 786 observe(mutations) |
763 { | 787 { |
764 this.queueFiltering(null, mutations); | 788 this.queueFiltering(null, mutations); |
765 }, | 789 }, |
766 | 790 |
767 apply(patterns) | 791 apply(patterns) |
768 { | 792 { |
769 this.patterns = []; | 793 this.patterns = []; |
770 for (let pattern of patterns) | 794 for (let pattern of patterns) |
771 { | 795 { |
772 let selectors = this.parseSelector(pattern.selector); | 796 let selectors = this.parseSelector(pattern.selector); |
773 if (selectors != null && selectors.length > 0) | 797 if (selectors != null && selectors.length > 0) |
774 this.patterns.push({selectors, text: pattern.text}); | 798 this.patterns.push(new Pattern(selectors, pattern.text)); |
775 } | 799 } |
776 | 800 |
777 if (this.patterns.length > 0) | 801 if (this.patterns.length > 0) |
778 { | 802 { |
779 this.queueFiltering(); | 803 this.queueFiltering(); |
780 this.observer.observe( | 804 this.observer.observe( |
781 this.document, | 805 this.document, |
782 { | 806 { |
783 childList: true, | 807 childList: true, |
784 attributes: shouldObserveAttributes(this.patterns), | 808 attributes: shouldObserveAttributes(this.patterns), |
785 characterData: shouldObserveCharacterData(this.patterns), | 809 characterData: shouldObserveCharacterData(this.patterns), |
786 subtree: true | 810 subtree: true |
787 } | 811 } |
788 ); | 812 ); |
789 this.document.addEventListener("load", this.onLoad.bind(this), true); | 813 this.document.addEventListener("load", this.onLoad.bind(this), true); |
790 } | 814 } |
791 } | 815 } |
792 }; | 816 }; |
793 | 817 |
794 exports.ElemHideEmulation = ElemHideEmulation; | 818 exports.ElemHideEmulation = ElemHideEmulation; |
LEFT | RIGHT |