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

Side by Side Diff: lib/filterClasses.js

Issue 4559243822759936: Issue 431/432 - Remove special handling for the $sitekey option (Closed)
Patch Set: Created Aug. 12, 2014, 9:30 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2014 Eyeo GmbH 3 * Copyright (C) 2006-2014 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 364 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 375
376 this.domainSource = null; 376 this.domainSource = null;
377 } 377 }
378 378
379 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); 379 Object.defineProperty(this, "domains", {value: domains, enumerable: true});
380 return this.domains; 380 return this.domains;
381 }, 381 },
382 382
383 /** 383 /**
384 * Checks whether this filter is active on a domain. 384 * Checks whether this filter is active on a domain.
385 * @param {String} docDomain domain name of the document that loads the URL
386 * @param {String} sitekey (optional) public key provided by the document
Wladimir Palant 2014/08/14 19:58:15 Nit: Please indicate the optional parameter with s
Thomas Greiner 2014/08/29 15:57:29 Done.
387 * @return {Boolean} true in case of the filter being active
385 */ 388 */
386 isActiveOnDomain: function(/**String*/ docDomain) /**Boolean*/ 389 isActiveOnDomain: function(docDomain, sitekey)
387 { 390 {
388 // If no domains are set the rule matches everywhere 391 // If no domains or sitekeys are set the rule matches everywhere
389 if (!this.domains) 392 if (!this.domains && !this.sitekeys)
Wladimir Palant 2014/08/14 19:58:15 I think there should be a |sitekey: null| property
Thomas Greiner 2014/08/29 15:57:29 Done.
390 return true; 393 return true;
391 394
392 // If the document has no host name, match only if the filter isn't restrict ed to specific domains 395 let activeDomain = true;
Wladimir Palant 2014/08/14 19:58:15 This made the code significantly more complicated
Thomas Greiner 2014/08/29 15:57:29 Done.
393 if (!docDomain) 396 if (this.domains)
394 return this.domains[""]; 397 {
398 if (!docDomain)
399 // If the document has no host name, match only if the filter isn't rest ricted to specific domains
400 activeDomain = this.domains[""];
401 else
402 {
403 if (this.ignoreTrailingDot)
404 docDomain = docDomain.replace(/\.+$/, "");
405 docDomain = docDomain.toUpperCase();
395 406
396 if (this.ignoreTrailingDot) 407 let domain = "";
397 docDomain = docDomain.replace(/\.+$/, ""); 408 while (true)
398 docDomain = docDomain.toUpperCase(); 409 {
410 if (docDomain in this.domains)
411 {
412 domain = docDomain;
413 break;
414 }
399 415
400 while (true) 416 let nextDot = docDomain.indexOf(".");
417 if (nextDot < 0)
418 break;
419 docDomain = docDomain.substr(nextDot + 1);
420 }
421 activeDomain = this.domains[domain];
422 }
423 }
424
425 let activeSitekey = true;
426 if (this.sitekeys)
401 { 427 {
402 if (docDomain in this.domains) 428 if (!sitekey)
403 return this.domains[docDomain]; 429 // If the document has no sitekey, match only if the filter isn't restri cted by sitekeys
404 430 activeSitekey = this.sitekeys[""];
405 let nextDot = docDomain.indexOf("."); 431 else
406 if (nextDot < 0) 432 {
407 break; 433 let [key, signature] = sitekey.split("_", 2);
Wladimir Palant 2014/08/14 19:58:15 There should be no signature at this point.
Thomas Greiner 2014/08/29 15:57:29 Done.
408 docDomain = docDomain.substr(nextDot + 1); 434 let formattedKey = key.replace(/=/g, "").toUpperCase();
435 if (formattedKey in this.sitekeys)
436 activeSitekey = this.sitekeys[formattedKey];
437 else
438 activeSitekey = this.sitekeys[""];
439 }
409 } 440 }
410 return this.domains[""]; 441 return activeDomain && activeSitekey;
411 }, 442 },
412 443
413 /** 444 /**
414 * Checks whether this filter is active only on a domain and its subdomains. 445 * Checks whether this filter is active only on a domain and its subdomains.
415 */ 446 */
416 isActiveOnlyOnDomain: function(/**String*/ docDomain) /**Boolean*/ 447 isActiveOnlyOnDomain: function(/**String*/ docDomain) /**Boolean*/
417 { 448 {
418 if (!docDomain || !this.domains || this.domains[""]) 449 if (!docDomain || !this.domains || this.domains[""])
419 return false; 450 return false;
420 451
(...skipping 27 matching lines...) Expand all
448 }; 479 };
449 480
450 /** 481 /**
451 * Abstract base class for RegExp-based filters 482 * Abstract base class for RegExp-based filters
452 * @param {String} text see Filter() 483 * @param {String} text see Filter()
453 * @param {String} regexpSource filter part that the regular expression should b e build from 484 * @param {String} regexpSource filter part that the regular expression should b e build from
454 * @param {Number} contentType (optional) Content types the filter applies to, combination of values from RegExpFilter.typeMap 485 * @param {Number} contentType (optional) Content types the filter applies to, combination of values from RegExpFilter.typeMap
455 * @param {Boolean} matchCase (optional) Defines whether the filter should dis tinguish between lower and upper case letters 486 * @param {Boolean} matchCase (optional) Defines whether the filter should dis tinguish between lower and upper case letters
456 * @param {String} domains (optional) Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" 487 * @param {String} domains (optional) Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com"
457 * @param {Boolean} thirdParty (optional) Defines whether the filter should app ly to third-party or first-party content only 488 * @param {Boolean} thirdParty (optional) Defines whether the filter should app ly to third-party or first-party content only
489 * @param {String} sitekeys (optional) Public keys of websites that this fil ter should apply to, e.g. "foo|bar|~baz"
Wladimir Palant 2014/08/14 19:58:15 Please remove the exceptions from the comment here
Thomas Greiner 2014/08/29 15:57:29 Done.
458 * @constructor 490 * @constructor
459 * @augments ActiveFilter 491 * @augments ActiveFilter
460 */ 492 */
461 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, third Party) 493 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, third Party, sitekeys)
462 { 494 {
463 ActiveFilter.call(this, text, domains); 495 ActiveFilter.call(this, text, domains, sitekeys);
464 496
465 if (contentType != null) 497 if (contentType != null)
466 this.contentType = contentType; 498 this.contentType = contentType;
467 if (matchCase) 499 if (matchCase)
468 this.matchCase = matchCase; 500 this.matchCase = matchCase;
469 if (thirdParty != null) 501 if (thirdParty != null)
470 this.thirdParty = thirdParty; 502 this.thirdParty = thirdParty;
503 if (sitekeys != null)
504 this.sitekeySource = sitekeys;
471 505
472 if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpS ource.length - 1] == "/") 506 if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpS ource.length - 1] == "/")
473 { 507 {
474 // The filter is a regular expression - convert it immediately to catch synt ax errors 508 // The filter is a regular expression - convert it immediately to catch synt ax errors
475 let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), thi s.matchCase ? "" : "i"); 509 let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), thi s.matchCase ? "" : "i");
476 Object.defineProperty(this, "regexp", {value: regexp}); 510 Object.defineProperty(this, "regexp", {value: regexp});
477 } 511 }
478 else 512 else
479 { 513 {
480 // No need to convert this filter to regular expression yet, do it on demand 514 // No need to convert this filter to regular expression yet, do it on demand
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 * @type Boolean 576 * @type Boolean
543 */ 577 */
544 matchCase: false, 578 matchCase: false,
545 /** 579 /**
546 * Defines whether the filter should apply to third-party or first-party conte nt only. Can be null (apply to all content). 580 * Defines whether the filter should apply to third-party or first-party conte nt only. Can be null (apply to all content).
547 * @type Boolean 581 * @type Boolean
548 */ 582 */
549 thirdParty: null, 583 thirdParty: null,
550 584
551 /** 585 /**
586 * String that the sitekey property should be generated from
587 * @type String
588 */
589 sitekeySource: null,
590
591 /**
592 * Map containing public keys of websites that this filter should apply to
593 * @type Object
594 */
595 get sitekeys()
596 {
597 let sitekeys = null;
598
599 if (this.sitekeySource)
600 {
601 let source = this.sitekeySource;
602 let list = source.split("|");
603 if (list.length == 1 && list[0][0] != "~")
604 {
605 // Fast track for the common one-sitekey scenario
606 sitekeys = {__proto__: null, "": false};
607 sitekeys[list[0]] = true;
608 }
609 else
610 {
611 let hasIncludes = false;
612 for (let i = 0; i < list.length; i++)
613 {
614 let sitekey = list[i];
615 if (sitekey == "")
616 continue;
617
618 let include;
619 if (sitekey[0] == "~")
620 {
621 include = false;
622 sitekey = sitekey.substr(1);
623 }
624 else
625 {
626 include = true;
627 hasIncludes = true;
628 }
629
630 if (!sitekeys)
631 sitekeys = Object.create(null);
632
633 sitekeys[sitekey] = include;
634 }
635 sitekeys[""] = !hasIncludes;
636 }
Wladimir Palant 2014/08/14 19:58:15 You don't need this complicated data structure if
Thomas Greiner 2014/08/29 15:57:29 Done.
637
638 this.sitekeySource = null;
639 }
640
641 Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true}) ;
642 return this.sitekeys;
643 },
644
645 /**
552 * Tests whether the URL matches this filter 646 * Tests whether the URL matches this filter
553 * @param {String} location URL to be tested 647 * @param {String} location URL to be tested
554 * @param {String} contentType content type identifier of the URL 648 * @param {String} contentType content type identifier of the URL
555 * @param {String} docDomain domain name of the document that loads the URL 649 * @param {String} docDomain domain name of the document that loads the URL
556 * @param {Boolean} thirdParty should be true if the URL is a third-party requ est 650 * @param {Boolean} thirdParty should be true if the URL is a third-party requ est
651 * @param {String} sitekey public key provided by the document
557 * @return {Boolean} true in case of a match 652 * @return {Boolean} true in case of a match
558 */ 653 */
559 matches: function(location, contentType, docDomain, thirdParty) 654 matches: function(location, contentType, docDomain, thirdParty, sitekey)
560 { 655 {
561 if (this.regexp.test(location) && 656 if (this.regexp.test(location) &&
562 (RegExpFilter.typeMap[contentType] & this.contentType) != 0 && 657 (RegExpFilter.typeMap[contentType] & this.contentType) != 0 &&
563 (this.thirdParty == null || this.thirdParty == thirdParty) && 658 (this.thirdParty == null || this.thirdParty == thirdParty) &&
564 this.isActiveOnDomain(docDomain)) 659 this.isActiveOnDomain(docDomain, sitekey))
565 { 660 {
566 return true; 661 return true;
567 } 662 }
568 663
569 return false; 664 return false;
570 } 665 }
571 }; 666 };
572 667
573 // Required to optimize Matcher, see also RegExpFilter.prototype.length 668 // Required to optimize Matcher, see also RegExpFilter.prototype.length
574 Object.defineProperty(RegExpFilter.prototype, "0", 669 Object.defineProperty(RegExpFilter.prototype, "0",
(...skipping 11 matching lines...) Expand all
586 let origText = text; 681 let origText = text;
587 if (text.indexOf("@@") == 0) 682 if (text.indexOf("@@") == 0)
588 { 683 {
589 blocking = false; 684 blocking = false;
590 text = text.substr(2); 685 text = text.substr(2);
591 } 686 }
592 687
593 let contentType = null; 688 let contentType = null;
594 let matchCase = null; 689 let matchCase = null;
595 let domains = null; 690 let domains = null;
596 let siteKeys = null; 691 let sitekeys = null;
597 let thirdParty = null; 692 let thirdParty = null;
598 let collapse = null; 693 let collapse = null;
599 let options; 694 let options;
600 let match = (text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null); 695 let match = (text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null);
601 if (match) 696 if (match)
602 { 697 {
603 options = match[1].toUpperCase().split(","); 698 options = match[1].toUpperCase().split(",");
604 text = match.input.substr(0, match.index); 699 text = match.input.substr(0, match.index);
605 for (let option of options) 700 for (let option of options)
606 { 701 {
(...skipping 25 matching lines...) Expand all
632 domains = value; 727 domains = value;
633 else if (option == "THIRD_PARTY") 728 else if (option == "THIRD_PARTY")
634 thirdParty = true; 729 thirdParty = true;
635 else if (option == "~THIRD_PARTY") 730 else if (option == "~THIRD_PARTY")
636 thirdParty = false; 731 thirdParty = false;
637 else if (option == "COLLAPSE") 732 else if (option == "COLLAPSE")
638 collapse = true; 733 collapse = true;
639 else if (option == "~COLLAPSE") 734 else if (option == "~COLLAPSE")
640 collapse = false; 735 collapse = false;
641 else if (option == "SITEKEY" && typeof value != "undefined") 736 else if (option == "SITEKEY" && typeof value != "undefined")
642 siteKeys = value.split(/\|/); 737 sitekeys = value;
643 else 738 else
644 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas e()); 739 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas e());
645 } 740 }
646 } 741 }
647 742
648 if (!blocking && (contentType == null || (contentType & RegExpFilter.typeMap.D OCUMENT)) && 743 if (!blocking && (contentType == null || (contentType & RegExpFilter.typeMap.D OCUMENT)) &&
649 (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text )) 744 (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text ))
650 { 745 {
651 // Exception filters shouldn't apply to pages by default unless they start w ith a protocol name 746 // Exception filters shouldn't apply to pages by default unless they start w ith a protocol name
652 if (contentType == null) 747 if (contentType == null)
653 contentType = RegExpFilter.prototype.contentType; 748 contentType = RegExpFilter.prototype.contentType;
654 contentType &= ~RegExpFilter.typeMap.DOCUMENT; 749 contentType &= ~RegExpFilter.typeMap.DOCUMENT;
655 } 750 }
656 if (!blocking && siteKeys)
657 contentType = RegExpFilter.typeMap.DOCUMENT;
658 751
659 try 752 try
660 { 753 {
661 if (blocking) 754 if (blocking)
662 return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, collapse); 755 return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse);
663 else 756 else
664 return new WhitelistFilter(origText, text, contentType, matchCase, domains , thirdParty, siteKeys); 757 return new WhitelistFilter(origText, text, contentType, matchCase, domains , thirdParty, sitekeys);
665 } 758 }
666 catch (e) 759 catch (e)
667 { 760 {
668 return new InvalidFilter(origText, e); 761 return new InvalidFilter(origText, e);
669 } 762 }
670 } 763 }
671 764
672 /** 765 /**
673 * Maps type strings like "SCRIPT" or "OBJECT" to bit masks 766 * Maps type strings like "SCRIPT" or "OBJECT" to bit masks
674 */ 767 */
(...skipping 23 matching lines...) Expand all
698 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFi lter.typeMap.POPUP); 791 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFi lter.typeMap.POPUP);
699 792
700 /** 793 /**
701 * Class for blocking filters 794 * Class for blocking filters
702 * @param {String} text see Filter() 795 * @param {String} text see Filter()
703 * @param {String} regexpSource see RegExpFilter() 796 * @param {String} regexpSource see RegExpFilter()
704 * @param {Number} contentType see RegExpFilter() 797 * @param {Number} contentType see RegExpFilter()
705 * @param {Boolean} matchCase see RegExpFilter() 798 * @param {Boolean} matchCase see RegExpFilter()
706 * @param {String} domains see RegExpFilter() 799 * @param {String} domains see RegExpFilter()
707 * @param {Boolean} thirdParty see RegExpFilter() 800 * @param {Boolean} thirdParty see RegExpFilter()
801 * @param {String} sitekeys see RegExpFilter()
708 * @param {Boolean} collapse defines whether the filter should collapse blocked content, can be null 802 * @param {Boolean} collapse defines whether the filter should collapse blocked content, can be null
709 * @constructor 803 * @constructor
710 * @augments RegExpFilter 804 * @augments RegExpFilter
711 */ 805 */
712 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thi rdParty, collapse) 806 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thi rdParty, sitekeys, collapse)
713 { 807 {
714 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty); 808 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty, sitekeys);
715 809
716 this.collapse = collapse; 810 this.collapse = collapse;
717 } 811 }
718 exports.BlockingFilter = BlockingFilter; 812 exports.BlockingFilter = BlockingFilter;
719 813
720 BlockingFilter.prototype = 814 BlockingFilter.prototype =
721 { 815 {
722 __proto__: RegExpFilter.prototype, 816 __proto__: RegExpFilter.prototype,
723 817
724 /** 818 /**
725 * Defines whether the filter should collapse blocked content. Can be null (us e the global preference). 819 * Defines whether the filter should collapse blocked content. Can be null (us e the global preference).
726 * @type Boolean 820 * @type Boolean
727 */ 821 */
728 collapse: null 822 collapse: null
729 }; 823 };
730 824
731 /** 825 /**
732 * Class for whitelist filters 826 * Class for whitelist filters
733 * @param {String} text see Filter() 827 * @param {String} text see Filter()
734 * @param {String} regexpSource see RegExpFilter() 828 * @param {String} regexpSource see RegExpFilter()
735 * @param {Number} contentType see RegExpFilter() 829 * @param {Number} contentType see RegExpFilter()
736 * @param {Boolean} matchCase see RegExpFilter() 830 * @param {Boolean} matchCase see RegExpFilter()
737 * @param {String} domains see RegExpFilter() 831 * @param {String} domains see RegExpFilter()
738 * @param {Boolean} thirdParty see RegExpFilter() 832 * @param {Boolean} thirdParty see RegExpFilter()
739 * @param {String[]} siteKeys public keys of websites that this filter should ap ply to 833 * @param {String} sitekeys see RegExpFilter()
740 * @constructor 834 * @constructor
741 * @augments RegExpFilter 835 * @augments RegExpFilter
742 */ 836 */
743 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, th irdParty, siteKeys) 837 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, th irdParty, sitekeys)
744 { 838 {
745 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty); 839 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty, sitekeys);
746
747 if (siteKeys != null)
748 this.siteKeys = siteKeys;
749 } 840 }
750 exports.WhitelistFilter = WhitelistFilter; 841 exports.WhitelistFilter = WhitelistFilter;
751 842
752 WhitelistFilter.prototype = 843 WhitelistFilter.prototype =
753 { 844 {
754 __proto__: RegExpFilter.prototype, 845 __proto__: RegExpFilter.prototype
755
756 /**
757 * List of public keys of websites that this filter should apply to
758 * @type String[]
759 */
760 siteKeys: null
761 } 846 }
762 847
763 /** 848 /**
764 * Base class for element hiding filters 849 * Base class for element hiding filters
765 * @param {String} text see Filter() 850 * @param {String} text see Filter()
766 * @param {String} domains (optional) Host names or domains the filter should be restricted to 851 * @param {String} domains (optional) Host names or domains the filter should be restricted to
767 * @param {String} selector CSS selector for the HTML elements that should be hidden 852 * @param {String} selector CSS selector for the HTML elements that should be hidden
768 * @constructor 853 * @constructor
769 * @augments ActiveFilter 854 * @augments ActiveFilter
770 */ 855 */
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
890 function ElemHideException(text, domains, selector) 975 function ElemHideException(text, domains, selector)
891 { 976 {
892 ElemHideBase.call(this, text, domains, selector); 977 ElemHideBase.call(this, text, domains, selector);
893 } 978 }
894 exports.ElemHideException = ElemHideException; 979 exports.ElemHideException = ElemHideException;
895 980
896 ElemHideException.prototype = 981 ElemHideException.prototype =
897 { 982 {
898 __proto__: ElemHideBase.prototype 983 __proto__: ElemHideBase.prototype
899 }; 984 };
OLDNEW

Powered by Google App Engine
This is Rietveld