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; |
@@ -861,27 +864,29 @@ |
XMLHTTPREQUEST: 2048, |
OBJECT_SUBREQUEST: 4096, |
DTD: 1, |
MEDIA: 16384, |
FONT: 32768, |
BACKGROUND: 4, // Backwards compat, same as IMAGE |
+ SNIPPET: 0x8000000, |
Manish Jethani
2018/04/26 13:19:16
I had to take this slot since I can't go any highe
|
POPUP: 0x10000000, |
GENERICBLOCK: 0x20000000, |
ELEMHIDE: 0x40000000, |
GENERICHIDE: 0x80000000 |
}; |
-// CSP, DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options |
+// CSP, DOCUMENT, ELEMHIDE, SNIPPET, POPUP, GENERICHIDE and GENERICBLOCK options |
// shouldn't be there by default |
RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.CSP | |
RegExpFilter.typeMap.DOCUMENT | |
RegExpFilter.typeMap.ELEMHIDE | |
+ RegExpFilter.typeMap.SNIPPET | |
Manish Jethani
2018/04/26 13:19:16
I'm actually not sure that this is needed, $snippe
|
RegExpFilter.typeMap.POPUP | |
RegExpFilter.typeMap.GENERICHIDE | |
RegExpFilter.typeMap.GENERICBLOCK); |
/** |
* Class for blocking filters |
* @param {string} text see Filter() |
* @param {string} regexpSource see RegExpFilter() |
@@ -945,65 +950,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 |
Manish Jethani
2018/04/26 13:19:16
CSS selectors are also code, so let's make this ge
|
}); |
/** |
+ * 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 +1106,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" |
+}); |