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

Delta Between Two Patch Sets: lib/filterClasses.js

Issue 5840485868371968: Issue 616 - Add $generichide + $genericblock filter options and enforce them. (Closed)
Left Patch Set: Rebased. Created March 12, 2015, 8:33 p.m.
Right Patch Set: Remove extra parenthesis Created Sept. 28, 2015, 10:15 a.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 | « lib/contentPolicy.js ('k') | 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-2015 Eyeo GmbH 3 * Copyright (C) 2006-2015 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 /** 18 /**
19 * @fileOverview Definition of Filter class and its subclasses. 19 * @fileOverview Definition of Filter class and its subclasses.
20 */ 20 */
21 21
22 let {FilterNotifier} = require("filterNotifier"); 22 let {FilterNotifier} = require("filterNotifier");
23 let {Utils} = require("utils");
23 24
24 /** 25 /**
25 * Abstract base class for filters 26 * Abstract base class for filters
26 * 27 *
27 * @param {String} text string representation of the filter 28 * @param {String} text string representation of the filter
28 * @constructor 29 * @constructor
29 */ 30 */
30 function Filter(text) 31 function Filter(text)
31 { 32 {
32 this.text = text; 33 this.text = text;
33 this.subscriptions = []; 34 this.subscriptions = [];
34 } 35 }
35 exports.Filter = Filter; 36 exports.Filter = Filter;
36 37
37 Filter.prototype = 38 Filter.prototype =
38 { 39 {
39 /** 40 /**
40 * String representation of the filter 41 * String representation of the filter
41 * @type String 42 * @type String
42 */ 43 */
43 text: null, 44 text: null,
44 45
45 /** 46 /**
46 * Filter subscriptions the filter belongs to 47 * Filter subscriptions the filter belongs to
47 * @type Array of Subscription 48 * @type Subscription[]
48 */ 49 */
49 subscriptions: null, 50 subscriptions: null,
50 51
51 /** 52 /**
52 * Serializes the filter to an array of strings for writing out on the disk. 53 * Serializes the filter to an array of strings for writing out on the disk.
53 * @param {Array of String} buffer buffer to push the serialization results i nto 54 * @param {string[]} buffer buffer to push the serialization results into
54 */ 55 */
55 serialize: function(buffer) 56 serialize: function(buffer)
56 { 57 {
57 buffer.push("[Filter]"); 58 buffer.push("[Filter]");
58 buffer.push("text=" + this.text); 59 buffer.push("text=" + this.text);
59 }, 60 },
60 61
61 toString: function() 62 toString: function()
62 { 63 {
63 return this.text; 64 return this.text;
(...skipping 14 matching lines...) Expand all
78 /** 79 /**
79 * Regular expression that RegExp filters specified as RegExps should match 80 * Regular expression that RegExp filters specified as RegExps should match
80 * @type RegExp 81 * @type RegExp
81 */ 82 */
82 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[ ^,\s]+)?)*)?$/; 83 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[ ^,\s]+)?)*)?$/;
83 /** 84 /**
84 * Regular expression that options on a RegExp filter should match 85 * Regular expression that options on a RegExp filter should match
85 * @type RegExp 86 * @type RegExp
86 */ 87 */
87 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/ ; 88 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/ ;
89 /**
90 * Regular expression that CSS property filters should match
91 * Properties must not contain " or '
92 * @type RegExp
93 */
94 Filter.csspropertyRegExp = /\[\-abp\-properties=(["'])([^"']+)\1\]/;
88 95
89 /** 96 /**
90 * Creates a filter of correct type from its text representation - does the basi c parsing and 97 * Creates a filter of correct type from its text representation - does the basi c parsing and
91 * calls the right constructor then. 98 * calls the right constructor then.
92 * 99 *
93 * @param {String} text as in Filter() 100 * @param {String} text as in Filter()
94 * @return {Filter} 101 * @return {Filter}
95 */ 102 */
96 Filter.fromText = function(text) 103 Filter.fromText = function(text)
97 { 104 {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 } 158 }
152 else if (Filter.elemhideRegExp.test(text)) 159 else if (Filter.elemhideRegExp.test(text))
153 { 160 {
154 // Special treatment for element hiding filters, right side is allowed to co ntain spaces 161 // Special treatment for element hiding filters, right side is allowed to co ntain spaces
155 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text); 162 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text);
156 return domain.replace(/\s/g, "") + separator + selector.trim(); 163 return domain.replace(/\s/g, "") + separator + selector.trim();
157 } 164 }
158 else 165 else
159 return text.replace(/\s/g, ""); 166 return text.replace(/\s/g, "");
160 }; 167 };
168
169 /**
170 * Converts filter text into regular expression string
171 * @param {String} text as in Filter()
172 * @return {String} regular expression representation of filter text
173 */
174 Filter.toRegExp = function(text)
175 {
176 return text
177 .replace(/\*+/g, "*") // remove multiple wildcards
178 .replace(/\^\|$/, "^") // remove anchors following separator placehold er
179 .replace(/\W/g, "\\$&") // escape special symbols
180 .replace(/\\\*/g, ".*") // replace wildcards by .*
181 // process separator placeholders (all ANSI characters but alphanumeric char acters and _%.-)
182 .replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x6 0\\x7B-\\x7F]|$)")
183 .replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") // process ex tended anchor at expression start
184 .replace(/^\\\|/, "^") // process anchor at expression start
185 .replace(/\\\|$/, "$") // process anchor at expression end
186 .replace(/^(\.\*)/, "") // remove leading wildcards
187 .replace(/(\.\*)$/, ""); // remove trailing wildcards
188 }
161 189
162 /** 190 /**
163 * Class for invalid filters 191 * Class for invalid filters
164 * @param {String} text see Filter() 192 * @param {String} text see Filter()
165 * @param {String} reason Reason why this filter is invalid 193 * @param {String} reason Reason why this filter is invalid
166 * @constructor 194 * @constructor
167 * @augments Filter 195 * @augments Filter
168 */ 196 */
169 function InvalidFilter(text, reason) 197 function InvalidFilter(text, reason)
170 { 198 {
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 409
382 this.domainSource = null; 410 this.domainSource = null;
383 } 411 }
384 412
385 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); 413 Object.defineProperty(this, "domains", {value: domains, enumerable: true});
386 return this.domains; 414 return this.domains;
387 }, 415 },
388 416
389 /** 417 /**
390 * Array containing public keys of websites that this filter should apply to 418 * Array containing public keys of websites that this filter should apply to
391 * @type Array of String 419 * @type string[]
392 */ 420 */
393 sitekeys: null, 421 sitekeys: null,
394 422
395 /** 423 /**
396 * Checks whether this filter is active on a domain. 424 * Checks whether this filter is active on a domain.
397 * @param {String} docDomain domain name of the document that loads the URL 425 * @param {String} docDomain domain name of the document that loads the URL
398 * @param {String} [sitekey] public key provided by the document 426 * @param {String} [sitekey] public key provided by the document
399 * @return {Boolean} true in case of the filter being active 427 * @return {Boolean} true in case of the filter being active
400 */ 428 */
401 isActiveOnDomain: function(docDomain, sitekey) 429 isActiveOnDomain: function(docDomain, sitekey)
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 468
441 if (this.ignoreTrailingDot) 469 if (this.ignoreTrailingDot)
442 docDomain = docDomain.replace(/\.+$/, ""); 470 docDomain = docDomain.replace(/\.+$/, "");
443 docDomain = docDomain.toUpperCase(); 471 docDomain = docDomain.toUpperCase();
444 472
445 for (let domain in this.domains) 473 for (let domain in this.domains)
446 if (this.domains[domain] && domain != docDomain && (domain.length <= docDo main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng th - 1)) 474 if (this.domains[domain] && domain != docDomain && (domain.length <= docDo main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng th - 1))
447 return false; 475 return false;
448 476
449 return true; 477 return true;
478 },
479
480 /**
481 * Checks whether this filter is generic or specific
482 */
483 isGeneric: function() /**Boolean*/
484 {
485 return !(this.sitekeys && this.sitekeys.length) &&
486 (!this.domains || this.domains[""]);
450 }, 487 },
451 488
452 /** 489 /**
453 * See Filter.serialize() 490 * See Filter.serialize()
454 */ 491 */
455 serialize: function(buffer) 492 serialize: function(buffer)
456 { 493 {
457 if (this._disabled || this._hitCount || this._lastHit) 494 if (this._disabled || this._hitCount || this._lastHit)
458 { 495 {
459 Filter.prototype.serialize.call(this, buffer); 496 Filter.prototype.serialize.call(this, buffer);
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
536 * @type RegExp 573 * @type RegExp
537 */ 574 */
538 get regexp() 575 get regexp()
539 { 576 {
540 // Despite this property being cached, the getter is called 577 // Despite this property being cached, the getter is called
541 // several times on Safari, due to WebKit bug 132872 578 // several times on Safari, due to WebKit bug 132872
542 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); 579 let prop = Object.getOwnPropertyDescriptor(this, "regexp");
543 if (prop) 580 if (prop)
544 return prop.value; 581 return prop.value;
545 582
546 // Remove multiple wildcards 583 let source = Filter.toRegExp(this.regexpSource);
547 let source = this.regexpSource
548 .replace(/\*+/g, "*") // remove multiple wildcards
549 .replace(/\^\|$/, "^") // remove anchors following separator placeho lder
550 .replace(/\W/g, "\\$&") // escape special symbols
551 .replace(/\\\*/g, ".*") // replace wildcards by .*
552 // process separator placeholders (all ANSI characters but alphanumeric ch aracters and _%.-)
553 .replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\ x60\\x7B-\\x7F]|$)")
554 .replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") // process extended anchor at expression start
555 .replace(/^\\\|/, "^") // process anchor at expression start
556 .replace(/\\\|$/, "$") // process anchor at expression end
557 .replace(/^(\.\*)/, "") // remove leading wildcards
558 .replace(/(\.\*)$/, ""); // remove trailing wildcards
559
560 let regexp = new RegExp(source, this.matchCase ? "" : "i"); 584 let regexp = new RegExp(source, this.matchCase ? "" : "i");
561 Object.defineProperty(this, "regexp", {value: regexp}); 585 Object.defineProperty(this, "regexp", {value: regexp});
562 return regexp; 586 return regexp;
563 }, 587 },
564 /** 588 /**
565 * Content types the filter applies to, combination of values from RegExpFilte r.typeMap 589 * Content types the filter applies to, combination of values from RegExpFilte r.typeMap
566 * @type Number 590 * @type Number
567 */ 591 */
568 contentType: 0x7FFFFFFF, 592 contentType: 0x7FFFFFFF,
569 /** 593 /**
570 * Defines whether the filter should distinguish between lower and upper case letters 594 * Defines whether the filter should distinguish between lower and upper case letters
571 * @type Boolean 595 * @type Boolean
572 */ 596 */
573 matchCase: false, 597 matchCase: false,
574 /** 598 /**
575 * Defines whether the filter should apply to third-party or first-party conte nt only. Can be null (apply to all content). 599 * Defines whether the filter should apply to third-party or first-party conte nt only. Can be null (apply to all content).
576 * @type Boolean 600 * @type Boolean
577 */ 601 */
578 thirdParty: null, 602 thirdParty: null,
579 603
580 /** 604 /**
581 * String that the sitekey property should be generated from 605 * String that the sitekey property should be generated from
582 * @type String 606 * @type String
583 */ 607 */
584 sitekeySource: null, 608 sitekeySource: null,
585 609
586 /** 610 /**
587 * Array containing public keys of websites that this filter should apply to 611 * Array containing public keys of websites that this filter should apply to
588 * @type Array of String 612 * @type string[]
589 */ 613 */
590 get sitekeys() 614 get sitekeys()
591 { 615 {
592 // Despite this property being cached, the getter is called 616 // Despite this property being cached, the getter is called
593 // several times on Safari, due to WebKit bug 132872 617 // several times on Safari, due to WebKit bug 132872
594 let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); 618 let prop = Object.getOwnPropertyDescriptor(this, "sitekeys");
595 if (prop) 619 if (prop)
596 return prop.value; 620 return prop.value;
597 621
598 let sitekeys = null; 622 let sitekeys = null;
599 623
600 if (this.sitekeySource) 624 if (this.sitekeySource)
601 { 625 {
602 sitekeys = this.sitekeySource.split("|"); 626 sitekeys = this.sitekeySource.split("|");
603 this.sitekeySource = null; 627 this.sitekeySource = null;
604 } 628 }
605 629
606 Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true}) ; 630 Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true}) ;
607 return this.sitekeys; 631 return this.sitekeys;
608 }, 632 },
609 633
610 /** 634 /**
611 * Tests whether the URL matches this filter 635 * Tests whether the URL matches this filter
612 * @param {String} location URL to be tested 636 * @param {String} location URL to be tested
613 * @param {String} contentType content type identifier of the URL 637 * @param {String} typeMask bitmask of content / request types to match
614 * @param {String} docDomain domain name of the document that loads the URL 638 * @param {String} docDomain domain name of the document that loads the URL
615 * @param {Boolean} thirdParty should be true if the URL is a third-party requ est 639 * @param {Boolean} thirdParty should be true if the URL is a third-party requ est
616 * @param {String} sitekey public key provided by the document 640 * @param {String} sitekey public key provided by the document
617 * @return {Boolean} true in case of a match 641 * @return {Boolean} true in case of a match
618 */ 642 */
619 matches: function(location, contentType, docDomain, thirdParty, sitekey) 643 matches: function(location, typeMask, docDomain, thirdParty, sitekey)
620 { 644 {
621 if (this.regexp.test(location) && 645 if (this.contentType & typeMask &&
622 (RegExpFilter.typeMap[contentType] & this.contentType) != 0 &&
623 (this.thirdParty == null || this.thirdParty == thirdParty) && 646 (this.thirdParty == null || this.thirdParty == thirdParty) &&
624 this.isActiveOnDomain(docDomain, sitekey)) 647 this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location))
625 { 648 {
626 return true; 649 return true;
627 } 650 }
628 651
629 return false; 652 return false;
630 } 653 }
631 }; 654 };
632 655
633 // Required to optimize Matcher, see also RegExpFilter.prototype.length 656 // Required to optimize Matcher, see also RegExpFilter.prototype.length
634 Object.defineProperty(RegExpFilter.prototype, "0", 657 Object.defineProperty(RegExpFilter.prototype, "0",
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
698 collapse = true; 721 collapse = true;
699 else if (option == "~COLLAPSE") 722 else if (option == "~COLLAPSE")
700 collapse = false; 723 collapse = false;
701 else if (option == "SITEKEY" && typeof value != "undefined") 724 else if (option == "SITEKEY" && typeof value != "undefined")
702 sitekeys = value; 725 sitekeys = value;
703 else 726 else
704 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas e()); 727 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas e());
705 } 728 }
706 } 729 }
707 730
708 if (!blocking && (contentType == null || (contentType & RegExpFilter.typeMap.D OCUMENT)) &&
709 (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text ))
710 {
711 // Exception filters shouldn't apply to pages by default unless they start w ith a protocol name
712 if (contentType == null)
713 contentType = RegExpFilter.prototype.contentType;
714 contentType &= ~RegExpFilter.typeMap.DOCUMENT;
715 }
716
717 try 731 try
718 { 732 {
719 if (blocking) 733 if (blocking)
720 return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); 734 return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse);
721 else 735 else
722 return new WhitelistFilter(origText, text, contentType, matchCase, domains , thirdParty, sitekeys); 736 return new WhitelistFilter(origText, text, contentType, matchCase, domains , thirdParty, sitekeys);
723 } 737 }
724 catch (e) 738 catch (e)
725 { 739 {
726 return new InvalidFilter(origText, e); 740 return new InvalidFilter(origText, e);
(...skipping 20 matching lines...) Expand all
747 FONT: 32768, 761 FONT: 32768,
748 762
749 BACKGROUND: 4, // Backwards compat, same as IMAGE 763 BACKGROUND: 4, // Backwards compat, same as IMAGE
750 764
751 POPUP: 0x10000000, 765 POPUP: 0x10000000,
752 GENERICBLOCK: 0x20000000, 766 GENERICBLOCK: 0x20000000,
753 ELEMHIDE: 0x40000000, 767 ELEMHIDE: 0x40000000,
754 GENERICHIDE: 0x80000000 768 GENERICHIDE: 0x80000000
755 }; 769 };
756 770
757 // ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't be there by d efault 771 // DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't
758 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | 772 // be there by default
773 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.DOCUMENT |
774 RegExpFilter.typeMap.ELEMHIDE |
759 RegExpFilter.typeMap.POPUP | 775 RegExpFilter.typeMap.POPUP |
760 RegExpFilter.typeMap.GENERICHIDE | 776 RegExpFilter.typeMap.GENERICHIDE |
761 RegExpFilter.typeMap.GENERICBLOCK); 777 RegExpFilter.typeMap.GENERICBLOCK);
762 778
763 /** 779 /**
764 * Class for blocking filters 780 * Class for blocking filters
765 * @param {String} text see Filter() 781 * @param {String} text see Filter()
766 * @param {String} regexpSource see RegExpFilter() 782 * @param {String} regexpSource see RegExpFilter()
767 * @param {Number} contentType see RegExpFilter() 783 * @param {Number} contentType see RegExpFilter()
768 * @param {Boolean} matchCase see RegExpFilter() 784 * @param {Boolean} matchCase see RegExpFilter()
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
860 }; 876 };
861 877
862 /** 878 /**
863 * Creates an element hiding filter from a pre-parsed text representation 879 * Creates an element hiding filter from a pre-parsed text representation
864 * 880 *
865 * @param {String} text same as in Filter() 881 * @param {String} text same as in Filter()
866 * @param {String} domain domain part of the text representation (can be emp ty) 882 * @param {String} domain domain part of the text representation (can be emp ty)
867 * @param {String} tagName tag name part (can be empty) 883 * @param {String} tagName tag name part (can be empty)
868 * @param {String} attrRules attribute matching rules (can be empty) 884 * @param {String} attrRules attribute matching rules (can be empty)
869 * @param {String} selector raw CSS selector (can be empty) 885 * @param {String} selector raw CSS selector (can be empty)
870 * @return {ElemHideFilter|ElemHideException|InvalidFilter} 886 * @return {ElemHideFilter|ElemHideException|CSSPropertyFilter|InvalidFilter}
871 */ 887 */
872 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector) 888 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector)
873 { 889 {
874 if (!selector) 890 if (!selector)
875 { 891 {
876 if (tagName == "*") 892 if (tagName == "*")
877 tagName = ""; 893 tagName = "";
878 894
879 let id = null; 895 let id = null;
880 let additional = ""; 896 let additional = "";
881 if (attrRules) { 897 if (attrRules)
898 {
882 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); 899 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g);
883 for (let rule of attrRules) { 900 for (let rule of attrRules)
901 {
884 rule = rule.substr(1, rule.length - 2); 902 rule = rule.substr(1, rule.length - 2);
885 let separatorPos = rule.indexOf("="); 903 let separatorPos = rule.indexOf("=");
886 if (separatorPos > 0) { 904 if (separatorPos > 0)
905 {
887 rule = rule.replace(/=/, '="') + '"'; 906 rule = rule.replace(/=/, '="') + '"';
888 additional += "[" + rule + "]"; 907 additional += "[" + rule + "]";
889 } 908 }
890 else { 909 else
910 {
891 if (id) 911 if (id)
892 {
893 let {Utils} = require("utils");
894 return new InvalidFilter(text, Utils.getString("filter_elemhide_dupl icate_id")); 912 return new InvalidFilter(text, Utils.getString("filter_elemhide_dupl icate_id"));
895 } 913
896 else 914 id = rule;
897 id = rule;
898 } 915 }
899 } 916 }
900 } 917 }
901 918
902 if (id) 919 if (id)
903 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad ditional; 920 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad ditional;
904 else if (tagName || additional) 921 else if (tagName || additional)
905 selector = tagName + additional; 922 selector = tagName + additional;
906 else 923 else
907 {
908 let {Utils} = require("utils");
909 return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria ")); 924 return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria "));
910 } 925 }
911 } 926
912 if (isException) 927 if (isException)
913 return new ElemHideException(text, domain, selector); 928 return new ElemHideException(text, domain, selector);
914 else 929
915 return new ElemHideFilter(text, domain, selector); 930 let match = Filter.csspropertyRegExp.exec(selector);
931 if (match)
932 {
933 // CSS property filters are inefficient so we need to make sure that
934 // they're only applied if they specify active domains
935 if (!/,[^~][^,.]*\.[^,]/.test("," + domain))
936 return new InvalidFilter(text, Utils.getString("filter_cssproperty_nodomai n"));
937
938 return new CSSPropertyFilter(text, domain, selector, match[2],
939 selector.substr(0, match.index),
940 selector.substr(match.index + match[0].length));
941 }
942
943 return new ElemHideFilter(text, domain, selector);
916 }; 944 };
917 945
918 /** 946 /**
919 * Class for element hiding filters 947 * Class for element hiding filters
920 * @param {String} text see Filter() 948 * @param {String} text see Filter()
921 * @param {String} domains see ElemHideBase() 949 * @param {String} domains see ElemHideBase()
922 * @param {String} selector see ElemHideBase() 950 * @param {String} selector see ElemHideBase()
923 * @constructor 951 * @constructor
924 * @augments ElemHideBase 952 * @augments ElemHideBase
925 */ 953 */
(...skipping 19 matching lines...) Expand all
945 function ElemHideException(text, domains, selector) 973 function ElemHideException(text, domains, selector)
946 { 974 {
947 ElemHideBase.call(this, text, domains, selector); 975 ElemHideBase.call(this, text, domains, selector);
948 } 976 }
949 exports.ElemHideException = ElemHideException; 977 exports.ElemHideException = ElemHideException;
950 978
951 ElemHideException.prototype = 979 ElemHideException.prototype =
952 { 980 {
953 __proto__: ElemHideBase.prototype 981 __proto__: ElemHideBase.prototype
954 }; 982 };
983
984 /**
985 * Class for CSS property filters
986 * @param {String} text see Filter()
987 * @param {String} domains see ElemHideBase()
988 * @param {String} selector see ElemHideBase()
989 * @param {String} regexpSource see CSSPropertyFilter.regexpSource
990 * @param {String} selectorPrefix see CSSPropertyFilter.selectorPrefix
991 * @param {String} selectorSuffix see CSSPropertyFilter.selectorSuffix
992 * @constructor
993 * @augments ElemHideBase
994 */
995 function CSSPropertyFilter(text, domains, selector, regexpSource,
996 selectorPrefix, selectorSuffix)
997 {
998 ElemHideBase.call(this, text, domains, selector);
999
1000 this.regexpSource = regexpSource;
1001 this.selectorPrefix = selectorPrefix;
1002 this.selectorSuffix = selectorSuffix;
1003 }
1004 exports.CSSPropertyFilter = CSSPropertyFilter;
1005
1006 CSSPropertyFilter.prototype =
1007 {
1008 __proto__: ElemHideBase.prototype,
1009
1010 /**
1011 * Expression from which a regular expression should be generated for matching
1012 * CSS properties - for delayed creation of the regexpString property
1013 * @type String
1014 */
1015 regexpSource: null,
1016 /**
1017 * Substring of CSS selector before properties for the HTML elements that
1018 * should be hidden
1019 * @type String
1020 */
1021 selectorPrefix: null,
1022 /**
1023 * Substring of CSS selector after properties for the HTML elements that
1024 * should be hidden
1025 * @type String
1026 */
1027 selectorSuffix: null,
1028
1029 /**
1030 * Raw regular expression string to be used when testing CSS properties
1031 * against this filter
1032 * @type String
1033 */
1034 get regexpString()
1035 {
1036 // Despite this property being cached, the getter is called
1037 // several times on Safari, due to WebKit bug 132872
1038 let prop = Object.getOwnPropertyDescriptor(this, "regexpString");
1039 if (prop)
1040 return prop.value;
1041
1042 let regexp = Filter.toRegExp(this.regexpSource);
1043 Object.defineProperty(this, "regexpString", {value: regexp});
1044 return regexp;
1045 }
1046 };
LEFTRIGHT

Powered by Google App Engine
This is Rietveld