| Index: lib/filterClasses.js |
| =================================================================== |
| --- a/lib/filterClasses.js |
| +++ b/lib/filterClasses.js |
| @@ -79,20 +79,20 @@ |
| /** |
| * Cache for known filters, maps string representation to filter objects. |
| * @type {Map.<string,Filter>} |
| */ |
| Filter.knownFilters = new Map(); |
| /** |
| - * Regular expression that element hiding filters should match |
| + * Regular expression that code injection filters should match |
| * @type {RegExp} |
| */ |
| -Filter.elemhideRegExp = /^([^/*|@"!]*?)#([@?])?#(.+)$/; |
| +Filter.codeInjectionRegExp = /^([^/*|@"!]*?)#([@?$])?#(.+)$/; |
| /** |
| * Regular expression that RegExp filters specified as RegExps should match |
| * @type {RegExp} |
| */ |
| Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^,\s]+)?)*)?$/; |
| /** |
| * Regular expression that options on a RegExp filter should match |
| * @type {RegExp} |
| @@ -112,31 +112,34 @@ |
| * @return {Filter} |
| */ |
| Filter.fromText = function(text) |
| { |
| let filter = Filter.knownFilters.get(text); |
| if (filter) |
| return filter; |
| - let match = (text.includes("#") ? Filter.elemhideRegExp.exec(text) : null); |
| + let match = text.includes("#") ? Filter.codeInjectionRegExp.exec(text) : null; |
| if (match) |
| { |
| let propsMatch; |
| if (!match[2] && |
| (propsMatch = /\[-abp-properties=(["'])([^"']+)\1\]/.exec(match[3]))) |
| { |
| // This is legacy CSS properties syntax, convert to current syntax |
| let prefix = match[3].substr(0, propsMatch.index); |
| let expression = propsMatch[2]; |
| let suffix = match[3].substr(propsMatch.index + propsMatch[0].length); |
| return Filter.fromText(`${match[1]}#?#` + |
| `${prefix}:-abp-properties(${expression})${suffix}`); |
| } |
| + if (match[2] == "$") |
| + return new SnippetFilter(text, match[1], match[3]); |
| + |
| filter = ElemHideBase.fromText( |
| text, match[1], match[2], match[3] |
| ); |
| } |
| else if (text[0] == "!") |
| filter = new CommentFilter(text); |
| else |
| filter = RegExpFilter.fromText(text); |
| @@ -179,22 +182,22 @@ |
| // Remove line breaks, tabs etc |
| text = text.replace(/[^\S ]+/g, ""); |
| // Don't remove spaces inside comments |
| if (/^ *!/.test(text)) |
| return text.trim(); |
| - // Special treatment for element hiding filters, right side is allowed to |
| + // Special treatment for code injection filters, right side is allowed to |
| // contain spaces |
| - if (Filter.elemhideRegExp.test(text)) |
| + if (Filter.codeInjectionRegExp.test(text)) |
| { |
| - let [, domain, separator, selector] = /^(.*?)(#[@?]?#?)(.*)$/.exec(text); |
| - return domain.replace(/ +/g, "") + separator + selector.trim(); |
| + let [, domain, separator, code] = /^(.*?)(#[@?$]?#?)(.*)$/.exec(text); |
| + return domain.replace(/ +/g, "") + separator + code.trim(); |
| } |
| // For most regexp filters we strip all spaces, but $csp filter options |
| // are allowed to contain single (non trailing) spaces. |
| let strippedText = text.replace(/ +/g, ""); |
| if (!strippedText.includes("$") || !/\bcsp=/i.test(strippedText)) |
| return strippedText; |
| @@ -945,65 +948,84 @@ |
| } |
| exports.WhitelistFilter = WhitelistFilter; |
| WhitelistFilter.prototype = extend(RegExpFilter, { |
| type: "whitelist" |
| }); |
| /** |
| - * Base class for element hiding filters |
| + * Base class for code injection filters |
| * @param {string} text see Filter() |
| * @param {string} [domains] Host names or domains the filter should be |
| * restricted to |
| - * @param {string} selector CSS selector for the HTML elements that should be |
| - * hidden |
| + * @param {string} code Code that should be injected |
| * @constructor |
| * @augments ActiveFilter |
| */ |
| -function ElemHideBase(text, domains, selector) |
| +function CodeInjectionFilter(text, domains, code) |
| { |
| ActiveFilter.call(this, text, domains || null); |
| if (domains) |
| { |
| - this.selectorDomain = domains.replace(/,~[^,]+/g, "") |
| - .replace(/^~[^,]+,?/, "").toLowerCase(); |
| + this.injectionDomain = domains.replace(/,~[^,]+/g, "") |
| + .replace(/^~[^,]+,?/, "").toLowerCase(); |
| } |
| - // Braces are being escaped to prevent CSS rule injection. |
| - this.selector = selector.replace("{", "\\7B ").replace("}", "\\7D "); |
| + this.code = code; |
| } |
| -exports.ElemHideBase = ElemHideBase; |
| +exports.CodeInjectionFilter = CodeInjectionFilter; |
| -ElemHideBase.prototype = extend(ActiveFilter, { |
| +CodeInjectionFilter.prototype = extend(ActiveFilter, { |
| /** |
| * @see ActiveFilter.domainSeparator |
| */ |
| domainSeparator: ",", |
| /** |
| * @see ActiveFilter.ignoreTrailingDot |
| */ |
| ignoreTrailingDot: false, |
| /** |
| * Host name or domain the filter should be restricted to (can be null for |
| * no restriction) |
| * @type {string} |
| */ |
| - selectorDomain: null, |
| + injectionDomain: null, |
| + |
| /** |
| - * CSS selector for the HTML elements that should be hidden |
| + * Code that should be injected |
| * @type {string} |
| */ |
| - selector: null |
| + code: null |
| }); |
| /** |
| + * Base class for element hiding filters |
| + * @param {string} text see Filter() |
| + * @param {string} [domains] see CodeInjectionFilter() |
| + * @param {string} selector CSS selector for the HTML elements that should be |
| + * hidden |
| + * @constructor |
| + * @augments CodeInjectionFilter |
| + */ |
| +function ElemHideBase(text, domains, selector) |
| +{ |
| + CodeInjectionFilter.call(this, text, domains, selector); |
| + |
| + // Braces are being escaped to prevent CSS rule injection. |
| + this.code = this.code.replace("{", "\\7B ").replace("}", "\\7D "); |
| +} |
| +exports.ElemHideBase = ElemHideBase; |
| + |
| +ElemHideBase.prototype = extend(CodeInjectionFilter, {}); |
| + |
| +/** |
| * Creates an element hiding filter from a pre-parsed text representation |
| * |
| * @param {string} text same as in Filter() |
| * @param {string?} domain |
| * domain part of the text representation |
| * @param {string?} type |
| * rule type, either empty or @ (exception) or ? (emulation rule) |
| * @param {string} selector raw CSS selector |
| @@ -1082,8 +1104,26 @@ |
| { |
| ElemHideBase.call(this, text, domains, selector); |
| } |
| exports.ElemHideEmulationFilter = ElemHideEmulationFilter; |
| ElemHideEmulationFilter.prototype = extend(ElemHideBase, { |
| type: "elemhideemulation" |
| }); |
| + |
| +/** |
| + * Class for snippet filters |
| + * @param {string} text see Filter() |
| + * @param {string} [domains] see CodeInjectionFilter() |
| + * @param {string} script Script that should be executed |
| + * @constructor |
| + * @augments CodeInjectionFilter |
| + */ |
| +function SnippetFilter(text, domains, script) |
| +{ |
| + CodeInjectionFilter.call(this, text, domains, script); |
| +} |
| +exports.SnippetFilter = SnippetFilter; |
| + |
| +SnippetFilter.prototype = extend(CodeInjectionFilter, { |
| + type: "snippet" |
| +}); |