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 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 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 // Just in case the getSelectors() generator above had to run some heavy | 217 // Just in case the getSelectors() generator above had to run some heavy |
213 // document.querySelectorAll() call which didn't produce any results, make | 218 // document.querySelectorAll() call which didn't produce any results, make |
214 // sure there is at least one point where execution can pause. | 219 // sure there is at least one point where execution can pause. |
215 yield null; | 220 yield null; |
216 } | 221 } |
217 | 222 |
218 function PlainSelector(selector) | 223 function PlainSelector(selector) |
219 { | 224 { |
220 this._selector = selector; | 225 this._selector = selector; |
221 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector); | 226 this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector); |
| 227 this.dependsOnDOM = this.maybeDependsOnAttributes; |
222 } | 228 } |
223 | 229 |
224 PlainSelector.prototype = { | 230 PlainSelector.prototype = { |
225 /** | 231 /** |
226 * Generator function returning a pair of selector | 232 * Generator function returning a pair of selector |
227 * string and subtree. | 233 * string and subtree. |
228 * @param {string} prefix the prefix for the selector. | 234 * @param {string} prefix the prefix for the selector. |
229 * @param {Node} subtree the subtree we work on. | 235 * @param {Node} subtree the subtree we work on. |
230 * @param {StringifiedStyle[]} styles the stringified style objects. | 236 * @param {StringifiedStyle[]} styles the stringified style objects. |
231 */ | 237 */ |
(...skipping 29 matching lines...) Expand all Loading... |
261 get maybeDependsOnAttributes() | 267 get maybeDependsOnAttributes() |
262 { | 268 { |
263 return this._innerSelectors.some( | 269 return this._innerSelectors.some( |
264 selector => selector.maybeDependsOnAttributes | 270 selector => selector.maybeDependsOnAttributes |
265 ); | 271 ); |
266 }, | 272 }, |
267 | 273 |
268 *getSelectors(prefix, subtree, styles) | 274 *getSelectors(prefix, subtree, styles) |
269 { | 275 { |
270 for (let element of this.getElements(prefix, subtree, styles)) | 276 for (let element of this.getElements(prefix, subtree, styles)) |
271 yield [makeSelector(element, ""), element]; | 277 yield [makeSelector(element), element]; |
272 }, | 278 }, |
273 | 279 |
274 /** | 280 /** |
275 * Generator function returning selected elements. | 281 * Generator function returning selected elements. |
276 * @param {string} prefix the prefix for the selector. | 282 * @param {string} prefix the prefix for the selector. |
277 * @param {Node} subtree the subtree we work on. | 283 * @param {Node} subtree the subtree we work on. |
278 * @param {StringifiedStyle[]} styles the stringified style objects. | 284 * @param {StringifiedStyle[]} styles the stringified style objects. |
279 */ | 285 */ |
280 *getElements(prefix, subtree, styles) | 286 *getElements(prefix, subtree, styles) |
281 { | 287 { |
(...skipping 24 matching lines...) Expand all Loading... |
306 } | 312 } |
307 | 313 |
308 ContainsSelector.prototype = { | 314 ContainsSelector.prototype = { |
309 requiresHiding: true, | 315 requiresHiding: true, |
310 dependsOnDOM: true, | 316 dependsOnDOM: true, |
311 dependsOnCharacterData: true, | 317 dependsOnCharacterData: true, |
312 | 318 |
313 *getSelectors(prefix, subtree, styles) | 319 *getSelectors(prefix, subtree, styles) |
314 { | 320 { |
315 for (let element of this.getElements(prefix, subtree, styles)) | 321 for (let element of this.getElements(prefix, subtree, styles)) |
316 yield [makeSelector(element, ""), subtree]; | 322 yield [makeSelector(element), subtree]; |
317 }, | 323 }, |
318 | 324 |
319 *getElements(prefix, subtree, styles) | 325 *getElements(prefix, subtree, styles) |
320 { | 326 { |
321 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? | 327 let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? |
322 prefix + "*" : prefix; | 328 prefix + "*" : prefix; |
323 | 329 |
324 let elements = scopedQuerySelectorAll(subtree, actualPrefix); | 330 let elements = scopedQuerySelectorAll(subtree, actualPrefix); |
| 331 |
325 if (elements) | 332 if (elements) |
326 { | 333 { |
| 334 let lastRoot = null; |
327 for (let element of elements) | 335 for (let element of elements) |
328 { | 336 { |
| 337 // For a filter like div:-abp-contains(Hello) and a subtree like |
| 338 // <div id="a"><div id="b"><div id="c">Hello</div></div></div> |
| 339 // we're only interested in div#a |
| 340 if (lastRoot && lastRoot.contains(element)) |
| 341 { |
| 342 yield null; |
| 343 continue; |
| 344 } |
| 345 |
| 346 lastRoot = element; |
| 347 |
329 if (this._regexp && this._regexp.test(element.textContent)) | 348 if (this._regexp && this._regexp.test(element.textContent)) |
330 yield element; | 349 yield element; |
331 else | 350 else |
332 yield null; | 351 yield null; |
333 } | 352 } |
334 } | 353 } |
335 } | 354 } |
336 }; | 355 }; |
337 | 356 |
338 function PropsSelector(propertyExpression) | 357 function PropsSelector(propertyExpression) |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
372 } | 391 } |
373 }, | 392 }, |
374 | 393 |
375 *getSelectors(prefix, subtree, styles) | 394 *getSelectors(prefix, subtree, styles) |
376 { | 395 { |
377 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) | 396 for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) |
378 yield [selector, subtree]; | 397 yield [selector, subtree]; |
379 } | 398 } |
380 }; | 399 }; |
381 | 400 |
382 function isSelectorHidingOnlyPattern(pattern) | 401 function Pattern(selectors, text) |
383 { | 402 { |
384 return pattern.selectors.some(s => s.preferHideWithSelector) && | 403 this.selectors = selectors; |
385 !pattern.selectors.some(s => s.requiresHiding); | 404 this.text = text; |
386 } | 405 } |
387 | 406 |
388 function patternDependsOnStyles(pattern) | 407 Pattern.prototype = { |
389 { | 408 isSelectorHidingOnlyPattern() |
390 return pattern.selectors.some(s => s.dependsOnStyles); | 409 { |
391 } | 410 return getCachedPropertyValue( |
392 | 411 this, "_selectorHidingOnlyPattern", |
393 function patternDependsOnDOM(pattern) | 412 () => this.selectors.some(selector => selector.preferHideWithSelector) && |
394 { | 413 !this.selectors.some(selector => selector.requiresHiding) |
395 return pattern.selectors.some(s => s.dependsOnDOM); | 414 ); |
396 } | 415 }, |
397 | 416 |
398 function patternDependsOnStylesAndDOM(pattern) | 417 get dependsOnStyles() |
399 { | 418 { |
400 return pattern.selectors.some(s => s.dependsOnStyles && s.dependsOnDOM); | 419 return getCachedPropertyValue( |
401 } | 420 this, "_dependsOnStyles", |
402 | 421 () => this.selectors.some(selector => selector.dependsOnStyles) |
403 function patternMaybeDependsOnAttributes(pattern) | 422 ); |
404 { | 423 }, |
405 // Observe changes to attributes if either there's a plain selector that | 424 |
406 // looks like an ID selector, class selector, or attribute selector in one of | 425 get dependsOnDOM() |
407 // the patterns (e.g. "a[href='https://example.com/']") | 426 { |
408 // or there's a properties selector nested inside a has selector | 427 return getCachedPropertyValue( |
409 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") | 428 this, "_dependsOnDOM", |
410 return pattern.selectors.some( | 429 () => this.selectors.some(selector => selector.dependsOnDOM) |
411 selector => selector.maybeDependsOnAttributes || | 430 ); |
412 (selector instanceof HasSelector && | 431 }, |
413 selector.dependsOnStyles) | 432 |
414 ); | 433 get dependsOnStylesAndDOM() |
415 } | 434 { |
416 | 435 return getCachedPropertyValue( |
417 function patternDependsOnCharacterData(pattern) | 436 this, "_dependsOnStylesAndDOM", |
418 { | 437 () => this.selectors.some(selector => selector.dependsOnStyles && |
419 // Observe changes to character data only if there's a contains selector in | 438 selector.dependsOnDOM) |
420 // one of the patterns. | 439 ); |
421 return pattern.selectors.some(selector => selector.dependsOnCharacterData); | 440 }, |
422 } | 441 |
423 | 442 get maybeDependsOnAttributes() |
424 function patternMatchesMutationTypes(pattern, mutationTypes) | 443 { |
425 { | 444 // Observe changes to attributes if either there's a plain selector that |
426 return mutationTypes.has("childList") || | 445 // looks like an ID selector, class selector, or attribute selector in one |
427 (mutationTypes.has("attributes") && | 446 // of the patterns (e.g. "a[href='https://example.com/']") |
428 patternMaybeDependsOnAttributes(pattern)) || | 447 // or there's a properties selector nested inside a has selector |
429 (mutationTypes.has("characterData") && | 448 // (e.g. "div:-abp-has(:-abp-properties(color: blue))") |
430 patternDependsOnCharacterData(pattern)); | 449 return getCachedPropertyValue( |
431 } | 450 this, "_maybeDependsOnAttributes", |
| 451 () => this.selectors.some( |
| 452 selector => selector.maybeDependsOnAttributes || |
| 453 (selector instanceof HasSelector && |
| 454 selector.dependsOnStyles) |
| 455 ) |
| 456 ); |
| 457 }, |
| 458 |
| 459 get dependsOnCharacterData() |
| 460 { |
| 461 // Observe changes to character data only if there's a contains selector in |
| 462 // one of the patterns. |
| 463 return getCachedPropertyValue( |
| 464 this, "_dependsOnCharacterData", |
| 465 () => this.selectors.some(selector => selector.dependsOnCharacterData) |
| 466 ); |
| 467 }, |
| 468 |
| 469 matchesMutationTypes(mutationTypes) |
| 470 { |
| 471 let mutationTypeMatchMap = getCachedPropertyValue( |
| 472 this, "_mutationTypeMatchMap", |
| 473 () => new Map([ |
| 474 // All types of DOM-dependent patterns are affected by mutations of |
| 475 // type "childList". |
| 476 ["childList", true], |
| 477 ["attributes", this.maybeDependsOnAttributes], |
| 478 ["characterData", this.dependsOnCharacterData] |
| 479 ]) |
| 480 ); |
| 481 |
| 482 for (let mutationType of mutationTypes) |
| 483 { |
| 484 if (mutationTypeMatchMap.get(mutationType)) |
| 485 return true; |
| 486 } |
| 487 |
| 488 return false; |
| 489 } |
| 490 }; |
432 | 491 |
433 function extractMutationTypes(mutations) | 492 function extractMutationTypes(mutations) |
434 { | 493 { |
435 let types = new Set(); | 494 let types = new Set(); |
436 | 495 |
437 for (let mutation of mutations) | 496 for (let mutation of mutations) |
438 { | 497 { |
439 types.add(mutation.type); | 498 types.add(mutation.type); |
440 | 499 |
441 // There are only 3 types of mutations: "attributes", "characterData", and | 500 // There are only 3 types of mutations: "attributes", "characterData", and |
442 // "childList". | 501 // "childList". |
443 if (types.size == 3) | 502 if (types.size == 3) |
444 break; | 503 break; |
445 } | 504 } |
446 | 505 |
447 return types; | 506 return types; |
448 } | 507 } |
449 | 508 |
450 function filterPatterns(patterns, {stylesheets, mutations}) | 509 function filterPatterns(patterns, {stylesheets, mutations}) |
451 { | 510 { |
452 if (!stylesheets && !mutations) | 511 if (!stylesheets && !mutations) |
453 return patterns.slice(); | 512 return patterns.slice(); |
454 | 513 |
455 let mutationTypes = mutations ? extractMutationTypes(mutations) : null; | 514 let mutationTypes = mutations ? extractMutationTypes(mutations) : null; |
456 | 515 |
457 return patterns.filter( | 516 return patterns.filter( |
458 pattern => (stylesheets && patternDependsOnStyles(pattern)) || | 517 pattern => (stylesheets && pattern.dependsOnStyles) || |
459 (mutations && patternDependsOnDOM(pattern) && | 518 (mutations && pattern.dependsOnDOM && |
460 patternMatchesMutationTypes(pattern, mutationTypes)) | 519 pattern.matchesMutationTypes(mutationTypes)) |
461 ); | 520 ); |
462 } | 521 } |
463 | 522 |
464 function shouldObserveAttributes(patterns) | 523 function shouldObserveAttributes(patterns) |
465 { | 524 { |
466 return patterns.some(patternMaybeDependsOnAttributes); | 525 return patterns.some(pattern => pattern.maybeDependsOnAttributes); |
467 } | 526 } |
468 | 527 |
469 function shouldObserveCharacterData(patterns) | 528 function shouldObserveCharacterData(patterns) |
470 { | 529 { |
471 return patterns.some(patternDependsOnCharacterData); | 530 return patterns.some(pattern => pattern.dependsOnCharacterData); |
472 } | 531 } |
473 | 532 |
474 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) | 533 function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) |
475 { | 534 { |
476 this.document = document; | 535 this.document = document; |
477 this.addSelectorsFunc = addSelectorsFunc; | 536 this.addSelectorsFunc = addSelectorsFunc; |
478 this.hideElemsFunc = hideElemsFunc; | 537 this.hideElemsFunc = hideElemsFunc; |
479 this.observer = new MutationObserver(this.observe.bind(this)); | 538 this.observer = new MutationObserver(this.observe.bind(this)); |
| 539 this.useInlineStyles = true; |
480 } | 540 } |
481 | 541 |
482 ElemHideEmulation.prototype = { | 542 ElemHideEmulation.prototype = { |
483 isSameOrigin(stylesheet) | 543 isSameOrigin(stylesheet) |
484 { | 544 { |
485 try | 545 try |
486 { | 546 { |
487 return new URL(stylesheet.href).origin == this.document.location.origin; | 547 return new URL(stylesheet.href).origin == this.document.location.origin; |
488 } | 548 } |
489 catch (e) | 549 catch (e) |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
587 // do full processing. | 647 // do full processing. |
588 if (!stylesheets && !mutations) | 648 if (!stylesheets && !mutations) |
589 stylesheets = this.document.styleSheets; | 649 stylesheets = this.document.styleSheets; |
590 | 650 |
591 // If there are any DOM mutations and any of the patterns depends on both | 651 // If there are any DOM mutations and any of the patterns depends on both |
592 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the | 652 // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the |
593 // rules in every style sheet in the document, because we need to run | 653 // rules in every style sheet in the document, because we need to run |
594 // querySelectorAll afterwards. On the other hand, if we only have patterns | 654 // querySelectorAll afterwards. On the other hand, if we only have patterns |
595 // that depend on either styles or DOM both not both | 655 // that depend on either styles or DOM both not both |
596 // (e.g. -abp-properties or -abp-contains), we can skip this part. | 656 // (e.g. -abp-properties or -abp-contains), we can skip this part. |
597 if (mutations && patterns.some(patternDependsOnStylesAndDOM)) | 657 if (mutations && patterns.some(pattern => pattern.dependsOnStylesAndDOM)) |
598 stylesheets = this.document.styleSheets; | 658 stylesheets = this.document.styleSheets; |
599 | 659 |
600 for (let stylesheet of stylesheets || []) | 660 for (let stylesheet of stylesheets || []) |
601 { | 661 { |
602 // Explicitly ignore third-party stylesheets to ensure consistent behavior | 662 // Explicitly ignore third-party stylesheets to ensure consistent behavior |
603 // between Firefox and Chrome. | 663 // between Firefox and Chrome. |
604 if (!this.isSameOrigin(stylesheet)) | 664 if (!this.isSameOrigin(stylesheet)) |
605 continue; | 665 continue; |
606 | 666 |
607 let rules = stylesheet.cssRules; | 667 let rules; |
| 668 try |
| 669 { |
| 670 rules = stylesheet.cssRules; |
| 671 } |
| 672 catch (e) |
| 673 { |
| 674 // On Firefox, there is a chance that an InvalidAccessError |
| 675 // get thrown when accessing cssRules. Just skip the stylesheet |
| 676 // in that case. |
| 677 // See https://searchfox.org/mozilla-central/rev/f65d7528e34ef1a7665b4a1
a7b7cdb1388fcd3aa/layout/style/StyleSheet.cpp#699 |
| 678 continue; |
| 679 } |
| 680 |
608 if (!rules) | 681 if (!rules) |
609 continue; | 682 continue; |
610 | 683 |
611 for (let rule of rules) | 684 for (let rule of rules) |
612 { | 685 { |
613 if (rule.type != rule.STYLE_RULE) | 686 if (rule.type != rule.STYLE_RULE) |
614 continue; | 687 continue; |
615 | 688 |
616 cssStyles.push(stringifyStyle(rule)); | 689 cssStyles.push(stringifyStyle(rule)); |
617 } | 690 } |
618 } | 691 } |
619 | 692 |
620 let pattern = null; | 693 let pattern = null; |
621 let generator = null; | 694 let generator = null; |
622 | 695 |
623 let processPatterns = () => | 696 let processPatterns = () => |
624 { | 697 { |
625 let cycleStart = performance.now(); | 698 let cycleStart = performance.now(); |
626 | 699 |
627 if (!pattern) | 700 if (!pattern) |
628 { | 701 { |
629 if (!patterns.length) | 702 if (!patterns.length) |
630 { | 703 { |
631 this.addSelectorsFunc(selectors, selectorFilters); | 704 if (selectors.length > 0) |
632 this.hideElemsFunc(elements, elementFilters); | 705 this.addSelectorsFunc(selectors, selectorFilters); |
| 706 if (elements.length > 0) |
| 707 this.hideElemsFunc(elements, elementFilters); |
633 if (typeof done == "function") | 708 if (typeof done == "function") |
634 done(); | 709 done(); |
635 return; | 710 return; |
636 } | 711 } |
637 | 712 |
638 pattern = patterns.shift(); | 713 pattern = patterns.shift(); |
639 | 714 |
640 generator = evaluate(pattern.selectors, 0, "", | 715 generator = evaluate(pattern.selectors, 0, "", |
641 this.document, cssStyles); | 716 this.document, cssStyles); |
642 } | 717 } |
643 for (let selector of generator) | 718 for (let selector of generator) |
644 { | 719 { |
645 if (selector != null) | 720 if (selector != null) |
646 { | 721 { |
647 if (isSelectorHidingOnlyPattern(pattern)) | 722 if (!this.useInlineStyles || |
| 723 pattern.isSelectorHidingOnlyPattern()) |
648 { | 724 { |
649 selectors.push(selector); | 725 selectors.push(selector); |
650 selectorFilters.push(pattern.text); | 726 selectorFilters.push(pattern.text); |
651 } | 727 } |
652 else | 728 else |
653 { | 729 { |
654 for (let element of this.document.querySelectorAll(selector)) | 730 for (let element of this.document.querySelectorAll(selector)) |
655 { | 731 { |
656 elements.push(element); | 732 elements.push(element); |
657 elementFilters.push(pattern.text); | 733 elementFilters.push(pattern.text); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
697 * for full reprocessing. | 773 * for full reprocessing. |
698 */ | 774 */ |
699 queueFiltering(stylesheets, mutations) | 775 queueFiltering(stylesheets, mutations) |
700 { | 776 { |
701 let completion = () => | 777 let completion = () => |
702 { | 778 { |
703 this._lastInvocation = performance.now(); | 779 this._lastInvocation = performance.now(); |
704 this._filteringInProgress = false; | 780 this._filteringInProgress = false; |
705 if (this._scheduledProcessing) | 781 if (this._scheduledProcessing) |
706 { | 782 { |
707 let {stylesheets, mutations} = this._scheduledProcessing; | 783 let params = Object.assign({}, this._scheduledProcessing); |
708 this._scheduledProcessing = null; | 784 this._scheduledProcessing = null; |
709 this.queueFiltering(stylesheets, mutations); | 785 this.queueFiltering(params.stylesheets, params.mutations); |
710 } | 786 } |
711 }; | 787 }; |
712 | 788 |
713 if (this._scheduledProcessing) | 789 if (this._scheduledProcessing) |
714 { | 790 { |
715 if (!stylesheets && !mutations) | 791 if (!stylesheets && !mutations) |
716 { | 792 { |
717 this._scheduledProcessing = {}; | 793 this._scheduledProcessing = {}; |
718 } | 794 } |
719 else | 795 else if (this._scheduledProcessing.stylesheets || |
| 796 this._scheduledProcessing.mutations) |
720 { | 797 { |
721 if (stylesheets) | 798 if (stylesheets) |
722 { | 799 { |
723 if (!this._scheduledProcessing.stylesheets) | 800 if (!this._scheduledProcessing.stylesheets) |
724 this._scheduledProcessing.stylesheets = []; | 801 this._scheduledProcessing.stylesheets = []; |
725 this._scheduledProcessing.stylesheets.push(...stylesheets); | 802 this._scheduledProcessing.stylesheets.push(...stylesheets); |
726 } | 803 } |
727 if (mutations) | 804 if (mutations) |
728 { | 805 { |
729 if (!this._scheduledProcessing.mutations) | 806 if (!this._scheduledProcessing.mutations) |
730 this._scheduledProcessing.mutations = []; | 807 this._scheduledProcessing.mutations = []; |
731 this._scheduledProcessing.mutations.push(...mutations); | 808 this._scheduledProcessing.mutations.push(...mutations); |
732 } | 809 } |
733 } | 810 } |
734 } | 811 } |
735 else if (this._filteringInProgress) | 812 else if (this._filteringInProgress) |
736 { | 813 { |
737 this._scheduledProcessing = {stylesheets, mutations}; | 814 this._scheduledProcessing = {stylesheets, mutations}; |
738 } | 815 } |
739 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) | 816 else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL) |
740 { | 817 { |
741 this._scheduledProcessing = {stylesheets, mutations}; | 818 this._scheduledProcessing = {stylesheets, mutations}; |
742 setTimeout(() => | 819 setTimeout(() => |
743 { | 820 { |
744 let {stylesheets, mutations} = this._scheduledProcessing; | 821 let params = Object.assign({}, this._scheduledProcessing); |
745 this._filteringInProgress = true; | 822 this._filteringInProgress = true; |
746 this._scheduledProcessing = null; | 823 this._scheduledProcessing = null; |
747 this._addSelectors(stylesheets, mutations, completion); | 824 this._addSelectors(params.stylesheets, params.mutations, completion); |
748 }, | 825 }, |
749 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); | 826 MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation)); |
750 } | 827 } |
751 else if (this.document.readyState == "loading") | 828 else if (this.document.readyState == "loading") |
752 { | 829 { |
753 this._scheduledProcessing = {stylesheets, mutations}; | 830 this._scheduledProcessing = {stylesheets, mutations}; |
754 let handler = () => | 831 let handler = () => |
755 { | 832 { |
756 document.removeEventListener("DOMContentLoaded", handler); | 833 this.document.removeEventListener("DOMContentLoaded", handler); |
757 let {stylesheets, mutations} = this._scheduledProcessing; | 834 let params = Object.assign({}, this._scheduledProcessing); |
758 this._filteringInProgress = true; | 835 this._filteringInProgress = true; |
759 this._scheduledProcessing = null; | 836 this._scheduledProcessing = null; |
760 this._addSelectors(stylesheets, mutations, completion); | 837 this._addSelectors(params.stylesheets, params.mutations, completion); |
761 }; | 838 }; |
762 document.addEventListener("DOMContentLoaded", handler); | 839 this.document.addEventListener("DOMContentLoaded", handler); |
763 } | 840 } |
764 else | 841 else |
765 { | 842 { |
766 this._filteringInProgress = true; | 843 this._filteringInProgress = true; |
767 this._addSelectors(stylesheets, mutations, completion); | 844 this._addSelectors(stylesheets, mutations, completion); |
768 } | 845 } |
769 }, | 846 }, |
770 | 847 |
771 onLoad(event) | 848 onLoad(event) |
772 { | 849 { |
773 let stylesheet = event.target.sheet; | 850 let stylesheet = event.target.sheet; |
774 if (stylesheet) | 851 if (stylesheet) |
775 this.queueFiltering([stylesheet]); | 852 this.queueFiltering([stylesheet]); |
776 }, | 853 }, |
777 | 854 |
778 observe(mutations) | 855 observe(mutations) |
779 { | 856 { |
780 this.queueFiltering(null, mutations); | 857 this.queueFiltering(null, mutations); |
781 }, | 858 }, |
782 | 859 |
783 apply(patterns) | 860 apply(patterns) |
784 { | 861 { |
785 this.patterns = []; | 862 this.patterns = []; |
786 for (let pattern of patterns) | 863 for (let pattern of patterns) |
787 { | 864 { |
788 let selectors = this.parseSelector(pattern.selector); | 865 let selectors = this.parseSelector(pattern.selector); |
789 if (selectors != null && selectors.length > 0) | 866 if (selectors != null && selectors.length > 0) |
790 this.patterns.push({selectors, text: pattern.text}); | 867 this.patterns.push(new Pattern(selectors, pattern.text)); |
791 } | 868 } |
792 | 869 |
793 if (this.patterns.length > 0) | 870 if (this.patterns.length > 0) |
794 { | 871 { |
795 this.queueFiltering(); | 872 this.queueFiltering(); |
796 this.observer.observe( | 873 this.observer.observe( |
797 this.document, | 874 this.document, |
798 { | 875 { |
799 childList: true, | 876 childList: true, |
800 attributes: shouldObserveAttributes(this.patterns), | 877 attributes: shouldObserveAttributes(this.patterns), |
801 characterData: shouldObserveCharacterData(this.patterns), | 878 characterData: shouldObserveCharacterData(this.patterns), |
802 subtree: true | 879 subtree: true |
803 } | 880 } |
804 ); | 881 ); |
805 this.document.addEventListener("load", this.onLoad.bind(this), true); | 882 this.document.addEventListener("load", this.onLoad.bind(this), true); |
806 } | 883 } |
807 } | 884 } |
808 }; | 885 }; |
809 | 886 |
810 exports.ElemHideEmulation = ElemHideEmulation; | 887 exports.ElemHideEmulation = ElemHideEmulation; |
LEFT | RIGHT |