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; |
36 return 0; | 44 return 0; |
37 } | 45 } |
38 | 46 |
39 function makeSelector(node, selector) | 47 function makeSelector(node, selector = "") |
40 { | 48 { |
41 if (node == null) | 49 if (node == null) |
42 return null; | 50 return null; |
43 if (!node.parentElement) | 51 if (!node.parentElement) |
44 { | 52 { |
45 let newSelector = ":root"; | 53 let newSelector = ":root"; |
46 if (selector) | 54 if (selector) |
47 newSelector += " > " + selector; | 55 newSelector += " > " + selector; |
48 return newSelector; | 56 return newSelector; |
49 } | 57 } |
(...skipping 187 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, |
255 dependsOnDOM: true, | |
247 | 256 |
248 get dependsOnStyles() | 257 get dependsOnStyles() |
249 { | 258 { |
250 return this._innerSelectors.some(selector => selector.dependsOnStyles); | 259 return this._innerSelectors.some(selector => selector.dependsOnStyles); |
251 }, | 260 }, |
252 | 261 |
253 get dependsOnCharacterData() | 262 get dependsOnCharacterData() |
254 { | 263 { |
255 return this._innerSelectors.some( | 264 return this._innerSelectors.some( |
256 selector => selector.dependsOnCharacterData | 265 selector => selector.dependsOnCharacterData |
257 ); | 266 ); |
258 }, | 267 }, |
259 | 268 |
260 get maybeDependsOnAttributes() | 269 get maybeDependsOnAttributes() |
261 { | 270 { |
262 return this._innerSelectors.some( | 271 return this._innerSelectors.some( |
263 selector => selector.maybeDependsOnAttributes | 272 selector => selector.maybeDependsOnAttributes |
264 ); | 273 ); |
265 }, | 274 }, |
266 | 275 |
267 *getSelectors(prefix, subtree, styles) | 276 *getSelectors(prefix, subtree, styles) |
268 { | 277 { |
269 for (let element of this.getElements(prefix, subtree, styles)) | 278 for (let element of this.getElements(prefix, subtree, styles)) |
270 yield [makeSelector(element, ""), element]; | 279 yield [makeSelector(element), element]; |
271 }, | 280 }, |
272 | 281 |
273 /** | 282 /** |
274 * Generator function returning selected elements. | 283 * Generator function returning selected elements. |
275 * @param {string} prefix the prefix for the selector. | 284 * @param {string} prefix the prefix for the selector. |
276 * @param {Node} subtree the subtree we work on. | 285 * @param {Node} subtree the subtree we work on. |
277 * @param {StringifiedStyle[]} styles the stringified style objects. | 286 * @param {StringifiedStyle[]} styles the stringified style objects. |
278 */ | 287 */ |
279 *getElements(prefix, subtree, styles) | 288 *getElements(prefix, subtree, styles) |
280 { | 289 { |
(...skipping 18 matching lines...) Expand all Loading... | |
299 } | 308 } |
300 }; | 309 }; |
301 | 310 |
302 function ContainsSelector(textContent) | 311 function ContainsSelector(textContent) |
303 { | 312 { |
304 this._regexp = makeRegExpParameter(textContent); | 313 this._regexp = makeRegExpParameter(textContent); |
305 } | 314 } |
306 | 315 |
307 ContainsSelector.prototype = { | 316 ContainsSelector.prototype = { |
308 requiresHiding: true, | 317 requiresHiding: true, |
318 dependsOnDOM: true, | |
309 dependsOnCharacterData: true, | 319 dependsOnCharacterData: true, |
310 | 320 |
311 *getSelectors(prefix, subtree, styles) | 321 *getSelectors(prefix, subtree, styles) |
312 { | 322 { |
313 for (let element of this.getElements(prefix, subtree, styles)) | 323 for (let element of this.getElements(prefix, subtree, styles)) |
314 yield [makeSelector(element, ""), subtree]; | 324 yield [makeSelector(element), subtree]; |
315 }, | 325 }, |
316 | 326 |
317 *getElements(prefix, subtree, styles) | 327 *getElements(prefix, subtree, styles) |
318 { | 328 { |
319 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? | 329 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? |
320 prefix + "*" : prefix; | 330 prefix + "*" : prefix; |
321 | 331 |
322 let elements = scopedQuerySelectorAll(subtree, actualPrefix); | 332 let elements = scopedQuerySelectorAll(subtree, actualPrefix); |
323 if (elements) | 333 if (elements) |
324 { | 334 { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
370 } | 380 } |
371 }, | 381 }, |
372 | 382 |
373 *getSelectors(prefix, subtree, styles) | 383 *getSelectors(prefix, subtree, styles) |
374 { | 384 { |
375 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) | 385 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) |
376 yield [selector, subtree]; | 386 yield [selector, subtree]; |
377 } | 387 } |
378 }; | 388 }; |
379 | 389 |
380 function isSelectorHidingOnlyPattern(pattern) | 390 function Pattern(selectors, text) |
381 { | 391 { |
382 return pattern.selectors.some(s => s.preferHideWithSelector) && | 392 this.selectors = selectors; |
383 !pattern.selectors.some(s => s.requiresHiding); | 393 this.text = text; |
394 } | |
395 | |
396 Pattern.prototype = { | |
397 isSelectorHidingOnlyPattern() | |
398 { | |
399 return getCachedPropertyValue( | |
400 this, "_selectorHidingOnlyPattern", | |
401 () => this.selectors.some(selector => selector.preferHideWithSelector) && | |
402 !this.selectors.some(selector => selector.requiresHiding) | |
403 ); | |
404 }, | |
405 | |
406 get dependsOnStyles() | |
407 { | |
408 return getCachedPropertyValue( | |
409 this, "_dependsOnStyles", | |
410 () => this.selectors.some(selector => selector.dependsOnStyles) | |
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}) | |
460 { | |
461 if (!stylesheets && !mutations) | |
462 return patterns.slice(); | |
463 | |
464 return patterns.filter( | |
465 pattern => (stylesheets && pattern.dependsOnStyles) || | |
466 (mutations && pattern.dependsOnDOM) | |
467 ); | |
384 } | 468 } |
385 | 469 |
386 function shouldObserveAttributes(patterns) | 470 function shouldObserveAttributes(patterns) |
387 { | 471 { |
388 // Observe changes to attributes if either there's a plain selector that | 472 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 } | 473 } |
401 | 474 |
402 function shouldObserveCharacterData(patterns) | 475 function shouldObserveCharacterData(patterns) |
403 { | 476 { |
404 // Observe changes to character data only if there's a contains selector in | 477 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 } | 478 } |
412 | 479 |
413 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) | 480 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) |
414 { | 481 { |
415 this.document = document; | 482 this.document = document; |
416 this.addSelectorsFunc = addSelectorsFunc; | 483 this.addSelectorsFunc = addSelectorsFunc; |
417 this.hideElemsFunc = hideElemsFunc; | 484 this.hideElemsFunc = hideElemsFunc; |
418 this.observer = new MutationObserver(this.observe.bind(this)); | 485 this.observer = new MutationObserver(this.observe.bind(this)); |
419 this.useInlineStyles = true; | 486 this.useInlineStyles = true; |
kzar
2018/03/19 21:18:54
This doesn't match `alwaysHideWithSelector` in the
Manish Jethani
2018/03/20 11:20:23
Done.
| |
420 } | 487 } |
421 | 488 |
422 ElemHideEmulation.prototype = { | 489 ElemHideEmulation.prototype = { |
423 isSameOrigin(stylesheet) | 490 isSameOrigin(stylesheet) |
424 { | 491 { |
425 try | 492 try |
426 { | 493 { |
427 return new URL(stylesheet.href).origin == this.document.location.origin; | 494 return new URL(stylesheet.href).origin == this.document.location.origin; |
428 } | 495 } |
429 catch (e) | 496 catch (e) |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
496 return selectors; | 563 return selectors; |
497 }, | 564 }, |
498 | 565 |
499 /** | 566 /** |
500 * Processes the current document and applies all rules to it. | 567 * Processes the current document and applies all rules to it. |
501 * @param {CSSStyleSheet[]} [stylesheets] | 568 * @param {CSSStyleSheet[]} [stylesheets] |
502 * The list of new stylesheets that have been added to the document and | 569 * The list of new stylesheets that have been added to the document and |
503 * made reprocessing necessary. This parameter shouldn't be passed in for | 570 * made reprocessing necessary. This parameter shouldn't be passed in for |
504 * the initial processing, all of document's stylesheets will be considered | 571 * the initial processing, all of document's stylesheets will be considered |
505 * then and all rules, including the ones not dependent on styles. | 572 * then and all rules, including the ones not dependent on styles. |
573 * @param {MutationRecord[]} [mutations] | |
574 * The list of DOM mutations that have been applied to the document and | |
575 * made reprocessing necessary. This parameter shouldn't be passed in for | |
576 * the initial processing, the entire document will be considered | |
577 * then and all rules, including the ones not dependent on the DOM. | |
506 * @param {function} [done] | 578 * @param {function} [done] |
507 * Callback to call when done. | 579 * Callback to call when done. |
508 */ | 580 */ |
509 _addSelectors(stylesheets, done) | 581 _addSelectors(stylesheets, mutations, done) |
510 { | 582 { |
583 let patterns = filterPatterns(this.patterns, {stylesheets, mutations}); | |
584 | |
511 let selectors = []; | 585 let selectors = []; |
512 let selectorFilters = []; | 586 let selectorFilters = []; |
513 | 587 |
514 let elements = []; | 588 let elements = []; |
515 let elementFilters = []; | 589 let elementFilters = []; |
516 | 590 |
517 let cssStyles = []; | 591 let cssStyles = []; |
518 | 592 |
519 let stylesheetOnlyChange = !!stylesheets; | 593 // If neither any style sheets nor any DOM mutations have been specified, |
520 if (!stylesheets) | 594 // do full processing. |
595 if (!stylesheets && !mutations) | |
521 stylesheets = this.document.styleSheets; | 596 stylesheets = this.document.styleSheets; |
522 | 597 |
523 for (let stylesheet of stylesheets) | 598 // If there are any DOM mutations and any of the patterns depends on both |
599 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the | |
600 // rules in every style sheet in the document, because we need to run | |
601 // querySelectorAll afterwards. On the other hand, if we only have patterns | |
602 // that depend on either styles or DOM both not both | |
603 // (e.g. -abp-properties or -abp-contains), we can skip this part. | |
604 if (mutations && patterns.some(pattern => pattern.dependsOnStylesAndDOM)) | |
605 stylesheets = this.document.styleSheets; | |
606 | |
607 for (let stylesheet of stylesheets || []) | |
524 { | 608 { |
525 // Explicitly ignore third-party stylesheets to ensure consistent behavior | 609 // Explicitly ignore third-party stylesheets to ensure consistent behavior |
526 // between Firefox and Chrome. | 610 // between Firefox and Chrome. |
527 if (!this.isSameOrigin(stylesheet)) | 611 if (!this.isSameOrigin(stylesheet)) |
528 continue; | 612 continue; |
529 | 613 |
530 let rules = stylesheet.cssRules; | 614 let rules = stylesheet.cssRules; |
531 if (!rules) | 615 if (!rules) |
532 continue; | 616 continue; |
533 | 617 |
534 for (let rule of rules) | 618 for (let rule of rules) |
535 { | 619 { |
536 if (rule.type != rule.STYLE_RULE) | 620 if (rule.type != rule.STYLE_RULE) |
537 continue; | 621 continue; |
538 | 622 |
539 cssStyles.push(stringifyStyle(rule)); | 623 cssStyles.push(stringifyStyle(rule)); |
540 } | 624 } |
541 } | 625 } |
542 | 626 |
543 let patterns = this.patterns.slice(); | |
544 let pattern = null; | 627 let pattern = null; |
545 let generator = null; | 628 let generator = null; |
546 | 629 |
547 let processPatterns = () => | 630 let processPatterns = () => |
548 { | 631 { |
549 let cycleStart = performance.now(); | 632 let cycleStart = performance.now(); |
550 | 633 |
551 if (!pattern) | 634 if (!pattern) |
552 { | 635 { |
553 if (!patterns.length) | 636 if (!patterns.length) |
554 { | 637 { |
555 this.addSelectorsFunc(selectors, selectorFilters); | 638 if (selectors.length > 0) |
556 this.hideElemsFunc(elements, elementFilters); | 639 this.addSelectorsFunc(selectors, selectorFilters); |
640 if (elements.length > 0) | |
641 this.hideElemsFunc(elements, elementFilters); | |
557 if (typeof done == "function") | 642 if (typeof done == "function") |
558 done(); | 643 done(); |
559 return; | 644 return; |
560 } | 645 } |
561 | 646 |
562 pattern = patterns.shift(); | 647 pattern = patterns.shift(); |
563 | 648 |
564 if (stylesheetOnlyChange && | |
565 !pattern.selectors.some(selector => selector.dependsOnStyles)) | |
566 { | |
567 pattern = null; | |
568 return processPatterns(); | |
569 } | |
570 generator = evaluate(pattern.selectors, 0, "", | 649 generator = evaluate(pattern.selectors, 0, "", |
571 this.document, cssStyles); | 650 this.document, cssStyles); |
572 } | 651 } |
573 for (let selector of generator) | 652 for (let selector of generator) |
574 { | 653 { |
575 if (selector != null) | 654 if (selector != null) |
576 { | 655 { |
577 if (!this.useInlineStyles || | 656 if (!this.useInlineStyles || |
578 isSelectorHidingOnlyPattern(pattern)) | 657 pattern.isSelectorHidingOnlyPattern()) |
579 { | 658 { |
580 selectors.push(selector); | 659 selectors.push(selector); |
581 selectorFilters.push(pattern.text); | 660 selectorFilters.push(pattern.text); |
582 } | 661 } |
583 else | 662 else |
584 { | 663 { |
585 for (let element of this.document.querySelectorAll(selector)) | 664 for (let element of this.document.querySelectorAll(selector)) |
586 { | 665 { |
587 elements.push(element); | 666 elements.push(element); |
588 elementFilters.push(pattern.text); | 667 elementFilters.push(pattern.text); |
(...skipping 26 matching lines...) Expand all Loading... | |
615 }, | 694 }, |
616 | 695 |
617 _filteringInProgress: false, | 696 _filteringInProgress: false, |
618 _lastInvocation: -MIN_INVOCATION_INTERVAL, | 697 _lastInvocation: -MIN_INVOCATION_INTERVAL, |
619 _scheduledProcessing: null, | 698 _scheduledProcessing: null, |
620 | 699 |
621 /** | 700 /** |
622 * Re-run filtering either immediately or queued. | 701 * Re-run filtering either immediately or queued. |
623 * @param {CSSStyleSheet[]} [stylesheets] | 702 * @param {CSSStyleSheet[]} [stylesheets] |
624 * new stylesheets to be processed. This parameter should be omitted | 703 * new stylesheets to be processed. This parameter should be omitted |
625 * for DOM modification (full reprocessing required). | 704 * for full reprocessing. |
705 * @param {MutationRecord[]} [mutations] | |
706 * new DOM mutations to be processed. This parameter should be omitted | |
707 * for full reprocessing. | |
626 */ | 708 */ |
627 queueFiltering(stylesheets) | 709 queueFiltering(stylesheets, mutations) |
628 { | 710 { |
629 let completion = () => | 711 let completion = () => |
630 { | 712 { |
631 this._lastInvocation = performance.now(); | 713 this._lastInvocation = performance.now(); |
632 this._filteringInProgress = false; | 714 this._filteringInProgress = false; |
633 if (this._scheduledProcessing) | 715 if (this._scheduledProcessing) |
634 { | 716 { |
635 let newStylesheets = this._scheduledProcessing.stylesheets; | 717 let params = Object.assign({}, this._scheduledProcessing); |
636 this._scheduledProcessing = null; | 718 this._scheduledProcessing = null; |
637 this.queueFiltering(newStylesheets); | 719 this.queueFiltering(params.stylesheets, params.mutations); |
638 } | 720 } |
639 }; | 721 }; |
640 | 722 |
641 if (this._scheduledProcessing) | 723 if (this._scheduledProcessing) |
642 { | 724 { |
643 if (!stylesheets) | 725 if (!stylesheets && !mutations) |
644 this._scheduledProcessing.stylesheets = null; | 726 { |
645 else if (this._scheduledProcessing.stylesheets) | 727 this._scheduledProcessing = {}; |
646 this._scheduledProcessing.stylesheets.push(...stylesheets); | 728 } |
729 else | |
730 { | |
731 if (stylesheets) | |
732 { | |
733 if (!this._scheduledProcessing.stylesheets) | |
734 this._scheduledProcessing.stylesheets = []; | |
735 this._scheduledProcessing.stylesheets.push(...stylesheets); | |
736 } | |
737 if (mutations) | |
738 { | |
739 if (!this._scheduledProcessing.mutations) | |
740 this._scheduledProcessing.mutations = []; | |
741 this._scheduledProcessing.mutations.push(...mutations); | |
742 } | |
743 } | |
647 } | 744 } |
648 else if (this._filteringInProgress) | 745 else if (this._filteringInProgress) |
649 { | 746 { |
650 this._scheduledProcessing = {stylesheets}; | 747 this._scheduledProcessing = {stylesheets, mutations}; |
651 } | 748 } |
652 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) | 749 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) |
653 { | 750 { |
654 this._scheduledProcessing = {stylesheets}; | 751 this._scheduledProcessing = {stylesheets, mutations}; |
655 setTimeout(() => | 752 setTimeout(() => |
656 { | 753 { |
657 let newStylesheets = this._scheduledProcessing.stylesheets; | 754 let params = Object.assign({}, this._scheduledProcessing); |
658 this._filteringInProgress = true; | 755 this._filteringInProgress = true; |
659 this._scheduledProcessing = null; | 756 this._scheduledProcessing = null; |
660 this._addSelectors(newStylesheets, completion); | 757 this._addSelectors(params.stylesheets, params.mutations, completion); |
661 }, | 758 }, |
662 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); | 759 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); |
663 } | 760 } |
664 else if (this.document.readyState == "loading") | 761 else if (this.document.readyState == "loading") |
665 { | 762 { |
666 this._scheduledProcessing = {stylesheets}; | 763 this._scheduledProcessing = {stylesheets, mutations}; |
667 let handler = () => | 764 let handler = () => |
668 { | 765 { |
669 document.removeEventListener("DOMContentLoaded", handler); | 766 document.removeEventListener("DOMContentLoaded", handler); |
670 let newStylesheets = this._scheduledProcessing.stylesheets; | 767 let params = Object.assign({}, this._scheduledProcessing); |
671 this._filteringInProgress = true; | 768 this._filteringInProgress = true; |
672 this._scheduledProcessing = null; | 769 this._scheduledProcessing = null; |
673 this._addSelectors(newStylesheets, completion); | 770 this._addSelectors(params.stylesheets, params.mutations, completion); |
674 }; | 771 }; |
675 document.addEventListener("DOMContentLoaded", handler); | 772 document.addEventListener("DOMContentLoaded", handler); |
676 } | 773 } |
677 else | 774 else |
678 { | 775 { |
679 this._filteringInProgress = true; | 776 this._filteringInProgress = true; |
680 this._addSelectors(stylesheets, completion); | 777 this._addSelectors(stylesheets, mutations, completion); |
681 } | 778 } |
682 }, | 779 }, |
683 | 780 |
684 onLoad(event) | 781 onLoad(event) |
685 { | 782 { |
686 let stylesheet = event.target.sheet; | 783 let stylesheet = event.target.sheet; |
687 if (stylesheet) | 784 if (stylesheet) |
688 this.queueFiltering([stylesheet]); | 785 this.queueFiltering([stylesheet]); |
689 }, | 786 }, |
690 | 787 |
691 observe(mutations) | 788 observe(mutations) |
692 { | 789 { |
693 this.queueFiltering(); | 790 this.queueFiltering(null, mutations); |
694 }, | 791 }, |
695 | 792 |
696 apply(patterns) | 793 apply(patterns) |
697 { | 794 { |
698 this.patterns = []; | 795 this.patterns = []; |
699 for (let pattern of patterns) | 796 for (let pattern of patterns) |
700 { | 797 { |
701 let selectors = this.parseSelector(pattern.selector); | 798 let selectors = this.parseSelector(pattern.selector); |
702 if (selectors != null && selectors.length > 0) | 799 if (selectors != null && selectors.length > 0) |
703 this.patterns.push({selectors, text: pattern.text}); | 800 this.patterns.push(new Pattern(selectors, pattern.text)); |
704 } | 801 } |
705 | 802 |
706 if (this.patterns.length > 0) | 803 if (this.patterns.length > 0) |
707 { | 804 { |
708 this.queueFiltering(); | 805 this.queueFiltering(); |
709 this.observer.observe( | 806 this.observer.observe( |
710 this.document, | 807 this.document, |
711 { | 808 { |
712 childList: true, | 809 childList: true, |
713 attributes: shouldObserveAttributes(this.patterns), | 810 attributes: shouldObserveAttributes(this.patterns), |
714 characterData: shouldObserveCharacterData(this.patterns), | 811 characterData: shouldObserveCharacterData(this.patterns), |
715 subtree: true | 812 subtree: true |
716 } | 813 } |
717 ); | 814 ); |
718 this.document.addEventListener("load", this.onLoad.bind(this), true); | 815 this.document.addEventListener("load", this.onLoad.bind(this), true); |
719 } | 816 } |
720 } | 817 } |
721 }; | 818 }; |
722 | 819 |
723 exports.ElemHideEmulation = ElemHideEmulation; | 820 exports.ElemHideEmulation = ElemHideEmulation; |
LEFT | RIGHT |