Index: lib/matcher.js |
=================================================================== |
--- a/lib/matcher.js |
+++ b/lib/matcher.js |
@@ -61,77 +61,91 @@ |
/** |
* Blacklist/whitelist filter matching |
*/ |
class Matcher |
{ |
constructor() |
{ |
/** |
- * Lookup table for filters by their associated keyword |
- * @type {Map.<string,(Filter|Set.<Filter>)>} |
+ * Lookup table for simple filters by their associated keyword |
+ * @type {Map.<string,(RegExpFilter|Set.<RegExpFilter>)>} |
Manish Jethani
2018/10/25 21:24:10
Unrelated documentation update, but these can only
|
* @private |
*/ |
- this._filterByKeyword = new Map(); |
+ this._simpleFiltersByKeyword = new Map(); |
+ |
+ /** |
+ * Lookup table for complex filters by their associated keyword |
+ * @type {Map.<string,(RegExpFilter|Set.<RegExpFilter>)>} |
+ * @private |
+ */ |
+ this._complexFiltersByKeyword = new Map(); |
} |
/** |
* Removes all known filters |
*/ |
clear() |
{ |
- this._filterByKeyword.clear(); |
+ this._simpleFiltersByKeyword.clear(); |
+ this._complexFiltersByKeyword.clear(); |
} |
/** |
* Adds a filter to the matcher |
* @param {RegExpFilter} filter |
*/ |
add(filter) |
{ |
+ let filtersByKeyword = filter.isLocationOnly() ? |
Manish Jethani
2018/10/25 21:24:10
The filter goes into one of the two maps, dependin
|
+ this._simpleFiltersByKeyword : |
+ this._complexFiltersByKeyword; |
// Look for a suitable keyword |
let keyword = this.findKeyword(filter); |
- let set = this._filterByKeyword.get(keyword); |
+ let set = filtersByKeyword.get(keyword); |
if (typeof set == "undefined") |
{ |
- this._filterByKeyword.set(keyword, filter); |
+ filtersByKeyword.set(keyword, filter); |
} |
else if (set.size == 1) |
{ |
if (filter != set) |
- this._filterByKeyword.set(keyword, new Set([set, filter])); |
+ filtersByKeyword.set(keyword, new Set([set, filter])); |
} |
else |
{ |
set.add(filter); |
} |
} |
/** |
* Removes a filter from the matcher |
* @param {RegExpFilter} filter |
*/ |
remove(filter) |
{ |
+ let filtersByKeyword = filter.isLocationOnly() ? |
+ this._simpleFiltersByKeyword : |
+ this._complexFiltersByKeyword; |
let keyword = this.findKeyword(filter); |
- let set = this._filterByKeyword.get(keyword); |
+ let set = filtersByKeyword.get(keyword); |
if (typeof set == "undefined") |
return; |
if (set.size == 1) |
{ |
if (filter == set) |
- this._filterByKeyword.delete(keyword); |
+ filtersByKeyword.delete(keyword); |
} |
else |
{ |
set.delete(filter); |
if (set.size == 1) |
- this._filterByKeyword.set(keyword, [...set][0]); |
+ filtersByKeyword.set(keyword, [...set][0]); |
} |
} |
/** |
* Chooses a keyword to be associated with the filter |
* @param {Filter} filter |
* @returns {string} keyword or an empty string if no keyword could be found |
* @protected |
@@ -142,24 +156,27 @@ |
let {pattern} = filter; |
if (pattern == null) |
return result; |
let candidates = pattern.toLowerCase().match(allKeywordsRegExp); |
if (!candidates) |
return result; |
- let hash = this._filterByKeyword; |
let resultCount = 0xFFFFFF; |
let resultLength = 0; |
for (let i = 0, l = candidates.length; i < l; i++) |
{ |
let candidate = candidates[i].substr(1); |
- let filters = hash.get(candidate); |
- let count = typeof filters != "undefined" ? filters.size : 0; |
+ let simpleFilters = this._simpleFiltersByKeyword.get(candidate); |
+ let complexFilters = this._complexFiltersByKeyword.get(candidate); |
+ let count = (typeof simpleFilters != "undefined" ? |
Manish Jethani
2018/10/25 21:24:10
Now count needs to be the sum of the sizes of the
|
+ simpleFilters.size : 0) + |
+ (typeof complexFilters != "undefined" ? |
+ complexFilters.size : 0); |
if (count < resultCount || |
(count == resultCount && candidate.length > resultLength)) |
{ |
result = candidate; |
resultCount = count; |
resultLength = candidate.length; |
} |
} |
@@ -176,29 +193,49 @@ |
* @param {string} [sitekey] |
* @param {boolean} [specificOnly] |
* @returns {?Filter} |
* @protected |
*/ |
checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey, |
specificOnly) |
{ |
- let set = this._filterByKeyword.get(keyword); |
- if (typeof set == "undefined") |
- return null; |
- |
- for (let filter of set) |
+ // We need to skip the simple (location-only) filters if the type mask does |
+ // not contain any non-default content types like $document, $elemhide, |
+ // $csp, and so on. |
+ if ((typeMask & RegExpFilter.prototype.contentType) != 0) |
{ |
- if (specificOnly && filter.isGeneric() && |
- !(filter instanceof WhitelistFilter)) |
- continue; |
+ let simpleSet = this._simpleFiltersByKeyword.get(keyword); |
+ if (simpleSet) |
+ { |
+ for (let filter of simpleSet) |
+ { |
+ if ((!specificOnly || filter instanceof WhitelistFilter) && |
+ filter.matchesLocation(location)) |
Manish Jethani
2018/10/25 21:24:10
For these filters, we only match the location. We
|
+ { |
+ return filter; |
+ } |
+ } |
+ } |
+ } |
- if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey)) |
- return filter; |
+ let complexSet = this._complexFiltersByKeyword.get(keyword); |
+ if (complexSet) |
+ { |
+ for (let filter of complexSet) |
+ { |
+ if (specificOnly && filter.isGeneric() && |
+ !(filter instanceof WhitelistFilter)) |
+ continue; |
+ |
+ if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey)) |
+ return filter; |
+ } |
} |
+ |
return null; |
} |
/** |
* Tests whether the URL matches any of the known filters |
* @param {string} location |
* URL to be tested |
* @param {number} typeMask |