 Issue 29737558:
  Issue 6538, 6781 - Implement support for snippet filters  (Closed) 
  Base URL: https://hg.adblockplus.org/adblockpluscore/
    
  
    Issue 29737558:
  Issue 6538, 6781 - Implement support for snippet filters  (Closed) 
  Base URL: https://hg.adblockplus.org/adblockpluscore/| 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-present eyeo GmbH | 3 * Copyright (C) 2006-present 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 | 
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 } | 77 } | 
| 78 }; | 78 }; | 
| 79 | 79 | 
| 80 /** | 80 /** | 
| 81 * Cache for known filters, maps string representation to filter objects. | 81 * Cache for known filters, maps string representation to filter objects. | 
| 82 * @type {Map.<string,Filter>} | 82 * @type {Map.<string,Filter>} | 
| 83 */ | 83 */ | 
| 84 Filter.knownFilters = new Map(); | 84 Filter.knownFilters = new Map(); | 
| 85 | 85 | 
| 86 /** | 86 /** | 
| 87 * Regular expression that element hiding filters should match | 87 * Regular expression that code injection filters should match | 
| 88 * @type {RegExp} | 88 * @type {RegExp} | 
| 89 */ | 89 */ | 
| 90 Filter.elemhideRegExp = /^([^/*|@"!]*?)#([@?])?#(.+)$/; | 90 Filter.codeInjectionRegExp = /^([^/*|@"!]*?)#([@?$])?#(.+)$/; | 
| 91 /** | 91 /** | 
| 92 * Regular expression that RegExp filters specified as RegExps should match | 92 * Regular expression that RegExp filters specified as RegExps should match | 
| 93 * @type {RegExp} | 93 * @type {RegExp} | 
| 94 */ | 94 */ | 
| 95 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^, \s]+)?)*)?$/; | 95 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^, \s]+)?)*)?$/; | 
| 96 /** | 96 /** | 
| 97 * Regular expression that options on a RegExp filter should match | 97 * Regular expression that options on a RegExp filter should match | 
| 98 * @type {RegExp} | 98 * @type {RegExp} | 
| 99 */ | 99 */ | 
| 100 Filter.optionsRegExp = /\$(~?[\w-]+(?:=[^,]+)?(?:,~?[\w-]+(?:=[^,]+)?)*)$/; | 100 Filter.optionsRegExp = /\$(~?[\w-]+(?:=[^,]+)?(?:,~?[\w-]+(?:=[^,]+)?)*)$/; | 
| 101 /** | 101 /** | 
| 102 * Regular expression that matches an invalid Content Security Policy | 102 * Regular expression that matches an invalid Content Security Policy | 
| 103 * @type {RegExp} | 103 * @type {RegExp} | 
| 104 */ | 104 */ | 
| 105 Filter.invalidCSPRegExp = /(;|^) ?(base-uri|referrer|report-to|report-uri|upgrad e-insecure-requests)\b/i; | 105 Filter.invalidCSPRegExp = /(;|^) ?(base-uri|referrer|report-to|report-uri|upgrad e-insecure-requests)\b/i; | 
| 106 | 106 | 
| 107 /** | 107 /** | 
| 108 * Creates a filter of correct type from its text representation - does the | 108 * Creates a filter of correct type from its text representation - does the | 
| 109 * basic parsing and calls the right constructor then. | 109 * basic parsing and calls the right constructor then. | 
| 110 * | 110 * | 
| 111 * @param {string} text as in Filter() | 111 * @param {string} text as in Filter() | 
| 112 * @return {Filter} | 112 * @return {Filter} | 
| 113 */ | 113 */ | 
| 114 Filter.fromText = function(text) | 114 Filter.fromText = function(text) | 
| 115 { | 115 { | 
| 116 let filter = Filter.knownFilters.get(text); | 116 let filter = Filter.knownFilters.get(text); | 
| 117 if (filter) | 117 if (filter) | 
| 118 return filter; | 118 return filter; | 
| 119 | 119 | 
| 120 let match = (text.includes("#") ? Filter.elemhideRegExp.exec(text) : null); | 120 let match = text.includes("#") ? Filter.codeInjectionRegExp.exec(text) : null; | 
| 121 if (match) | 121 if (match) | 
| 122 { | 122 { | 
| 123 let propsMatch; | 123 let propsMatch; | 
| 124 if (!match[2] && | 124 if (!match[2] && | 
| 125 (propsMatch = /\[-abp-properties=(["'])([^"']+)\1\]/.exec(match[3]))) | 125 (propsMatch = /\[-abp-properties=(["'])([^"']+)\1\]/.exec(match[3]))) | 
| 126 { | 126 { | 
| 127 // This is legacy CSS properties syntax, convert to current syntax | 127 // This is legacy CSS properties syntax, convert to current syntax | 
| 128 let prefix = match[3].substr(0, propsMatch.index); | 128 let prefix = match[3].substr(0, propsMatch.index); | 
| 129 let expression = propsMatch[2]; | 129 let expression = propsMatch[2]; | 
| 130 let suffix = match[3].substr(propsMatch.index + propsMatch[0].length); | 130 let suffix = match[3].substr(propsMatch.index + propsMatch[0].length); | 
| 131 return Filter.fromText(`${match[1]}#?#` + | 131 return Filter.fromText(`${match[1]}#?#` + | 
| 132 `${prefix}:-abp-properties(${expression})${suffix}`); | 132 `${prefix}:-abp-properties(${expression})${suffix}`); | 
| 133 } | 133 } | 
| 134 | 134 | 
| 135 if (match[2] == "$") | |
| 136 return new SnippetFilter(text, match[1], match[3]); | |
| 
hub
2018/05/03 22:59:38
Here you shouldn't return, but rather assign it to
 
Manish Jethani
2018/05/07 19:03:27
Done.
 | |
| 137 | |
| 135 filter = ElemHideBase.fromText( | 138 filter = ElemHideBase.fromText( | 
| 136 text, match[1], match[2], match[3] | 139 text, match[1], match[2], match[3] | 
| 137 ); | 140 ); | 
| 138 } | 141 } | 
| 139 else if (text[0] == "!") | 142 else if (text[0] == "!") | 
| 140 filter = new CommentFilter(text); | 143 filter = new CommentFilter(text); | 
| 141 else | 144 else | 
| 142 filter = RegExpFilter.fromText(text); | 145 filter = RegExpFilter.fromText(text); | 
| 143 | 146 | 
| 144 Filter.knownFilters.set(filter.text, filter); | 147 Filter.knownFilters.set(filter.text, filter); | 
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 177 if (!text) | 180 if (!text) | 
| 178 return text; | 181 return text; | 
| 179 | 182 | 
| 180 // Remove line breaks, tabs etc | 183 // Remove line breaks, tabs etc | 
| 181 text = text.replace(/[^\S ]+/g, ""); | 184 text = text.replace(/[^\S ]+/g, ""); | 
| 182 | 185 | 
| 183 // Don't remove spaces inside comments | 186 // Don't remove spaces inside comments | 
| 184 if (/^ *!/.test(text)) | 187 if (/^ *!/.test(text)) | 
| 185 return text.trim(); | 188 return text.trim(); | 
| 186 | 189 | 
| 187 // Special treatment for element hiding filters, right side is allowed to | 190 // Special treatment for code injection filters, right side is allowed to | 
| 188 // contain spaces | 191 // contain spaces | 
| 189 if (Filter.elemhideRegExp.test(text)) | 192 if (Filter.codeInjectionRegExp.test(text)) | 
| 190 { | 193 { | 
| 191 let [, domain, separator, selector] = /^(.*?)(#[@?]?#?)(.*)$/.exec(text); | 194 let [, domain, separator, code] = /^(.*?)(#[@?$]?#?)(.*)$/.exec(text); | 
| 192 return domain.replace(/ +/g, "") + separator + selector.trim(); | 195 return domain.replace(/ +/g, "") + separator + code.trim(); | 
| 193 } | 196 } | 
| 194 | 197 | 
| 195 // For most regexp filters we strip all spaces, but $csp filter options | 198 // For most regexp filters we strip all spaces, but $csp filter options | 
| 196 // are allowed to contain single (non trailing) spaces. | 199 // are allowed to contain single (non trailing) spaces. | 
| 197 let strippedText = text.replace(/ +/g, ""); | 200 let strippedText = text.replace(/ +/g, ""); | 
| 198 if (!strippedText.includes("$") || !/\bcsp=/i.test(strippedText)) | 201 if (!strippedText.includes("$") || !/\bcsp=/i.test(strippedText)) | 
| 199 return strippedText; | 202 return strippedText; | 
| 200 | 203 | 
| 201 let optionsMatch = Filter.optionsRegExp.exec(strippedText); | 204 let optionsMatch = Filter.optionsRegExp.exec(strippedText); | 
| 202 if (!optionsMatch) | 205 if (!optionsMatch) | 
| (...skipping 740 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 943 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, | 946 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, | 
| 944 thirdParty, sitekeys); | 947 thirdParty, sitekeys); | 
| 945 } | 948 } | 
| 946 exports.WhitelistFilter = WhitelistFilter; | 949 exports.WhitelistFilter = WhitelistFilter; | 
| 947 | 950 | 
| 948 WhitelistFilter.prototype = extend(RegExpFilter, { | 951 WhitelistFilter.prototype = extend(RegExpFilter, { | 
| 949 type: "whitelist" | 952 type: "whitelist" | 
| 950 }); | 953 }); | 
| 951 | 954 | 
| 952 /** | 955 /** | 
| 953 * Base class for element hiding filters | 956 * Base class for code injection filters | 
| 954 * @param {string} text see Filter() | 957 * @param {string} text see Filter() | 
| 955 * @param {string} [domains] Host names or domains the filter should be | 958 * @param {string} [domains] Host names or domains the filter should be | 
| 956 * restricted to | 959 * restricted to | 
| 957 * @param {string} selector CSS selector for the HTML elements that should be | 960 * @param {string} code Code that should be injected | 
| 958 * hidden | |
| 959 * @constructor | 961 * @constructor | 
| 960 * @augments ActiveFilter | 962 * @augments ActiveFilter | 
| 961 */ | 963 */ | 
| 962 function ElemHideBase(text, domains, selector) | 964 function CodeInjectionFilter(text, domains, code) | 
| 963 { | 965 { | 
| 964 ActiveFilter.call(this, text, domains || null); | 966 ActiveFilter.call(this, text, domains || null); | 
| 965 | 967 | 
| 966 if (domains) | 968 if (domains) | 
| 967 { | 969 { | 
| 968 this.selectorDomain = domains.replace(/,~[^,]+/g, "") | 970 this.injectionDomain = domains.replace(/,~[^,]+/g, "") | 
| 969 .replace(/^~[^,]+,?/, "").toLowerCase(); | 971 .replace(/^~[^,]+,?/, "").toLowerCase(); | 
| 970 } | 972 } | 
| 971 | 973 | 
| 972 // Braces are being escaped to prevent CSS rule injection. | 974 this.code = code; | 
| 973 this.selector = selector.replace("{", "\\7B ").replace("}", "\\7D "); | |
| 974 } | 975 } | 
| 975 exports.ElemHideBase = ElemHideBase; | 976 exports.CodeInjectionFilter = CodeInjectionFilter; | 
| 976 | 977 | 
| 977 ElemHideBase.prototype = extend(ActiveFilter, { | 978 CodeInjectionFilter.prototype = extend(ActiveFilter, { | 
| 978 /** | 979 /** | 
| 979 * @see ActiveFilter.domainSeparator | 980 * @see ActiveFilter.domainSeparator | 
| 980 */ | 981 */ | 
| 981 domainSeparator: ",", | 982 domainSeparator: ",", | 
| 982 | 983 | 
| 983 /** | 984 /** | 
| 984 * @see ActiveFilter.ignoreTrailingDot | 985 * @see ActiveFilter.ignoreTrailingDot | 
| 985 */ | 986 */ | 
| 986 ignoreTrailingDot: false, | 987 ignoreTrailingDot: false, | 
| 987 | 988 | 
| 988 /** | 989 /** | 
| 989 * Host name or domain the filter should be restricted to (can be null for | 990 * Host name or domain the filter should be restricted to (can be null for | 
| 990 * no restriction) | 991 * no restriction) | 
| 991 * @type {string} | 992 * @type {string} | 
| 992 */ | 993 */ | 
| 993 selectorDomain: null, | 994 injectionDomain: null, | 
| 995 | |
| 994 /** | 996 /** | 
| 995 * CSS selector for the HTML elements that should be hidden | 997 * Code that should be injected | 
| 996 * @type {string} | 998 * @type {string} | 
| 997 */ | 999 */ | 
| 998 selector: null | 1000 code: null | 
| 999 }); | 1001 }); | 
| 1000 | 1002 | 
| 1001 /** | 1003 /** | 
| 1004 * Base class for element hiding filters | |
| 1005 * @param {string} text see Filter() | |
| 1006 * @param {string} [domains] see CodeInjectionFilter() | |
| 1007 * @param {string} selector CSS selector for the HTML elements that should be | |
| 1008 * hidden | |
| 1009 * @constructor | |
| 1010 * @augments CodeInjectionFilter | |
| 1011 */ | |
| 1012 function ElemHideBase(text, domains, selector) | |
| 1013 { | |
| 1014 CodeInjectionFilter.call(this, text, domains, selector); | |
| 1015 | |
| 1016 // Braces are being escaped to prevent CSS rule injection. | |
| 1017 this.code = this.code.replace("{", "\\7B ").replace("}", "\\7D "); | |
| 1018 } | |
| 1019 exports.ElemHideBase = ElemHideBase; | |
| 1020 | |
| 1021 ElemHideBase.prototype = extend(CodeInjectionFilter, {}); | |
| 1022 | |
| 1023 /** | |
| 1002 * Creates an element hiding filter from a pre-parsed text representation | 1024 * Creates an element hiding filter from a pre-parsed text representation | 
| 1003 * | 1025 * | 
| 1004 * @param {string} text same as in Filter() | 1026 * @param {string} text same as in Filter() | 
| 1005 * @param {string?} domain | 1027 * @param {string?} domain | 
| 1006 * domain part of the text representation | 1028 * domain part of the text representation | 
| 1007 * @param {string?} type | 1029 * @param {string?} type | 
| 1008 * rule type, either empty or @ (exception) or ? (emulation rule) | 1030 * rule type, either empty or @ (exception) or ? (emulation rule) | 
| 1009 * @param {string} selector raw CSS selector | 1031 * @param {string} selector raw CSS selector | 
| 1010 * @return {ElemHideFilter|ElemHideException| | 1032 * @return {ElemHideFilter|ElemHideException| | 
| 1011 * ElemHideEmulationFilter|InvalidFilter} | 1033 * ElemHideEmulationFilter|InvalidFilter} | 
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1080 */ | 1102 */ | 
| 1081 function ElemHideEmulationFilter(text, domains, selector) | 1103 function ElemHideEmulationFilter(text, domains, selector) | 
| 1082 { | 1104 { | 
| 1083 ElemHideBase.call(this, text, domains, selector); | 1105 ElemHideBase.call(this, text, domains, selector); | 
| 1084 } | 1106 } | 
| 1085 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; | 1107 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; | 
| 1086 | 1108 | 
| 1087 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { | 1109 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { | 
| 1088 type: "elemhideemulation" | 1110 type: "elemhideemulation" | 
| 1089 }); | 1111 }); | 
| 1112 | |
| 1113 /** | |
| 1114 * Class for snippet filters | |
| 1115 * @param {string} text see Filter() | |
| 1116 * @param {string} [domains] see CodeInjectionFilter() | |
| 1117 * @param {string} script Script that should be executed | |
| 1118 * @constructor | |
| 1119 * @augments CodeInjectionFilter | |
| 1120 */ | |
| 1121 function SnippetFilter(text, domains, script) | |
| 1122 { | |
| 1123 CodeInjectionFilter.call(this, text, domains, script); | |
| 1124 } | |
| 1125 exports.SnippetFilter = SnippetFilter; | |
| 1126 | |
| 1127 SnippetFilter.prototype = extend(CodeInjectionFilter, { | |
| 1128 type: "snippet" | |
| 1129 }); | |
| OLD | NEW |