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 |
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
379 domainSource: null, | 379 domainSource: null, |
380 | 380 |
381 /** | 381 /** |
382 * Separator character used in domainSource property, must be | 382 * Separator character used in domainSource property, must be |
383 * overridden by subclasses | 383 * overridden by subclasses |
384 * @type {string} | 384 * @type {string} |
385 */ | 385 */ |
386 domainSeparator: null, | 386 domainSeparator: null, |
387 | 387 |
388 /** | 388 /** |
389 * Determines whether the trailing dot in domain names isn't important and | |
390 * should be ignored, must be overridden by subclasses. | |
391 * @type {boolean} | |
392 */ | |
393 ignoreTrailingDot: true, | |
394 | |
395 /** | |
396 * Determines whether domainSource is already upper-case, | 389 * Determines whether domainSource is already upper-case, |
397 * can be overridden by subclasses. | 390 * can be overridden by subclasses. |
398 * @type {boolean} | 391 * @type {boolean} |
399 */ | 392 */ |
400 domainSourceIsUpperCase: false, | 393 domainSourceIsUpperCase: false, |
401 | 394 |
402 /** | 395 /** |
403 * String specifying the domain that this filter should match on, or map | 396 * Map containing domains that this filter should match on/not match |
404 * containing domains that this filter should match on/not match on, or null | 397 * on or null if the filter should match on all domains |
405 * if the filter should match on all domains | 398 * @type {?Map.<string,boolean>} |
406 * @type {?Map.<string,boolean>|string} | |
407 */ | 399 */ |
408 get domains() | 400 get domains() |
409 { | 401 { |
410 // Despite this property being cached, the getter is called | 402 // Despite this property being cached, the getter is called |
411 // several times on Safari, due to WebKit bug 132872 | 403 // several times on Safari, due to WebKit bug 132872 |
412 let prop = Object.getOwnPropertyDescriptor(this, "domains"); | 404 let prop = Object.getOwnPropertyDescriptor(this, "_domains"); |
413 if (prop) | 405 if (prop) |
414 return prop.value; | 406 { |
407 let {value} = prop; | |
408 return typeof value == "string" ? | |
409 new Map([[value, true], ["", false]]) : value; | |
410 } | |
415 | 411 |
416 let domains = null; | 412 let domains = null; |
417 | 413 |
418 if (this.domainSource) | 414 if (this.domainSource) |
419 { | 415 { |
420 let source = this.domainSource; | 416 let source = this.domainSource; |
421 if (!this.domainSourceIsUpperCase) | 417 if (!this.domainSourceIsUpperCase) |
422 { | 418 { |
423 // RegExpFilter already have uppercase domains | 419 // RegExpFilter already have uppercase domains |
424 source = source.toUpperCase(); | 420 source = source.toUpperCase(); |
425 } | 421 } |
426 let list = source.split(this.domainSeparator); | 422 let list = source.split(this.domainSeparator); |
427 if (list.length == 1 && list[0][0] != "~") | 423 if (list.length == 1 && list[0][0] != "~") |
428 { | 424 { |
429 // Fast track for the common one-domain scenario | 425 // Fast track for the common one-domain scenario |
430 if (this.ignoreTrailingDot) | |
431 list[0] = list[0].replace(/\.+$/, ""); | |
432 domains = list[0]; | 426 domains = list[0]; |
433 } | 427 } |
434 else | 428 else |
435 { | 429 { |
436 let hasIncludes = false; | 430 let hasIncludes = false; |
437 for (let i = 0; i < list.length; i++) | 431 for (let i = 0; i < list.length; i++) |
438 { | 432 { |
439 let domain = list[i]; | 433 let domain = list[i]; |
440 if (this.ignoreTrailingDot) | |
441 domain = domain.replace(/\.+$/, ""); | |
442 if (domain == "") | 434 if (domain == "") |
443 continue; | 435 continue; |
444 | 436 |
445 let include; | 437 let include; |
446 if (domain[0] == "~") | 438 if (domain[0] == "~") |
447 { | 439 { |
448 include = false; | 440 include = false; |
449 domain = domain.substr(1); | 441 domain = domain.substr(1); |
450 } | 442 } |
451 else | 443 else |
452 { | 444 { |
453 include = true; | 445 include = true; |
454 hasIncludes = true; | 446 hasIncludes = true; |
455 } | 447 } |
456 | 448 |
457 if (!domains) | 449 if (!domains) |
458 domains = new Map(); | 450 domains = new Map(); |
459 | 451 |
460 domains.set(domain, include); | 452 domains.set(domain, include); |
461 } | 453 } |
462 if (domains) | 454 if (domains) |
463 domains.set("", !hasIncludes); | 455 domains.set("", !hasIncludes); |
464 } | 456 } |
465 | 457 |
466 this.domainSource = null; | 458 this.domainSource = null; |
467 } | 459 } |
468 | 460 |
469 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); | 461 Object.defineProperty(this, "_domains", {value: domains}); |
470 return this.domains; | 462 return this.domains; |
471 }, | 463 }, |
472 | 464 |
473 /** | 465 /** |
474 * Array containing public keys of websites that this filter should apply to | 466 * Array containing public keys of websites that this filter should apply to |
475 * @type {?string[]} | 467 * @type {?string[]} |
476 */ | 468 */ |
477 sitekeys: null, | 469 sitekeys: null, |
478 | 470 |
479 /** | 471 /** |
(...skipping 13 matching lines...) Expand all Loading... | |
493 return false; | 485 return false; |
494 } | 486 } |
495 | 487 |
496 // If no domains are set the rule matches everywhere | 488 // If no domains are set the rule matches everywhere |
497 if (!this.domains) | 489 if (!this.domains) |
498 return true; | 490 return true; |
499 | 491 |
500 // If the document has no host name, match only if the filter | 492 // If the document has no host name, match only if the filter |
501 // isn't restricted to specific domains | 493 // isn't restricted to specific domains |
502 if (!docDomain) | 494 if (!docDomain) |
503 return typeof this.domains != "string" && this.domains.get(""); | 495 return this.domains.get(""); |
504 | 496 |
505 if (this.ignoreTrailingDot) | 497 docDomain = docDomain.replace(/\.+$/, "").toUpperCase(); |
506 docDomain = docDomain.replace(/\.+$/, ""); | |
507 docDomain = docDomain.toUpperCase(); | |
508 | |
509 if (typeof this.domains == "string") | |
510 { | |
511 return docDomain == this.domains || | |
512 docDomain.endsWith("." + this.domains); | |
513 } | |
514 | 498 |
515 while (true) | 499 while (true) |
516 { | 500 { |
517 let isDomainIncluded = this.domains.get(docDomain); | 501 let isDomainIncluded = this.domains.get(docDomain); |
518 if (typeof isDomainIncluded != "undefined") | 502 if (typeof isDomainIncluded != "undefined") |
519 return isDomainIncluded; | 503 return isDomainIncluded; |
520 | 504 |
521 let nextDot = docDomain.indexOf("."); | 505 let nextDot = docDomain.indexOf("."); |
522 if (nextDot < 0) | 506 if (nextDot < 0) |
523 break; | 507 break; |
524 docDomain = docDomain.substr(nextDot + 1); | 508 docDomain = docDomain.substr(nextDot + 1); |
525 } | 509 } |
526 return this.domains.get(""); | 510 return this.domains.get(""); |
527 }, | 511 }, |
528 | 512 |
529 /** | 513 /** |
530 * Checks whether this filter is active only on a domain and its subdomains. | 514 * Checks whether this filter is active only on a domain and its subdomains. |
531 * @param {string} docDomain | 515 * @param {string} docDomain |
532 * @return {boolean} | 516 * @return {boolean} |
533 */ | 517 */ |
534 isActiveOnlyOnDomain(docDomain) | 518 isActiveOnlyOnDomain(docDomain) |
535 { | 519 { |
536 if (!docDomain || !this.domains || | 520 if (!docDomain || !this.domains || this.domains.get("")) |
537 typeof this.domains != "string" && this.domains.get("")) | |
sergei
2018/05/28 09:22:07
IMO, perhaps it would be safer to rather check tha
kzar
2018/06/05 16:51:30
Disagree on this one, people that don't understand
Manish Jethani
2018/06/06 11:16:31
This is one of those things that can lead to an en
Manish Jethani
2018/06/06 12:31:18
typeof is typically faster than instanceof, so whe
| |
538 { | |
539 return false; | 521 return false; |
540 } | 522 |
541 | 523 docDomain = docDomain.replace(/\.+$/, "").toUpperCase(); |
542 if (this.ignoreTrailingDot) | |
543 docDomain = docDomain.replace(/\.+$/, ""); | |
544 docDomain = docDomain.toUpperCase(); | |
545 | |
546 if (typeof this.domains == "string") | |
547 { | |
548 return docDomain == this.domains || | |
549 this.domains.endsWith("." + docDomain); | |
550 } | |
551 | 524 |
552 for (let [domain, isIncluded] of this.domains) | 525 for (let [domain, isIncluded] of this.domains) |
553 { | 526 { |
554 if (isIncluded && domain != docDomain) | 527 if (isIncluded && domain != docDomain) |
555 { | 528 { |
556 if (domain.length <= docDomain.length) | 529 if (domain.length <= docDomain.length) |
557 return false; | 530 return false; |
558 | 531 |
559 if (!domain.endsWith("." + docDomain)) | 532 if (!domain.endsWith("." + docDomain)) |
560 return false; | 533 return false; |
561 } | 534 } |
562 } | 535 } |
563 | 536 |
564 return true; | 537 return true; |
565 }, | 538 }, |
566 | 539 |
567 /** | 540 /** |
568 * Checks whether this filter is generic or specific | 541 * Checks whether this filter is generic or specific |
569 * @return {boolean} | 542 * @return {boolean} |
570 */ | 543 */ |
571 isGeneric() | 544 isGeneric() |
572 { | 545 { |
573 return !(this.sitekeys && this.sitekeys.length) && | 546 return !(this.sitekeys && this.sitekeys.length) && |
574 (!this.domains || | 547 (!this.domains || this.domains.get("")); |
575 typeof this.domains != "string" && this.domains.get("")); | |
576 }, | 548 }, |
577 | 549 |
578 /** | 550 /** |
579 * See Filter.serialize() | 551 * See Filter.serialize() |
580 * @inheritdoc | 552 * @inheritdoc |
581 */ | 553 */ |
582 serialize(buffer) | 554 serialize(buffer) |
583 { | 555 { |
584 if (this._disabled || this._hitCount || this._lastHit) | 556 if (this._disabled || this._hitCount || this._lastHit) |
585 { | 557 { |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
679 { | 651 { |
680 // Despite this property being cached, the getter is called | 652 // Despite this property being cached, the getter is called |
681 // several times on Safari, due to WebKit bug 132872 | 653 // several times on Safari, due to WebKit bug 132872 |
682 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); | 654 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); |
683 if (prop) | 655 if (prop) |
684 return prop.value; | 656 return prop.value; |
685 | 657 |
686 let source = Filter.toRegExp(this.regexpSource); | 658 let source = Filter.toRegExp(this.regexpSource); |
687 let regexp = new RegExp(source, this.matchCase ? "" : "i"); | 659 let regexp = new RegExp(source, this.matchCase ? "" : "i"); |
688 Object.defineProperty(this, "regexp", {value: regexp}); | 660 Object.defineProperty(this, "regexp", {value: regexp}); |
689 delete this.regexpSource; | 661 this.regexpSource = null; |
690 return regexp; | 662 return regexp; |
691 }, | 663 }, |
692 /** | 664 /** |
693 * Content types the filter applies to, combination of values from | 665 * Content types the filter applies to, combination of values from |
694 * RegExpFilter.typeMap | 666 * RegExpFilter.typeMap |
695 * @type {number} | 667 * @type {number} |
696 */ | 668 */ |
697 contentType: 0x7FFFFFFF, | 669 contentType: 0x7FFFFFFF, |
698 /** | 670 /** |
699 * Defines whether the filter should distinguish between lower and | 671 * Defines whether the filter should distinguish between lower and |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
836 collapse = false; | 808 collapse = false; |
837 else if (option == "SITEKEY" && value) | 809 else if (option == "SITEKEY" && value) |
838 sitekeys = value.toUpperCase(); | 810 sitekeys = value.toUpperCase(); |
839 else if (option == "REWRITE" && value) | 811 else if (option == "REWRITE" && value) |
840 rewrite = value; | 812 rewrite = value; |
841 else | 813 else |
842 return new InvalidFilter(origText, "filter_unknown_option"); | 814 return new InvalidFilter(origText, "filter_unknown_option"); |
843 } | 815 } |
844 } | 816 } |
845 | 817 |
818 // For security reasons, never match $rewrite filters | |
819 // against requests that might load any code to be executed. | |
820 if (rewrite != null) | |
821 { | |
822 if (contentType == null) | |
823 ({contentType} = RegExpFilter.prototype); | |
824 contentType &= ~(RegExpFilter.typeMap.SCRIPT | | |
825 RegExpFilter.typeMap.SUBDOCUMENT | | |
826 RegExpFilter.typeMap.OBJECT | | |
827 RegExpFilter.typeMap.OBJECT_SUBREQUEST); | |
828 } | |
829 | |
846 try | 830 try |
847 { | 831 { |
848 if (blocking) | 832 if (blocking) |
849 { | 833 { |
850 if (csp && Filter.invalidCSPRegExp.test(csp)) | 834 if (csp && Filter.invalidCSPRegExp.test(csp)) |
851 return new InvalidFilter(origText, "filter_invalid_csp"); | 835 return new InvalidFilter(origText, "filter_invalid_csp"); |
852 | 836 |
853 return new BlockingFilter(origText, text, contentType, matchCase, domains, | 837 return new BlockingFilter(origText, text, contentType, matchCase, domains, |
854 thirdParty, sitekeys, collapse, csp, rewrite); | 838 thirdParty, sitekeys, collapse, csp, rewrite); |
855 } | 839 } |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
919 * BlockingFilter.prototype.rewrite. | 903 * BlockingFilter.prototype.rewrite. |
920 * @constructor | 904 * @constructor |
921 * @augments RegExpFilter | 905 * @augments RegExpFilter |
922 */ | 906 */ |
923 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, | 907 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, |
924 thirdParty, sitekeys, collapse, csp, rewrite) | 908 thirdParty, sitekeys, collapse, csp, rewrite) |
925 { | 909 { |
926 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, | 910 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, |
927 thirdParty, sitekeys); | 911 thirdParty, sitekeys); |
928 | 912 |
929 this.collapse = collapse; | 913 if (collapse != null) |
930 this.csp = csp; | 914 this.collapse = collapse; |
931 this.rewrite = rewrite; | 915 |
916 if (csp != null) | |
917 this.csp = csp; | |
918 | |
919 if (rewrite != null) | |
920 this.rewrite = rewrite; | |
932 } | 921 } |
933 exports.BlockingFilter = BlockingFilter; | 922 exports.BlockingFilter = BlockingFilter; |
934 | 923 |
935 BlockingFilter.prototype = extend(RegExpFilter, { | 924 BlockingFilter.prototype = extend(RegExpFilter, { |
936 type: "blocking", | 925 type: "blocking", |
937 | 926 |
938 /** | 927 /** |
939 * Defines whether the filter should collapse blocked content. | 928 * Defines whether the filter should collapse blocked content. |
940 * Can be null (use the global preference). | 929 * Can be null (use the global preference). |
941 * @type {?boolean} | 930 * @type {?boolean} |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1007 * restricted to | 996 * restricted to |
1008 * @param {string} selector CSS selector for the HTML elements that should be | 997 * @param {string} selector CSS selector for the HTML elements that should be |
1009 * hidden | 998 * hidden |
1010 * @constructor | 999 * @constructor |
1011 * @augments ActiveFilter | 1000 * @augments ActiveFilter |
1012 */ | 1001 */ |
1013 function ElemHideBase(text, domains, selector) | 1002 function ElemHideBase(text, domains, selector) |
1014 { | 1003 { |
1015 ActiveFilter.call(this, text, domains || null); | 1004 ActiveFilter.call(this, text, domains || null); |
1016 | 1005 |
1017 if (domains) | |
1018 { | |
1019 this.selectorDomains = domains.replace(/,~[^,]+/g, "") | |
1020 .replace(/^~[^,]+,?/, "").toLowerCase(); | |
1021 } | |
1022 | |
1023 // Braces are being escaped to prevent CSS rule injection. | 1006 // Braces are being escaped to prevent CSS rule injection. |
1024 this.selector = selector.replace("{", "\\7B ").replace("}", "\\7D "); | 1007 this.selector = selector.replace("{", "\\7B ").replace("}", "\\7D "); |
1025 } | 1008 } |
1026 exports.ElemHideBase = ElemHideBase; | 1009 exports.ElemHideBase = ElemHideBase; |
1027 | 1010 |
1028 ElemHideBase.prototype = extend(ActiveFilter, { | 1011 ElemHideBase.prototype = extend(ActiveFilter, { |
1029 /** | 1012 /** |
1030 * @see ActiveFilter.domainSeparator | 1013 * @see ActiveFilter.domainSeparator |
1031 */ | 1014 */ |
1032 domainSeparator: ",", | 1015 domainSeparator: ",", |
1033 | 1016 |
1034 /** | |
1035 * @see ActiveFilter.ignoreTrailingDot | |
1036 */ | |
1037 ignoreTrailingDot: false, | |
1038 | |
1039 /** | |
1040 * Host names or domains the filter should be restricted to (can be null for | |
1041 * no restriction) | |
1042 * @type {?string} | |
1043 */ | |
1044 selectorDomains: null, | |
1045 /** | 1017 /** |
1046 * CSS selector for the HTML elements that should be hidden | 1018 * CSS selector for the HTML elements that should be hidden |
1047 * @type {string} | 1019 * @type {string} |
1048 */ | 1020 */ |
1049 selector: null | 1021 selector: null |
1050 }); | 1022 }); |
1051 | 1023 |
1052 /** | 1024 /** |
1053 * Creates an element hiding filter from a pre-parsed text representation | 1025 * Creates an element hiding filter from a pre-parsed text representation |
1054 * | 1026 * |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1131 */ | 1103 */ |
1132 function ElemHideEmulationFilter(text, domains, selector) | 1104 function ElemHideEmulationFilter(text, domains, selector) |
1133 { | 1105 { |
1134 ElemHideBase.call(this, text, domains, selector); | 1106 ElemHideBase.call(this, text, domains, selector); |
1135 } | 1107 } |
1136 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; | 1108 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; |
1137 | 1109 |
1138 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { | 1110 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { |
1139 type: "elemhideemulation" | 1111 type: "elemhideemulation" |
1140 }); | 1112 }); |
LEFT | RIGHT |