| Index: lib/matcher.js |
| =================================================================== |
| --- a/lib/matcher.js |
| +++ b/lib/matcher.js |
| @@ -21,45 +21,42 @@ |
| * @fileOverview Matcher class implementing matching addresses against |
| * a list of filters. |
| */ |
| const {Filter, WhitelistFilter} = require("./filterClasses"); |
| /** |
| * Blacklist/whitelist filter matching |
| - * @constructor |
| */ |
| -function Matcher() |
| +class Matcher |
| { |
| - this.clear(); |
| -} |
| -exports.Matcher = Matcher; |
| + constructor() |
| + { |
| + /** |
| + * Lookup table for filters by their associated keyword |
| + * @type {Map.<string,(Filter|Filter[])>} |
| + */ |
| + this.filterByKeyword = new Map(); |
| -Matcher.prototype = { |
| - /** |
| - * Lookup table for filters by their associated keyword |
| - * @type {Map.<string,(Filter|Filter[])>} |
| - */ |
| - filterByKeyword: null, |
| - |
| - /** |
| - * Lookup table for keywords by the filter |
| - * @type {Map.<Filter,string>} |
| - */ |
| - keywordByFilter: null, |
| + /** |
| + * Lookup table for keywords by the filter |
| + * @type {Map.<Filter,string>} |
| + */ |
| + this.keywordByFilter = new Map(); |
| + } |
| /** |
| * Removes all known filters |
| */ |
| clear() |
| { |
| - this.filterByKeyword = new Map(); |
| - this.keywordByFilter = new Map(); |
| - }, |
| + this.filterByKeyword.clear(); |
| + this.keywordByFilter.clear(); |
| + } |
| /** |
| * Adds a filter to the matcher |
| * @param {RegExpFilter} filter |
| */ |
| add(filter) |
| { |
| if (this.keywordByFilter.has(filter)) |
| @@ -70,17 +67,17 @@ |
| let oldEntry = this.filterByKeyword.get(keyword); |
| if (typeof oldEntry == "undefined") |
| this.filterByKeyword.set(keyword, filter); |
| else if (oldEntry.length == 1) |
| this.filterByKeyword.set(keyword, [oldEntry, filter]); |
| else |
| oldEntry.push(filter); |
| this.keywordByFilter.set(filter, keyword); |
| - }, |
| + } |
| /** |
| * Removes a filter from the matcher |
| * @param {RegExpFilter} filter |
| */ |
| remove(filter) |
| { |
| let keyword = this.keywordByFilter.get(filter); |
| @@ -97,22 +94,22 @@ |
| { |
| list.splice(index, 1); |
| if (list.length == 1) |
| this.filterByKeyword.set(keyword, list[0]); |
| } |
| } |
| this.keywordByFilter.delete(filter); |
| - }, |
| + } |
| /** |
| * Chooses a keyword to be associated with the filter |
| * @param {Filter} filter |
| - * @return {string} keyword or an empty string if no keyword could be found |
| + * @returns {string} keyword or an empty string if no keyword could be found |
| */ |
| findKeyword(filter) |
| { |
| let result = ""; |
| let {text} = filter; |
| if (Filter.regexpRegExp.test(text)) |
| return result; |
| @@ -143,49 +140,49 @@ |
| (count == resultCount && candidate.length > resultLength)) |
| { |
| result = candidate; |
| resultCount = count; |
| resultLength = candidate.length; |
| } |
| } |
| return result; |
| - }, |
| + } |
| /** |
| * Checks whether a particular filter is being matched against. |
| * @param {RegExpFilter} filter |
| - * @return {boolean} |
| + * @returns {boolean} |
| */ |
| hasFilter(filter) |
| { |
| return this.keywordByFilter.has(filter); |
| - }, |
| + } |
| /** |
| * Returns the keyword used for a filter, null for unknown filters. |
|
Manish Jethani
2018/09/03 18:46:49
Let's make this `<code>null</code>` as well while
Jon Sonesen
2018/09/05 14:05:27
Done.
|
| * @param {RegExpFilter} filter |
| - * @return {?string} |
| + * @returns {?string} |
| */ |
| getKeywordForFilter(filter) |
| { |
| let keyword = this.keywordByFilter.get(filter); |
| return typeof keyword != "undefined" ? keyword : null; |
| - }, |
| + } |
| /** |
| * Checks whether the entries for a particular keyword match a URL |
| * @param {string} keyword |
| * @param {string} location |
| * @param {number} typeMask |
| - * @param {string} docDomain |
| - * @param {boolean} thirdParty |
| - * @param {string} sitekey |
| - * @param {boolean} specificOnly |
| - * @return {?Filter} |
| + * @param {string} [docDomain] |
| + * @param {boolean} [thirdParty] |
| + * @param {string} [sitekey] |
| + * @param {boolean} [specificOnly] |
| + * @returns {?Filter} |
| */ |
| _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey, |
| specificOnly) |
| { |
| let list = this.filterByKeyword.get(keyword); |
| if (typeof list == "undefined") |
| return null; |
| for (let i = 0; i < list.length; i++) |
| @@ -195,34 +192,34 @@ |
| 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 |
| * bitmask of content / request types to match |
| - * @param {string} docDomain |
| + * @param {string} [docDomain] |
| * domain name of the document that loads the URL |
| - * @param {boolean} thirdParty |
| + * @param {boolean} [thirdParty] |
| * should be true if the URL is a third-party request |
| - * @param {string} sitekey |
| + * @param {string} [sitekey] |
| * public key provided by the document |
| - * @param {boolean} specificOnly |
| - * should be true if generic matches should be ignored |
| - * @return {?RegExpFilter} |
| - * matching filter or null |
| + * @param {boolean} [specificOnly] |
| + * should be <code>true</code> if generic matches should be ignored |
| + * @returns {?RegExpFilter} |
| + * matching filter or <code>null</code> |
| */ |
| matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly) |
| { |
| let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); |
| if (candidates === null) |
| candidates = []; |
| candidates.push(""); |
| for (let i = 0, l = candidates.length; i < l; i++) |
| @@ -231,146 +228,142 @@ |
| docDomain, thirdParty, sitekey, |
| specificOnly); |
| if (result) |
| return result; |
| } |
| return null; |
| } |
| -}; |
| +} |
| + |
| +exports.Matcher = Matcher; |
| + |
| /** |
| * Combines a matcher for blocking and exception rules, automatically sorts |
| - * rules into two Matcher instances. |
| - * @constructor |
| - * @augments Matcher |
| + * rules into two {@link Matcher} instances. |
| */ |
| -function CombinedMatcher() |
| +class CombinedMatcher |
| { |
| - this.blacklist = new Matcher(); |
| - this.whitelist = new Matcher(); |
| - this.resultCache = new Map(); |
| -} |
| -exports.CombinedMatcher = CombinedMatcher; |
| - |
| -/** |
| - * Maximal number of matching cache entries to be kept |
| - * @type {number} |
| - */ |
| -CombinedMatcher.maxCacheEntries = 1000; |
| + constructor() |
| + { |
| + /** |
| + * Maximal number of matching cache entries to be kept |
| + * @type {number} |
| + */ |
| + this.maxCacheEntries = 1000; |
| -CombinedMatcher.prototype = |
| -{ |
| - /** |
| - * Matcher for blocking rules. |
| - * @type {Matcher} |
| - */ |
| - blacklist: null, |
| + /** |
| + * Matcher for blocking rules. |
| + * @type {Matcher} |
| + */ |
| + this.blacklist = new Matcher(); |
| - /** |
| - * Matcher for exception rules. |
| - * @type {Matcher} |
| - */ |
| - whitelist: null, |
| + /** |
| + * Matcher for exception rules. |
| + * @type {Matcher} |
| + */ |
| + this.whitelist = new Matcher(); |
| - /** |
| - * Lookup table of previous matchesAny results |
| - * @type {Map.<string,Filter>} |
| - */ |
| - resultCache: null, |
| + /** |
| + * Lookup table of previous {@link Matcher#matchesAny} results |
| + * @type {Map.<string,Filter>} |
| + */ |
| + this.resultCache = new Map(); |
| + } |
| /** |
| * @see Matcher#clear |
| */ |
| clear() |
| { |
| this.blacklist.clear(); |
| this.whitelist.clear(); |
| this.resultCache.clear(); |
| - }, |
| + } |
| /** |
| * @see Matcher#add |
| * @param {Filter} filter |
|
Manish Jethani
2018/09/03 18:46:49
When we use @see, we're not being consistent in ad
Jon Sonesen
2018/09/05 14:05:27
The reason for this is that matchesAny does not ha
Manish Jethani
2018/09/05 19:33:57
Oh damn. OK, no problem then.
|
| */ |
| add(filter) |
| { |
| if (filter instanceof WhitelistFilter) |
| this.whitelist.add(filter); |
| else |
| this.blacklist.add(filter); |
| this.resultCache.clear(); |
| - }, |
| + } |
| /** |
| * @see Matcher#remove |
| * @param {Filter} filter |
| */ |
| remove(filter) |
| { |
| if (filter instanceof WhitelistFilter) |
| this.whitelist.remove(filter); |
| else |
| this.blacklist.remove(filter); |
| this.resultCache.clear(); |
| - }, |
| + } |
| /** |
| * @see Matcher#findKeyword |
| * @param {Filter} filter |
| - * @return {string} keyword |
| + * @returns {string} keyword |
| */ |
| findKeyword(filter) |
| { |
| if (filter instanceof WhitelistFilter) |
| return this.whitelist.findKeyword(filter); |
| return this.blacklist.findKeyword(filter); |
| - }, |
| + } |
| /** |
| * @see Matcher#hasFilter |
| * @param {Filter} filter |
| - * @return {boolean} |
| + * @returns {boolean} |
| */ |
| hasFilter(filter) |
| { |
| if (filter instanceof WhitelistFilter) |
| return this.whitelist.hasFilter(filter); |
| return this.blacklist.hasFilter(filter); |
| - }, |
| + } |
| /** |
| * @see Matcher#getKeywordForFilter |
| * @param {Filter} filter |
| - * @return {string} keyword |
| + * @returns {string} keyword |
| */ |
| getKeywordForFilter(filter) |
| { |
| if (filter instanceof WhitelistFilter) |
| return this.whitelist.getKeywordForFilter(filter); |
| return this.blacklist.getKeywordForFilter(filter); |
| - }, |
| + } |
| /** |
| * Checks whether a particular filter is slow |
| * @param {RegExpFilter} filter |
| - * @return {boolean} |
| + * @returns {boolean} |
| */ |
| isSlowFilter(filter) |
| { |
| let matcher = ( |
| filter instanceof WhitelistFilter ? this.whitelist : this.blacklist |
| ); |
| if (matcher.hasFilter(filter)) |
| return !matcher.getKeywordForFilter(filter); |
| return !matcher.findKeyword(filter); |
| - }, |
| + } |
| /** |
| * Optimized filter matching testing both whitelist and blacklist matchers |
| * simultaneously. For parameters see |
| {@link Matcher#matchesAny Matcher.matchesAny()}. |
| * @see Matcher#matchesAny |
| * @inheritdoc |
|
Manish Jethani
2018/09/03 18:46:49
I don't think @inheritdoc makes sense here (also b
Jon Sonesen
2018/09/05 14:05:27
Similarly to above, eslint complains here without
|
| */ |
| @@ -395,17 +388,17 @@ |
| { |
| blacklistHit = this.blacklist._checkEntryMatch( |
| substr, location, typeMask, docDomain, thirdParty, sitekey, |
| specificOnly |
| ); |
| } |
| } |
| return blacklistHit; |
| - }, |
| + } |
| /** |
| * @see Matcher#matchesAny |
| * @inheritdoc |
| */ |
| matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly) |
| { |
| let key = location + " " + typeMask + " " + docDomain + " " + thirdParty + |
| @@ -420,15 +413,17 @@ |
| if (this.resultCache.size >= CombinedMatcher.maxCacheEntries) |
| this.resultCache.clear(); |
| this.resultCache.set(key, result); |
| return result; |
| } |
| -}; |
| +} |
| + |
| +exports.CombinedMatcher = CombinedMatcher; |
| /** |
| - * Shared CombinedMatcher instance that should usually be used. |
| + * Shared {@link CombinedMatcher} instance that should usually be used. |
| * @type {CombinedMatcher} |
| */ |
| exports.defaultMatcher = new CombinedMatcher(); |
|
Manish Jethani
2018/09/03 18:46:49
I think we should follow the convention of first d
Jon Sonesen
2018/09/05 14:05:27
Done.
|