| Index: lib/matcher.js |
| =================================================================== |
| deleted file mode 100644 |
| --- a/lib/matcher.js |
| +++ /dev/null |
| @@ -1,407 +0,0 @@ |
| -/* |
| - * This file is part of Adblock Plus <https://adblockplus.org/>, |
| - * Copyright (C) 2006-2016 Eyeo GmbH |
| - * |
| - * Adblock Plus is free software: you can redistribute it and/or modify |
| - * it under the terms of the GNU General Public License version 3 as |
| - * published by the Free Software Foundation. |
| - * |
| - * Adblock Plus is distributed in the hope that it will be useful, |
| - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| - * 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/>. |
| - */ |
| - |
| -/** |
| - * @fileOverview Matcher class implementing matching addresses against a list of filters. |
| - */ |
| - |
| -let {Filter, RegExpFilter, WhitelistFilter} = require("filterClasses"); |
| - |
| -/** |
| - * Blacklist/whitelist filter matching |
| - * @constructor |
| - */ |
| -function Matcher() |
| -{ |
| - this.clear(); |
| -} |
| -exports.Matcher = Matcher; |
| - |
| -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: function() |
| - { |
| - this.filterByKeyword = Object.create(null); |
| - this.keywordByFilter = Object.create(null); |
| - }, |
| - |
| - /** |
| - * Adds a filter to the matcher |
| - * @param {RegExpFilter} filter |
| - */ |
| - add: function(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: function(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 {String} text text representation of the filter |
| - * @return {String} keyword (might be empty string) |
| - */ |
| - findKeyword: function(filter) |
| - { |
| - let result = ""; |
| - let text = filter.text; |
| - 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. |
| - */ |
| - hasFilter: function(/**RegExpFilter*/ filter) /**Boolean*/ |
| - { |
| - return (filter.text in this.keywordByFilter); |
| - }, |
| - |
| - /** |
| - * Returns the keyword used for a filter, null for unknown filters. |
| - */ |
| - getKeywordForFilter: function(/**RegExpFilter*/ filter) /**String*/ |
| - { |
| - if (filter.text in this.keywordByFilter) |
| - return this.keywordByFilter[filter.text]; |
| - else |
| - return null; |
| - }, |
| - |
| - /** |
| - * Checks whether the entries for a particular keyword match a URL |
| - */ |
| - _checkEntryMatch: function(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 {String} 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: function(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 |
| - */ |
| -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: function() |
| - { |
| - this.blacklist.clear(); |
| - this.whitelist.clear(); |
| - this.resultCache = Object.create(null); |
| - this.cacheEntries = 0; |
| - }, |
| - |
| - /** |
| - * @see Matcher#add |
| - */ |
| - add: function(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 |
| - */ |
| - remove: function(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 |
| - */ |
| - findKeyword: function(filter) |
| - { |
| - if (filter instanceof WhitelistFilter) |
| - return this.whitelist.findKeyword(filter); |
| - else |
| - return this.blacklist.findKeyword(filter); |
| - }, |
| - |
| - /** |
| - * @see Matcher#hasFilter |
| - */ |
| - hasFilter: function(filter) |
| - { |
| - if (filter instanceof WhitelistFilter) |
| - return this.whitelist.hasFilter(filter); |
| - else |
| - return this.blacklist.hasFilter(filter); |
| - }, |
| - |
| - /** |
| - * @see Matcher#getKeywordForFilter |
| - */ |
| - getKeywordForFilter: function(filter) |
| - { |
| - if (filter instanceof WhitelistFilter) |
| - return this.whitelist.getKeywordForFilter(filter); |
| - else |
| - return this.blacklist.getKeywordForFilter(filter); |
| - }, |
| - |
| - /** |
| - * Checks whether a particular filter is slow |
| - */ |
| - isSlowFilter: function(/**RegExpFilter*/ filter) /**Boolean*/ |
| - { |
| - let matcher = (filter instanceof WhitelistFilter ? this.whitelist : this.blacklist); |
| - if (matcher.hasFilter(filter)) |
| - return !matcher.getKeywordForFilter(filter); |
| - else |
| - return !matcher.findKeyword(filter); |
| - }, |
| - |
| - /** |
| - * Optimized filter matching testing both whitelist and blacklist matchers |
| - * simultaneously. For parameters see Matcher.matchesAny(). |
| - * @see Matcher#matchesAny |
| - */ |
| - matchesAnyInternal: function(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 |
| - */ |
| - matchesAny: function(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 |
| - */ |
| -let defaultMatcher = exports.defaultMatcher = new CombinedMatcher(); |