Index: lib/filterClasses.js |
=================================================================== |
--- a/lib/filterClasses.js |
+++ b/lib/filterClasses.js |
@@ -20,16 +20,18 @@ |
/** |
* @fileOverview Definition of Filter class and its subclasses. |
*/ |
const {filterNotifier} = require("./filterNotifier"); |
const {extend} = require("./coreUtils"); |
const {filterToRegExp} = require("./common"); |
+let tripleAnchorRegExp = new RegExp(filterToRegExp("|||")); |
+ |
/** |
* All known unique domain sources mapped to their parsed values. |
* @type {Map.<string,Map.<string,boolean>>} |
*/ |
let knownDomainMaps = new Map(); |
/** |
* Abstract base class for filters |
@@ -659,21 +661,22 @@ |
* letters |
* @param {string} [domains] |
* Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" |
* @param {boolean} [thirdParty] |
* Defines whether the filter should apply to third-party or first-party |
* content only |
* @param {string} [sitekeys] |
* Public keys of websites that this filter should apply to |
- * @constructor |
+ * @param {?string} [rewrite] |
+ * The (optional) rule specifying how to rewrite the URL. |
* @augments ActiveFilter |
*/ |
function RegExpFilter(text, regexpSource, contentType, matchCase, domains, |
- thirdParty, sitekeys) |
+ thirdParty, sitekeys, rewrite) |
{ |
ActiveFilter.call(this, text, domains); |
if (contentType != null) |
this.contentType = contentType; |
if (matchCase) |
this.matchCase = matchCase; |
if (thirdParty != null) |
@@ -690,16 +693,22 @@ |
let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), |
this.matchCase ? "" : "i"); |
Object.defineProperty(this, "regexp", {value: regexp}); |
} |
else |
{ |
// No need to convert this filter to regular expression yet, do it on demand |
this.pattern = regexpSource; |
+ |
+ if (!this.matchCase && rewrite == null && |
+ !/[*^|]/.test(this.pattern.replace(/^\|{2}/, ""))) |
+ { |
+ this.pattern = this.pattern.toLowerCase(); |
+ } |
} |
} |
exports.RegExpFilter = RegExpFilter; |
RegExpFilter.prototype = extend(ActiveFilter, { |
/** |
* Number of filters contained, will always be 1 (required to |
* optimize {@link Matcher}). |
@@ -719,20 +728,27 @@ |
*/ |
pattern: null, |
/** |
* Regular expression to be used when testing against this filter |
* @type {RegExp} |
*/ |
get regexp() |
{ |
- let source = filterToRegExp(this.pattern, this.rewrite != null); |
- let regexp = new RegExp(source, this.matchCase ? "" : "i"); |
- Object.defineProperty(this, "regexp", {value: regexp}); |
- return regexp; |
+ let value = null; |
+ |
+ let {pattern, rewrite} = this; |
+ if (rewrite != null || /[*^|]/.test(pattern.replace(/^\|{2}/, ""))) |
+ { |
+ value = new RegExp(filterToRegExp(pattern, rewrite != null), |
+ this.matchCase ? "" : "i"); |
+ } |
+ |
+ Object.defineProperty(this, "regexp", {value}); |
+ return value; |
}, |
/** |
* Content types the filter applies to, combination of values from |
* RegExpFilter.typeMap |
* @type {number} |
*/ |
contentType: 0x7FFFFFFF, |
/** |
@@ -783,17 +799,39 @@ |
* @param {string} [sitekey] public key provided by the document |
* @return {boolean} true in case of a match |
*/ |
matches(location, typeMask, docDomain, thirdParty, sitekey) |
{ |
return (this.contentType & typeMask) != 0 && |
(this.thirdParty == null || this.thirdParty == thirdParty) && |
this.isActiveOnDomain(docDomain, sitekey) && |
- this.regexp.test(location); |
+ this.matchesLocation(location); |
+ }, |
+ |
+ matchesLocation(location) |
+ { |
+ let {regexp} = this; |
+ |
+ if (regexp) |
+ return regexp.test(location); |
+ |
+ if (!this.matchCase) |
+ location = location.toLowerCase(); |
+ |
+ let {pattern} = this; |
+ |
+ if (pattern[0] == "|" && pattern[1] == "|") |
+ { |
+ let index = location.indexOf(pattern.substring(2)); |
+ return index != -1 && location[index] != "/" && |
+ tripleAnchorRegExp.test(location.substring(0, index)); |
+ } |
+ |
+ return location.includes(pattern); |
} |
}); |
/** |
* Yields the filter itself (required to optimize {@link Matcher}). |
* @yields {RegExpFilter} |
*/ |
RegExpFilter.prototype[Symbol.iterator] = function*() |
@@ -988,17 +1026,17 @@ |
* BlockingFilter.prototype.rewrite. |
* @constructor |
* @augments RegExpFilter |
*/ |
function BlockingFilter(text, regexpSource, contentType, matchCase, domains, |
thirdParty, sitekeys, collapse, csp, rewrite) |
{ |
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, |
- thirdParty, sitekeys); |
+ thirdParty, sitekeys, rewrite); |
if (collapse != null) |
this.collapse = collapse; |
if (csp != null) |
this.csp = csp; |
if (rewrite != null) |