| Index: lib/matcher.js |
| =================================================================== |
| --- a/lib/matcher.js |
| +++ b/lib/matcher.js |
| @@ -12,444 +12,11 @@ |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| "use strict"; |
| -/** |
| - * @fileOverview Matcher class implementing matching addresses against |
| - * a list of filters. |
| - */ |
| - |
| -const {Filter, WhitelistFilter} = require("filterClasses"); |
| - |
| -/** |
| - * Blacklist/whitelist filter matching |
| - * @constructor |
| - */ |
| -function Matcher() |
| -{ |
| - this.clear(); |
| -} |
| +const {Matcher} = require("compiled"); |
| exports.Matcher = Matcher; |
|
sergei
2017/12/20 09:23:23
this Matcher is used in lib/notifications.js, so t
|
| - |
| -Matcher.prototype = { |
| - /** |
| - * Lookup table for filters by their associated keyword |
| - * @type {Object} |
| - */ |
| - filterByKeyword: null, |
| - |
| - /** |
| - * Lookup table for keywords by the filter text |
| - * @type {Object} |
| - */ |
| - keywordByFilter: null, |
| - |
| - /** |
| - * Removes all known filters |
| - */ |
| - clear() |
| - { |
| - this.filterByKeyword = Object.create(null); |
| - this.keywordByFilter = Object.create(null); |
| - }, |
| - |
| - /** |
| - * Adds a filter to the matcher |
| - * @param {RegExpFilter} filter |
| - */ |
| - add(filter) |
| - { |
| - if (filter.text in this.keywordByFilter) |
| - return; |
| - |
| - // Look for a suitable keyword |
| - let keyword = this.findKeyword(filter); |
| - let oldEntry = this.filterByKeyword[keyword]; |
| - if (typeof oldEntry == "undefined") |
| - this.filterByKeyword[keyword] = filter; |
| - else if (oldEntry.length == 1) |
| - this.filterByKeyword[keyword] = [oldEntry, filter]; |
| - else |
| - oldEntry.push(filter); |
| - this.keywordByFilter[filter.text] = keyword; |
| - }, |
| - |
| - /** |
| - * Removes a filter from the matcher |
| - * @param {RegExpFilter} filter |
| - */ |
| - remove(filter) |
| - { |
| - if (!(filter.text in this.keywordByFilter)) |
| - return; |
| - |
| - let keyword = this.keywordByFilter[filter.text]; |
| - let list = this.filterByKeyword[keyword]; |
| - if (list.length <= 1) |
| - delete this.filterByKeyword[keyword]; |
| - else |
| - { |
| - let index = list.indexOf(filter); |
| - if (index >= 0) |
| - { |
| - list.splice(index, 1); |
| - if (list.length == 1) |
| - this.filterByKeyword[keyword] = list[0]; |
| - } |
| - } |
| - |
| - delete this.keywordByFilter[filter.text]; |
| - }, |
| - |
| - /** |
| - * 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 |
| - */ |
| - findKeyword(filter) |
| - { |
| - let result = ""; |
| - let {text} = filter; |
| - if (Filter.regexpRegExp.test(text)) |
| - return result; |
| - |
| - // Remove options |
| - let match = Filter.optionsRegExp.exec(text); |
| - if (match) |
| - text = match.input.substr(0, match.index); |
| - |
| - // Remove whitelist marker |
| - if (text.substr(0, 2) == "@@") |
| - text = text.substr(2); |
| - |
| - let candidates = text.toLowerCase().match( |
| - /[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g |
| - ); |
| - 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 count = (candidate in hash ? hash[candidate].length : 0); |
| - if (count < resultCount || |
| - (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} |
| - */ |
| - hasFilter(filter) |
| - { |
| - return (filter.text in this.keywordByFilter); |
| - }, |
| - |
| - /** |
| - * Returns the keyword used for a filter, null for unknown filters. |
| - * @param {RegExpFilter} filter |
| - * @return {string} |
| - */ |
| - getKeywordForFilter(filter) |
| - { |
| - if (filter.text in this.keywordByFilter) |
| - return this.keywordByFilter[filter.text]; |
| - return 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} |
| - */ |
| - _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey, |
| - specificOnly) |
| - { |
| - let list = this.filterByKeyword[keyword]; |
| - for (let i = 0; i < list.length; i++) |
| - { |
| - let filter = list[i]; |
| - |
| - 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 |
| - * domain name of the document that loads the URL |
| - * @param {boolean} thirdParty |
| - * should be true if the URL is a third-party request |
| - * @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 |
| - */ |
| - 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++) |
| - { |
| - let substr = candidates[i]; |
| - if (substr in this.filterByKeyword) |
| - { |
| - let result = this._checkEntryMatch(substr, location, typeMask, |
| - docDomain, thirdParty, sitekey, |
| - specificOnly); |
| - if (result) |
| - return result; |
| - } |
| - } |
| - |
| - return null; |
| - } |
| -}; |
| - |
| -/** |
| - * Combines a matcher for blocking and exception rules, automatically sorts |
| - * rules into two Matcher instances. |
| - * @constructor |
| - * @augments Matcher |
| - */ |
| -function CombinedMatcher() |
| -{ |
| - this.blacklist = new Matcher(); |
| - this.whitelist = new Matcher(); |
| - this.resultCache = Object.create(null); |
| -} |
| -exports.CombinedMatcher = CombinedMatcher; |
| - |
| -/** |
| - * Maximal number of matching cache entries to be kept |
| - * @type {number} |
| - */ |
| -CombinedMatcher.maxCacheEntries = 1000; |
| - |
| -CombinedMatcher.prototype = |
| -{ |
| - /** |
| - * Matcher for blocking rules. |
| - * @type {Matcher} |
| - */ |
| - blacklist: null, |
| - |
| - /** |
| - * Matcher for exception rules. |
| - * @type {Matcher} |
| - */ |
| - whitelist: null, |
| - |
| - /** |
| - * Lookup table of previous matchesAny results |
| - * @type {Object} |
| - */ |
| - resultCache: null, |
| - |
| - /** |
| - * Number of entries in resultCache |
| - * @type {number} |
| - */ |
| - cacheEntries: 0, |
| - |
| - /** |
| - * @see Matcher#clear |
| - */ |
| - clear() |
| - { |
| - this.blacklist.clear(); |
| - this.whitelist.clear(); |
| - this.resultCache = Object.create(null); |
| - this.cacheEntries = 0; |
| - }, |
| - |
| - /** |
| - * @see Matcher#add |
| - * @param {Filter} filter |
| - */ |
| - add(filter) |
| - { |
| - if (filter instanceof WhitelistFilter) |
| - this.whitelist.add(filter); |
| - else |
| - this.blacklist.add(filter); |
| - |
| - if (this.cacheEntries > 0) |
| - { |
| - this.resultCache = Object.create(null); |
| - this.cacheEntries = 0; |
| - } |
| - }, |
| - |
| - /** |
| - * @see Matcher#remove |
| - * @param {Filter} filter |
| - */ |
| - remove(filter) |
| - { |
| - if (filter instanceof WhitelistFilter) |
| - this.whitelist.remove(filter); |
| - else |
| - this.blacklist.remove(filter); |
| - |
| - if (this.cacheEntries > 0) |
| - { |
| - this.resultCache = Object.create(null); |
| - this.cacheEntries = 0; |
| - } |
| - }, |
| - |
| - /** |
| - * @see Matcher#findKeyword |
| - * @param {Filter} filter |
| - * @return {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} |
| - */ |
| - 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 |
| - */ |
| - 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} |
| - */ |
| - 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 Matcher.matchesAny(). |
| - * @see Matcher#matchesAny |
| - * @inheritdoc |
| - */ |
| - matchesAnyInternal(location, typeMask, docDomain, thirdParty, sitekey, |
| - specificOnly) |
| - { |
| - let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); |
| - if (candidates === null) |
| - candidates = []; |
| - candidates.push(""); |
| - |
| - let blacklistHit = null; |
| - for (let i = 0, l = candidates.length; i < l; i++) |
| - { |
| - let substr = candidates[i]; |
| - if (substr in this.whitelist.filterByKeyword) |
| - { |
| - let result = this.whitelist._checkEntryMatch( |
| - substr, location, typeMask, docDomain, thirdParty, sitekey |
| - ); |
| - if (result) |
| - return result; |
| - } |
| - if (substr in this.blacklist.filterByKeyword && blacklistHit === null) |
| - { |
| - 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 + |
| - " " + sitekey + " " + specificOnly; |
| - if (key in this.resultCache) |
| - return this.resultCache[key]; |
| - |
| - let result = this.matchesAnyInternal(location, typeMask, docDomain, |
| - thirdParty, sitekey, specificOnly); |
| - |
| - if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) |
| - { |
| - this.resultCache = Object.create(null); |
| - this.cacheEntries = 0; |
| - } |
| - |
| - this.resultCache[key] = result; |
| - this.cacheEntries++; |
| - |
| - return result; |
| - } |
| -}; |
| - |
| -/** |
| - * Shared CombinedMatcher instance that should usually be used. |
| - * @type {CombinedMatcher} |
| - */ |
| -exports.defaultMatcher = new CombinedMatcher(); |
| +exports.defaultMatcher = Matcher.create(); |
|
sergei
2017/10/18 10:58:43
BTW, just for reference, currently it's not delete
|