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: Addressed missing nits Created May 6, 2015, 4: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 * 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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 };
161 168
162 /** 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 }
189
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 {
171 Filter.call(this, text); 199 Filter.call(this, text);
172 200
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
536 * @type RegExp 564 * @type RegExp
537 */ 565 */
538 get regexp() 566 get regexp()
539 { 567 {
540 // Despite this property being cached, the getter is called 568 // Despite this property being cached, the getter is called
541 // several times on Safari, due to WebKit bug 132872 569 // several times on Safari, due to WebKit bug 132872
542 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); 570 let prop = Object.getOwnPropertyDescriptor(this, "regexp");
543 if (prop) 571 if (prop)
544 return prop.value; 572 return prop.value;
545 573
546 // Remove multiple wildcards 574 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"); 575 let regexp = new RegExp(source, this.matchCase ? "" : "i");
561 Object.defineProperty(this, "regexp", {value: regexp}); 576 Object.defineProperty(this, "regexp", {value: regexp});
562 return regexp; 577 return regexp;
563 }, 578 },
564 /** 579 /**
565 * Content types the filter applies to, combination of values from RegExpFilte r.typeMap 580 * Content types the filter applies to, combination of values from RegExpFilte r.typeMap
566 * @type Number 581 * @type Number
567 */ 582 */
568 contentType: 0x7FFFFFFF, 583 contentType: 0x7FFFFFFF,
569 /** 584 /**
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after
854 }; 869 };
855 870
856 /** 871 /**
857 * Creates an element hiding filter from a pre-parsed text representation 872 * Creates an element hiding filter from a pre-parsed text representation
858 * 873 *
859 * @param {String} text same as in Filter() 874 * @param {String} text same as in Filter()
860 * @param {String} domain domain part of the text representation (can be emp ty) 875 * @param {String} domain domain part of the text representation (can be emp ty)
861 * @param {String} tagName tag name part (can be empty) 876 * @param {String} tagName tag name part (can be empty)
862 * @param {String} attrRules attribute matching rules (can be empty) 877 * @param {String} attrRules attribute matching rules (can be empty)
863 * @param {String} selector raw CSS selector (can be empty) 878 * @param {String} selector raw CSS selector (can be empty)
864 * @return {ElemHideFilter|ElemHideException|InvalidFilter} 879 * @return {ElemHideFilter|ElemHideException|CSSPropertyFilter|InvalidFilter}
865 */ 880 */
866 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector) 881 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector)
867 { 882 {
868 if (!selector) 883 if (!selector)
869 { 884 {
870 if (tagName == "*") 885 if (tagName == "*")
871 tagName = ""; 886 tagName = "";
872 887
873 let id = null; 888 let id = null;
874 let additional = ""; 889 let additional = "";
875 if (attrRules) { 890 if (attrRules)
891 {
876 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); 892 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g);
877 for (let rule of attrRules) { 893 for (let rule of attrRules)
894 {
878 rule = rule.substr(1, rule.length - 2); 895 rule = rule.substr(1, rule.length - 2);
879 let separatorPos = rule.indexOf("="); 896 let separatorPos = rule.indexOf("=");
880 if (separatorPos > 0) { 897 if (separatorPos > 0)
898 {
881 rule = rule.replace(/=/, '="') + '"'; 899 rule = rule.replace(/=/, '="') + '"';
882 additional += "[" + rule + "]"; 900 additional += "[" + rule + "]";
883 } 901 }
884 else { 902 else
903 {
885 if (id) 904 if (id)
886 {
887 let {Utils} = require("utils");
888 return new InvalidFilter(text, Utils.getString("filter_elemhide_dupl icate_id")); 905 return new InvalidFilter(text, Utils.getString("filter_elemhide_dupl icate_id"));
889 } 906
890 else 907 id = rule;
891 id = rule;
892 } 908 }
893 } 909 }
894 } 910 }
895 911
896 if (id) 912 if (id)
897 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad ditional; 913 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad ditional;
898 else if (tagName || additional) 914 else if (tagName || additional)
899 selector = tagName + additional; 915 selector = tagName + additional;
900 else 916 else
901 {
902 let {Utils} = require("utils");
903 return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria ")); 917 return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria "));
904 }
905 } 918 }
919
906 if (isException) 920 if (isException)
907 return new ElemHideException(text, domain, selector); 921 return new ElemHideException(text, domain, selector);
908 else 922
909 return new ElemHideFilter(text, domain, selector); 923 if (Filter.csspropertyRegExp.test(selector))
Wladimir Palant 2015/06/05 22:08:59 How about actually storing the result of this sele
Thomas Greiner 2015/06/08 14:33:43 Done. I don't really mind either approach. Note t
924 {
925 // CSS property filters are inefficient so we need to make sure that
926 // they're only applied if they specify active domains
927 if (!/,[^~][^,.]*\.[^,]/.test("," + domain))
928 return new InvalidFilter(text, Utils.getString("filter_cssproperty_nodomai n"));
929
930 return new CSSPropertyFilter(text, domain, selector);
931 }
932
933 return new ElemHideFilter(text, domain, selector);
910 }; 934 };
911 935
912 /** 936 /**
913 * Class for element hiding filters 937 * Class for element hiding filters
914 * @param {String} text see Filter() 938 * @param {String} text see Filter()
915 * @param {String} domains see ElemHideBase() 939 * @param {String} domains see ElemHideBase()
916 * @param {String} selector see ElemHideBase() 940 * @param {String} selector see ElemHideBase()
917 * @constructor 941 * @constructor
918 * @augments ElemHideBase 942 * @augments ElemHideBase
919 */ 943 */
(...skipping 19 matching lines...) Expand all
939 function ElemHideException(text, domains, selector) 963 function ElemHideException(text, domains, selector)
940 { 964 {
941 ElemHideBase.call(this, text, domains, selector); 965 ElemHideBase.call(this, text, domains, selector);
942 } 966 }
943 exports.ElemHideException = ElemHideException; 967 exports.ElemHideException = ElemHideException;
944 968
945 ElemHideException.prototype = 969 ElemHideException.prototype =
946 { 970 {
947 __proto__: ElemHideBase.prototype 971 __proto__: ElemHideBase.prototype
948 }; 972 };
973
974 /**
975 * Class for CSS property filters
976 * @param {String} text see Filter()
977 * @param {String} domains see ElemHideBase()
978 * @param {String} selector see ElemHideBase()
979 * @constructor
980 * @augments ElemHideBase
981 */
982 function CSSPropertyFilter(text, domains, selector)
983 {
984 ElemHideBase.call(this, text, domains, selector);
985
986 let properties;
987 [properties, , this.regexpSource] = selector.match(Filter.csspropertyRegExp);
988 [this.selectorPrefix, this.selectorSuffix] = selector.split(properties);
989 }
990 exports.CSSPropertyFilter = CSSPropertyFilter;
991
992 CSSPropertyFilter.prototype =
993 {
994 __proto__: ElemHideBase.prototype,
995
996 /**
997 * Expression from which a regular expression should be generated for matching
998 * CSS properties - for delayed creation of the regexpString property
999 * @type String
1000 */
1001 regexpSource: null,
1002 /**
1003 * Substring of CSS selector before properties for the HTML elements that
1004 * should be hidden
1005 * @type String
1006 */
1007 selectorPrefix: null,
1008 /**
1009 * Substring of CSS selector after properties for the HTML elements that
1010 * should be hidden
1011 * @type String
1012 */
1013 selectorSuffix: null,
1014
1015 /**
1016 * Raw regular expression string to be used when testing CSS properties
1017 * against this filter
1018 * @type String
1019 */
1020 get regexpString()
1021 {
1022 // Despite this property being cached, the getter is called
1023 // several times on Safari, due to WebKit bug 132872
1024 let prop = Object.getOwnPropertyDescriptor(this, "regexpString");
1025 if (prop)
1026 return prop.value;
1027
1028 let regexp = Filter.toRegExp(this.regexpSource);
1029 Object.defineProperty(this, "regexpString", {value: regexp});
1030 return regexp;
1031 }
1032 };
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