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

Side by Side Diff: lib/filterClasses.js

Issue 5730585574113280: Issue 2390 - Created filter class for CSS property filters (Closed)
Patch Set: Created May 4, 2015, 6:05 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/locale/en-US/global.properties ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 * @type RegExp
92 */
93 Filter.csspropertyRegExp = /\[\-abp\-properties=(["'])([^"']+)\1\]/;
Sebastian Noack 2015/05/05 15:47:02 So we don't allow quotes in the value here. Should
Thomas Greiner 2015/05/06 12:08:32 Done, I added a comment as suggested. We could cha
Thomas Greiner 2015/05/06 15:33:07 Yes, there's currently no negative lookbehind in J
Sebastian Noack 2015/05/06 15:42:29 Yeah, let's stick to not allowing any quotes for n
88 94
89 /** 95 /**
90 * Creates a filter of correct type from its text representation - does the basi c parsing and 96 * Creates a filter of correct type from its text representation - does the basi c parsing and
91 * calls the right constructor then. 97 * calls the right constructor then.
92 * 98 *
93 * @param {String} text as in Filter() 99 * @param {String} text as in Filter()
94 * @return {Filter} 100 * @return {Filter}
95 */ 101 */
96 Filter.fromText = function(text) 102 Filter.fromText = function(text)
97 { 103 {
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 { 159 {
154 // Special treatment for element hiding filters, right side is allowed to co ntain spaces 160 // Special treatment for element hiding filters, right side is allowed to co ntain spaces
155 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text); 161 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text);
156 return domain.replace(/\s/g, "") + separator + selector.trim(); 162 return domain.replace(/\s/g, "") + separator + selector.trim();
157 } 163 }
158 else 164 else
159 return text.replace(/\s/g, ""); 165 return text.replace(/\s/g, "");
160 }; 166 };
161 167
162 /** 168 /**
169 * Converts filter text into regular expression string
170 * @param {String} text as in Filter()
171 * @return {String} regular expression representation of filter text
172 */
173 Filter.toRegExp = function(text)
174 {
175 return text
176 .replace(/\*+/g, "*") // remove multiple wildcards
177 .replace(/\^\|$/, "^") // remove anchors following separator placehold er
178 .replace(/\W/g, "\\$&") // escape special symbols
179 .replace(/\\\*/g, ".*") // replace wildcards by .*
180 // process separator placeholders (all ANSI characters but alphanumeric char acters and _%.-)
181 .replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x6 0\\x7B-\\x7F]|$)")
182 .replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") // process ex tended anchor at expression start
183 .replace(/^\\\|/, "^") // process anchor at expression start
184 .replace(/\\\|$/, "$") // process anchor at expression end
185 .replace(/^(\.\*)/, "") // remove leading wildcards
186 .replace(/(\.\*)$/, ""); // remove trailing wildcards
187 }
188
189 /**
163 * Class for invalid filters 190 * Class for invalid filters
164 * @param {String} text see Filter() 191 * @param {String} text see Filter()
165 * @param {String} reason Reason why this filter is invalid 192 * @param {String} reason Reason why this filter is invalid
166 * @constructor 193 * @constructor
167 * @augments Filter 194 * @augments Filter
168 */ 195 */
169 function InvalidFilter(text, reason) 196 function InvalidFilter(text, reason)
170 { 197 {
171 Filter.call(this, text); 198 Filter.call(this, text);
172 199
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
536 * @type RegExp 563 * @type RegExp
537 */ 564 */
538 get regexp() 565 get regexp()
539 { 566 {
540 // Despite this property being cached, the getter is called 567 // Despite this property being cached, the getter is called
541 // several times on Safari, due to WebKit bug 132872 568 // several times on Safari, due to WebKit bug 132872
542 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); 569 let prop = Object.getOwnPropertyDescriptor(this, "regexp");
543 if (prop) 570 if (prop)
544 return prop.value; 571 return prop.value;
545 572
546 // Remove multiple wildcards 573 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"); 574 let regexp = new RegExp(source, this.matchCase ? "" : "i");
561 Object.defineProperty(this, "regexp", {value: regexp}); 575 Object.defineProperty(this, "regexp", {value: regexp});
562 return regexp; 576 return regexp;
563 }, 577 },
564 /** 578 /**
565 * Content types the filter applies to, combination of values from RegExpFilte r.typeMap 579 * Content types the filter applies to, combination of values from RegExpFilte r.typeMap
566 * @type Number 580 * @type Number
567 */ 581 */
568 contentType: 0x7FFFFFFF, 582 contentType: 0x7FFFFFFF,
569 /** 583 /**
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after
854 }; 868 };
855 869
856 /** 870 /**
857 * Creates an element hiding filter from a pre-parsed text representation 871 * Creates an element hiding filter from a pre-parsed text representation
858 * 872 *
859 * @param {String} text same as in Filter() 873 * @param {String} text same as in Filter()
860 * @param {String} domain domain part of the text representation (can be emp ty) 874 * @param {String} domain domain part of the text representation (can be emp ty)
861 * @param {String} tagName tag name part (can be empty) 875 * @param {String} tagName tag name part (can be empty)
862 * @param {String} attrRules attribute matching rules (can be empty) 876 * @param {String} attrRules attribute matching rules (can be empty)
863 * @param {String} selector raw CSS selector (can be empty) 877 * @param {String} selector raw CSS selector (can be empty)
864 * @return {ElemHideFilter|ElemHideException|InvalidFilter} 878 * @return {ElemHideFilter|ElemHideException|CSSPropertyFilter|InvalidFilter}
865 */ 879 */
866 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector) 880 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector)
867 { 881 {
868 if (!selector) 882 if (!selector)
869 { 883 {
870 if (tagName == "*") 884 if (tagName == "*")
871 tagName = ""; 885 tagName = "";
872 886
873 let id = null; 887 let id = null;
874 let additional = ""; 888 let additional = "";
875 if (attrRules) { 889 if (attrRules) {
876 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); 890 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g);
877 for (let rule of attrRules) { 891 for (let rule of attrRules) {
878 rule = rule.substr(1, rule.length - 2); 892 rule = rule.substr(1, rule.length - 2);
879 let separatorPos = rule.indexOf("="); 893 let separatorPos = rule.indexOf("=");
880 if (separatorPos > 0) { 894 if (separatorPos > 0) {
881 rule = rule.replace(/=/, '="') + '"'; 895 rule = rule.replace(/=/, '="') + '"';
882 additional += "[" + rule + "]"; 896 additional += "[" + rule + "]";
883 } 897 }
884 else { 898 else {
885 if (id) 899 if (id)
886 {
887 let {Utils} = require("utils");
888 return new InvalidFilter(text, Utils.getString("filter_elemhide_dupl icate_id")); 900 return new InvalidFilter(text, Utils.getString("filter_elemhide_dupl icate_id"));
889 }
890 else 901 else
Sebastian Noack 2015/05/05 15:47:02 Nit: Redundant else statement.
Thomas Greiner 2015/05/06 12:08:32 Done.
891 id = rule; 902 id = rule;
892 } 903 }
893 } 904 }
894 } 905 }
895 906
896 if (id) 907 if (id)
897 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad ditional; 908 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad ditional;
898 else if (tagName || additional) 909 else if (tagName || additional)
899 selector = tagName + additional; 910 selector = tagName + additional;
900 else 911 else
901 {
902 let {Utils} = require("utils");
903 return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria ")); 912 return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria "));
904 }
905 } 913 }
906 if (isException) 914 if (isException)
907 return new ElemHideException(text, domain, selector); 915 return new ElemHideException(text, domain, selector);
908 else 916 else
Sebastian Noack 2015/05/05 15:47:02 Nit: Redundant else statement.
Thomas Greiner 2015/05/06 12:08:32 Done.
909 return new ElemHideFilter(text, domain, selector); 917 {
918 if (Filter.csspropertyRegExp.test(selector))
919 {
920 // CSS property filters are inefficient so we need to make sure that
921 // they're only applied if they specify active domains
922 if (/,[^~]/.test("," + domain))
Sebastian Noack 2015/05/05 15:47:02 I wonder whether we should also look for at least
Thomas Greiner 2015/05/06 12:08:32 Good point but note that this still doesn't cover
Sebastian Noack 2015/05/06 13:07:48 Hmm, if we want to do it properly, I suppose we ha
923 return new CSSPropertyFilter(text, domain, selector);
924 else
Sebastian Noack 2015/05/05 15:47:02 Nit: Redundant else statement.
925 return new InvalidFilter(text, Utils.getString("filter_cssproperty_nodom ain"));
926 }
927 else
Sebastian Noack 2015/05/05 15:47:02 Nit: Redundant else statement.
Thomas Greiner 2015/05/06 12:08:32 Done.
928 return new ElemHideFilter(text, domain, selector);
929 }
910 }; 930 };
911 931
912 /** 932 /**
913 * Class for element hiding filters 933 * Class for element hiding filters
914 * @param {String} text see Filter() 934 * @param {String} text see Filter()
915 * @param {String} domains see ElemHideBase() 935 * @param {String} domains see ElemHideBase()
916 * @param {String} selector see ElemHideBase() 936 * @param {String} selector see ElemHideBase()
917 * @constructor 937 * @constructor
918 * @augments ElemHideBase 938 * @augments ElemHideBase
919 */ 939 */
(...skipping 19 matching lines...) Expand all
939 function ElemHideException(text, domains, selector) 959 function ElemHideException(text, domains, selector)
940 { 960 {
941 ElemHideBase.call(this, text, domains, selector); 961 ElemHideBase.call(this, text, domains, selector);
942 } 962 }
943 exports.ElemHideException = ElemHideException; 963 exports.ElemHideException = ElemHideException;
944 964
945 ElemHideException.prototype = 965 ElemHideException.prototype =
946 { 966 {
947 __proto__: ElemHideBase.prototype 967 __proto__: ElemHideBase.prototype
948 }; 968 };
969
970 /**
971 * Class for CSS property filters
972 * @param {String} text see Filter()
973 * @param {String} domains see ElemHideBase()
974 * @param {String} selector see ElemHideBase()
975 * @constructor
976 * @augments ElemHideBase
977 */
978 function CSSPropertyFilter(text, domains, selector)
979 {
980 ElemHideBase.call(this, text, domains, selector);
981
982 let properties;
983 [properties, , this.regexpSource] = selector.match(Filter.csspropertyRegExp);
984 [this.selectorPrefix, this.selectorSuffix] = selector.split(properties);
985 }
986 exports.CSSPropertyFilter = CSSPropertyFilter;
987
988 CSSPropertyFilter.prototype =
989 {
990 __proto__: ElemHideBase.prototype,
991
992 /**
993 * Expression from which a regular expression should be generated for matching
994 * CSS properties - for delayed creation of the regexp property
995 * @type String
996 */
997 regexpSource: null,
998 /**
999 * Substring of CSS selector before properties for the HTML elements that
1000 * should be hidden
1001 * @type String
1002 */
1003 selectorPrefix: null,
1004 /**
1005 * Substring of CSS selector after properties for the HTML elements that
1006 * should be hidden
1007 * @type String
1008 */
1009 selectorSuffix: null,
1010
1011 /**
1012 * Regular expression to be used when testing CSS properties against
1013 * this filter
1014 * @type RegExp
1015 */
1016 get regexp()
Sebastian Noack 2015/05/05 15:47:02 Hmm, this is almost the same code we already have
Thomas Greiner 2015/05/06 12:08:32 You're right, the issue description doesn't explic
Sebastian Noack 2015/05/06 13:07:48 Well, objects get serialized when passed to conten
1017 {
1018 // Despite this property being cached, the getter is called
1019 // several times on Safari, due to WebKit bug 132872
1020 let prop = Object.getOwnPropertyDescriptor(this, "regexp");
1021 if (prop)
1022 return prop.value;
1023
1024 let source = Filter.toRegExp(this.regexpSource);
1025 let regexp = new RegExp(source, "");
1026 Object.defineProperty(this, "regexp", {value: regexp});
1027 return regexp;
1028 }
1029 };
OLDNEW
« no previous file with comments | « chrome/locale/en-US/global.properties ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld