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

Delta Between Two Patch Sets: lib/filterClasses.js

Issue 29791555: Issue 6727 - Use string rather than map for single-domain filters (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Created May 26, 2018, 10:52 a.m.
Right Patch Set: Use temporary Map objects Created June 6, 2018, 12:13 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH 3 * Copyright (C) 2006-present eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
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 });
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld