 Issue 5730585574113280:
  Issue 2390 - Created filter class for CSS property filters  (Closed)
    
  
    Issue 5730585574113280:
  Issue 2390 - Created filter class for CSS property filters  (Closed) 
  | Left: | ||
| Right: | 
| OLD | NEW | 
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 }; | |
| OLD | NEW |