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 content filters should match |
* @type {RegExp} |
*/ |
-Filter.elemhideRegExp = /^([^/*|@"!]*?)#([@?])?#(.+)$/; |
+Filter.contentRegExp = /^([^/*|@"!]*?)#([@?$])?#(.+)$/; |
/** |
* 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,34 +112,32 @@ |
* @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.contentRegExp.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}`); |
} |
- filter = ElemHideBase.fromText( |
- text, match[1], match[2], match[3] |
- ); |
+ filter = ContentFilter.fromText(text, match[1], match[2], match[3]); |
} |
else if (text[0] == "!") |
filter = new CommentFilter(text); |
else |
filter = RegExpFilter.fromText(text); |
Filter.knownFilters.set(filter.text, filter); |
return filter; |
@@ -179,22 +177,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 |
- // contain spaces |
- if (Filter.elemhideRegExp.test(text)) |
+ // Special treatment for content filters, right side is allowed to contain |
+ // spaces |
+ if (Filter.contentRegExp.test(text)) |
{ |
- let [, domains, separator, selector] = /^(.*?)(#[@?]?#?)(.*)$/.exec(text); |
- return domains.replace(/ +/g, "") + separator + selector.trim(); |
+ let [, domains, separator, script] = /^(.*?)(#[@?$]?#?)(.*)$/.exec(text); |
+ return domains.replace(/ +/g, "") + separator + script.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; |
@@ -990,83 +988,114 @@ |
} |
exports.WhitelistFilter = WhitelistFilter; |
WhitelistFilter.prototype = extend(RegExpFilter, { |
type: "whitelist" |
}); |
/** |
- * Base class for element hiding filters |
+ * Base class for content filters |
* @param {string} text see {@link Filter 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} script Script that should be executed |
* @constructor |
* @augments ActiveFilter |
*/ |
-function ElemHideBase(text, domains, selector) |
+function ContentFilter(text, domains, script) |
{ |
ActiveFilter.call(this, text, domains || null); |
- // Braces are being escaped to prevent CSS rule injection. |
- this.selector = selector.replace("{", "\\7B ").replace("}", "\\7D "); |
+ this.script = script; |
} |
-exports.ElemHideBase = ElemHideBase; |
+exports.ContentFilter = ContentFilter; |
-ElemHideBase.prototype = extend(ActiveFilter, { |
+ContentFilter.prototype = extend(ActiveFilter, { |
/** |
* @see ActiveFilter.domainSeparator |
*/ |
domainSeparator: ",", |
/** |
- * CSS selector for the HTML elements that should be hidden |
+ * Script that should be executed |
* @type {string} |
*/ |
- selector: null |
+ script: null |
}); |
/** |
- * Creates an element hiding filter from a pre-parsed text representation |
+ * Creates a content filter from a pre-parsed text representation |
* |
* @param {string} text same as in Filter() |
* @param {string} [domains] |
* domains part of the text representation |
* @param {string} [type] |
-* rule type, either empty or @ (exception) or ? (emulation rule) |
- * @param {string} selector raw CSS selector |
+ * rule type, either empty or @ (exception) or ? (emulation rule) or |
+ * $ (snippet) |
+ * @param {string} script |
+ * script part of the text representation, either a CSS selector or a snippet |
+ * script |
* @return {ElemHideFilter|ElemHideException| |
- * ElemHideEmulationFilter|InvalidFilter} |
+ * ElemHideEmulationFilter|SnippetFilter|InvalidFilter} |
*/ |
-ElemHideBase.fromText = function(text, domains, type, selector) |
+ContentFilter.fromText = function(text, domains, type, script) |
{ |
- // We don't allow ElemHide filters which have any empty domains. |
- // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that |
- // changes this must be changed too. |
+ // We don't allow content filters which have any empty domains. |
+ // Note: The ContentFilter.prototype.domainSeparator is duplicated here, if |
+ // that changes this must be changed too. |
if (domains && /(^|,)~?(,|$)/.test(domains)) |
return new InvalidFilter(text, "filter_invalid_domain"); |
if (type == "@") |
- return new ElemHideException(text, domains, selector); |
+ return new ElemHideException(text, domains, script); |
+ |
+ if (type == "$") |
+ return new SnippetFilter(text, domains, script); |
if (type == "?") |
{ |
// Element hiding emulation filters are inefficient so we need to make sure |
// that they're only applied if they specify active domains |
if (!/,[^~][^,.]*\.[^,]/.test("," + domains)) |
return new InvalidFilter(text, "filter_elemhideemulation_nodomain"); |
- return new ElemHideEmulationFilter(text, domains, selector); |
+ return new ElemHideEmulationFilter(text, domains, script); |
} |
- return new ElemHideFilter(text, domains, selector); |
+ return new ElemHideFilter(text, domains, script); |
}; |
+/* |
+ * Base class for element hiding filters |
+ * @param {string} text see {@link Filter Filter()} |
+ * @param {string} [domains] see {@link ContentFilter ContentFilter()} |
+ * @param {string} selector CSS selector for the HTML elements that should be |
+ * hidden |
+ * @constructor |
+ * @augments ContentFilter |
+ */ |
+function ElemHideBase(text, domains, selector) |
+{ |
+ ContentFilter.call(this, text, domains, selector); |
+} |
+exports.ElemHideBase = ElemHideBase; |
+ |
+ElemHideBase.prototype = extend(ContentFilter, { |
+ /** |
+ * CSS selector for the HTML elements that should be hidden |
+ * @type {string} |
+ */ |
+ get selector() |
+ { |
+ // Braces are being escaped to prevent CSS rule injection. |
+ return this.script.replace("{", "\\7B ").replace("}", "\\7D "); |
+ } |
+}); |
+ |
/** |
* Class for element hiding filters |
* @param {string} text see {@link Filter Filter()} |
* @param {string} [domains] see {@link ElemHideBase ElemHideBase()} |
* @param {string} selector see {@link ElemHideBase ElemHideBase()} |
* @constructor |
* @augments ElemHideBase |
*/ |
@@ -1110,8 +1139,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 ContentFilter() |
+ * @param {string} script Script that should be executed |
+ * @constructor |
+ * @augments ContentFilter |
+ */ |
+function SnippetFilter(text, domains, script) |
+{ |
+ ContentFilter.call(this, text, domains, script); |
+} |
+exports.SnippetFilter = SnippetFilter; |
+ |
+SnippetFilter.prototype = extend(ContentFilter, { |
+ type: "snippet" |
+}); |