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

Side by Side Diff: lib/filterClasses.js

Issue 29361668: Issue 4394 - Create a filter class for element hiding emulation filters (Closed) Base URL: https://bitbucket.org/fhd/adblockpluscore
Patch Set: Created Nov. 3, 2016, 3:42 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
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-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 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 {extend} = require("coreUtils"); 23 let {extend} = require("coreUtils");
24 let {elemHideEmulationFeatureMap, filterToRegExp} = require("shared");
24 25
25 /** 26 /**
26 * Abstract base class for filters 27 * Abstract base class for filters
27 * 28 *
28 * @param {String} text string representation of the filter 29 * @param {String} text string representation of the filter
29 * @constructor 30 * @constructor
30 */ 31 */
31 function Filter(text) 32 function Filter(text)
32 { 33 {
33 this.text = text; 34 this.text = text;
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 /** 89 /**
89 * Regular expression that RegExp filters specified as RegExps should match 90 * Regular expression that RegExp filters specified as RegExps should match
90 * @type RegExp 91 * @type RegExp
91 */ 92 */
92 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[ ^,\s]+)?)*)?$/; 93 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[ ^,\s]+)?)*)?$/;
93 /** 94 /**
94 * Regular expression that options on a RegExp filter should match 95 * Regular expression that options on a RegExp filter should match
95 * @type RegExp 96 * @type RegExp
96 */ 97 */
97 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/ ; 98 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/ ;
98 /**
99 * Regular expression that CSS property filters should match
100 * Properties must not contain " or '
101 * @type RegExp
102 */
103 Filter.csspropertyRegExp = /\[\-abp\-properties=(["'])([^"']+)\1\]/;
104 99
105 /** 100 /**
106 * Creates a filter of correct type from its text representation - does the basi c parsing and 101 * Creates a filter of correct type from its text representation - does the basi c parsing and
107 * calls the right constructor then. 102 * calls the right constructor then.
108 * 103 *
109 * @param {String} text as in Filter() 104 * @param {String} text as in Filter()
110 * @return {Filter} 105 * @return {Filter}
111 */ 106 */
112 Filter.fromText = function(text) 107 Filter.fromText = function(text)
113 { 108 {
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
169 { 164 {
170 // Special treatment for element hiding filters, right side is allowed to co ntain spaces 165 // Special treatment for element hiding filters, right side is allowed to co ntain spaces
171 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text); 166 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text);
172 return domain.replace(/\s/g, "") + separator + selector.trim(); 167 return domain.replace(/\s/g, "") + separator + selector.trim();
173 } 168 }
174 else 169 else
175 return text.replace(/\s/g, ""); 170 return text.replace(/\s/g, "");
176 }; 171 };
177 172
178 /** 173 /**
179 * Converts filter text into regular expression string 174 * @see filterToRegExp
180 * @param {String} text as in Filter()
181 * @return {String} regular expression representation of filter text
182 */ 175 */
183 Filter.toRegExp = function(text) 176 Filter.toRegExp = filterToRegExp;
184 {
185 return text
186 .replace(/\*+/g, "*") // remove multiple wildcards
187 .replace(/\^\|$/, "^") // remove anchors following separator placehold er
188 .replace(/\W/g, "\\$&") // escape special symbols
189 .replace(/\\\*/g, ".*") // replace wildcards by .*
190 // process separator placeholders (all ANSI characters but alphanumeric char acters and _%.-)
191 .replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x6 0\\x7B-\\x7F]|$)")
192 .replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") // process ex tended anchor at expression start
193 .replace(/^\\\|/, "^") // process anchor at expression start
194 .replace(/\\\|$/, "$") // process anchor at expression end
195 .replace(/^(\.\*)/, "") // remove leading wildcards
196 .replace(/(\.\*)$/, ""); // remove trailing wildcards
197 }
198 177
199 /** 178 /**
200 * Class for invalid filters 179 * Class for invalid filters
201 * @param {String} text see Filter() 180 * @param {String} text see Filter()
202 * @param {String} reason Reason why this filter is invalid 181 * @param {String} reason Reason why this filter is invalid
203 * @constructor 182 * @constructor
204 * @augments Filter 183 * @augments Filter
205 */ 184 */
206 function InvalidFilter(text, reason) 185 function InvalidFilter(text, reason)
207 { 186 {
(...skipping 668 matching lines...) Expand 10 before | Expand all | Expand 10 after
876 855
877 /** 856 /**
878 * Creates an element hiding filter from a pre-parsed text representation 857 * Creates an element hiding filter from a pre-parsed text representation
879 * 858 *
880 * @param {String} text same as in Filter() 859 * @param {String} text same as in Filter()
881 * @param {String} domain domain part of the text representation (can be e mpty) 860 * @param {String} domain domain part of the text representation (can be e mpty)
882 * @param {Boolean} isException exception rule indicator 861 * @param {Boolean} isException exception rule indicator
883 * @param {String} tagName tag name part (can be empty) 862 * @param {String} tagName tag name part (can be empty)
884 * @param {String} attrRules attribute matching rules (can be empty) 863 * @param {String} attrRules attribute matching rules (can be empty)
885 * @param {String} selector raw CSS selector (can be empty) 864 * @param {String} selector raw CSS selector (can be empty)
886 * @return {ElemHideFilter|ElemHideException|CSSPropertyFilter|InvalidFilter} 865 * @return {ElemHideFilter|ElemHideException|ElemHideEmulationFilter|InvalidFilt er}
887 */ 866 */
888 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector) 867 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector)
889 { 868 {
890 if (!selector) 869 if (!selector)
891 { 870 {
892 if (tagName == "*") 871 if (tagName == "*")
893 tagName = ""; 872 tagName = "";
894 873
895 let id = null; 874 let id = null;
896 let additional = ""; 875 let additional = "";
(...skipping 29 matching lines...) Expand all
926 905
927 // We don't allow ElemHide filters which have any empty domains. 906 // We don't allow ElemHide filters which have any empty domains.
928 // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that 907 // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that
929 // changes this must be changed too. 908 // changes this must be changed too.
930 if (domain && /(^|,)~?(,|$)/.test(domain)) 909 if (domain && /(^|,)~?(,|$)/.test(domain))
931 return new InvalidFilter(text, "filter_invalid_domain"); 910 return new InvalidFilter(text, "filter_invalid_domain");
932 911
933 if (isException) 912 if (isException)
934 return new ElemHideException(text, domain, selector); 913 return new ElemHideException(text, domain, selector);
935 914
936 let match = Filter.csspropertyRegExp.exec(selector); 915 let emulatedFeatures = 0;
937 if (match) 916 if (selector.indexOf("[-abp-properties") != -1)
917 emulatedFeatures |= ElemHideEmulationFilter.featureMap.PROPERTY_SELECTOR;
918 if (selector.indexOf(":has(") != -1)
kzar 2016/11/04 15:45:56 The feature flags are a nice idea but I wonder wha
Felix Dahlke 2016/11/04 16:43:35 Well, if we detect NO feature here, this would jus
kzar 2016/11/07 12:36:26 I guess my question is what's the point of having
Felix Dahlke 2016/11/07 14:41:24 Well, we have to check for emulated features befor
kzar 2016/11/07 15:59:44 Well we don't need to set a boolean flag here eith
Felix Dahlke 2016/11/07 16:51:24 Yeah of course, my bad.
kzar 2016/11/07 17:10:22 That's not quite true, since if we're recording us
Felix Dahlke 2016/11/08 17:45:35 Well, sure, we don't have to run all checks if one
kzar 2016/11/15 16:54:13 Well no I think that if the only reason to add all
Felix Dahlke 2016/11/15 21:11:44 I still think it makes sense to start detecting (y
Wladimir Palant 2016/11/21 11:39:14 I actually have to agree with Dave here. You are d
Felix Dahlke 2016/11/21 14:38:58 Fair enough, I guess if we're not worried about ba
919 emulatedFeatures |= ElemHideEmulationFilter.featureMap.HAS_PSEUDO_CLASS;
920
921 if (emulatedFeatures != 0)
938 { 922 {
939 // CSS property filters are inefficient so we need to make sure that 923 // Element hiding emulation filters are inefficient so we need to make sure
940 // they're only applied if they specify active domains 924 // that they're only applied if they specify active domains
941 if (!/,[^~][^,.]*\.[^,]/.test("," + domain)) 925 if (!/,[^~][^,.]*\.[^,]/.test("," + domain))
942 return new InvalidFilter(text, "filter_cssproperty_nodomain"); 926 return new InvalidFilter(text, "filter_elemhideemulation_nodomain");
943 927
944 return new CSSPropertyFilter(text, domain, selector, match[2], 928 return new ElemHideEmulationFilter(text, domain, selector,
kzar 2016/11/04 15:45:56 I wonder if it's really a good idea to move the lo
Felix Dahlke 2016/11/04 16:43:35 Well, one part of the truth is that I don't know h
kzar 2016/11/07 17:19:25 That's a pretty good point actually, I didn't thin
945 selector.substr(0, match.index), 929 emulatedFeatures);
946 selector.substr(match.index + match[0].length));
947 } 930 }
948 931
949 return new ElemHideFilter(text, domain, selector); 932 return new ElemHideFilter(text, domain, selector);
950 }; 933 };
951 934
952 /** 935 /**
953 * Class for element hiding filters 936 * Class for element hiding filters
954 * @param {String} text see Filter() 937 * @param {String} text see Filter()
955 * @param {String} domains see ElemHideBase() 938 * @param {String} domains see ElemHideBase()
956 * @param {String} selector see ElemHideBase() 939 * @param {String} selector see ElemHideBase()
(...skipping 22 matching lines...) Expand all
979 { 962 {
980 ElemHideBase.call(this, text, domains, selector); 963 ElemHideBase.call(this, text, domains, selector);
981 } 964 }
982 exports.ElemHideException = ElemHideException; 965 exports.ElemHideException = ElemHideException;
983 966
984 ElemHideException.prototype = extend(ElemHideBase, { 967 ElemHideException.prototype = extend(ElemHideBase, {
985 type: "elemhideexception" 968 type: "elemhideexception"
986 }); 969 });
987 970
988 /** 971 /**
989 * Class for CSS property filters 972 * Class for element hiding emulation filters
990 * @param {String} text see Filter() 973 * @param {String} text see Filter()
991 * @param {String} domains see ElemHideBase() 974 * @param {String} domains see ElemHideBase()
992 * @param {String} selector see ElemHideBase() 975 * @param {String} selector see ElemHideBase()
993 * @param {String} regexpSource see CSSPropertyFilter.regexpSource 976 * @param {Integer} features see ElemHideEmulationFilter.features
994 * @param {String} selectorPrefix see CSSPropertyFilter.selectorPrefix
995 * @param {String} selectorSuffix see CSSPropertyFilter.selectorSuffix
996 * @constructor 977 * @constructor
997 * @augments ElemHideBase 978 * @augments ElemHideBase
998 */ 979 */
999 function CSSPropertyFilter(text, domains, selector, regexpSource, 980 function ElemHideEmulationFilter(text, domains, selector, features)
1000 selectorPrefix, selectorSuffix)
1001 { 981 {
1002 ElemHideBase.call(this, text, domains, selector); 982 ElemHideBase.call(this, text, domains, selector);
1003 983
1004 this.regexpSource = regexpSource; 984 this.features = features;
1005 this.selectorPrefix = selectorPrefix;
1006 this.selectorSuffix = selectorSuffix;
1007 } 985 }
1008 exports.CSSPropertyFilter = CSSPropertyFilter; 986 exports.ElemHideEmulationFilter = ElemHideEmulationFilter;
1009 987
1010 CSSPropertyFilter.prototype = extend(ElemHideBase, { 988 ElemHideEmulationFilter.prototype = extend(ElemHideBase, {
1011 type: "cssproperty", 989 type: "elemhideemulation",
1012 990
1013 /** 991 /**
1014 * Expression from which a regular expression should be generated for matching 992 * Features used in this filter, combination of values from
kzar 2016/11/04 15:45:56 Perhaps describe it as bit flags instead of combin
Felix Dahlke 2016/11/04 16:43:35 I thought I'd keep this in line with the doc comme
kzar 2016/11/07 17:19:25 Well bitmap works too. (I guess if you're worried
1015 * CSS properties - for delayed creation of the regexpString property 993 * ElemHideEmulationFilter.featureMap
1016 * @type String 994 * @type Integer
1017 */ 995 */
1018 regexpSource: null, 996 features: 0
1019 /** 997 });
1020 * Substring of CSS selector before properties for the HTML elements that
1021 * should be hidden
1022 * @type String
1023 */
1024 selectorPrefix: null,
1025 /**
1026 * Substring of CSS selector after properties for the HTML elements that
1027 * should be hidden
1028 * @type String
1029 */
1030 selectorSuffix: null,
1031 998
1032 /** 999 /**
1033 * Raw regular expression string to be used when testing CSS properties 1000 * @see elemHideEmulationFeatureMap
1034 * against this filter 1001 */
1035 * @type String 1002 ElemHideEmulationFilter.featureMap = elemHideEmulationFeatureMap;
1036 */
1037 get regexpString()
1038 {
1039 // Despite this property being cached, the getter is called
1040 // several times on Safari, due to WebKit bug 132872
1041 let prop = Object.getOwnPropertyDescriptor(this, "regexpString");
1042 if (prop)
1043 return prop.value;
1044
1045 let regexp = Filter.toRegExp(this.regexpSource);
1046 Object.defineProperty(this, "regexpString", {value: regexp});
1047 return regexp;
1048 }
1049 });
OLDNEW

Powered by Google App Engine
This is Rietveld