| Index: lib/filterClasses.js |
| =================================================================== |
| deleted file mode 100644 |
| --- a/lib/filterClasses.js |
| +++ /dev/null |
| @@ -1,1070 +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 Definition of Filter class and its subclasses. |
| - */ |
| - |
| -let {FilterNotifier} = require("filterNotifier"); |
| -let {Utils} = require("utils"); |
| - |
| -/** |
| - * Abstract base class for filters |
| - * |
| - * @param {String} text string representation of the filter |
| - * @constructor |
| - */ |
| -function Filter(text) |
| -{ |
| - this.text = text; |
| - this.subscriptions = []; |
| -} |
| -exports.Filter = Filter; |
| - |
| -Filter.prototype = |
| -{ |
| - /** |
| - * String representation of the filter |
| - * @type String |
| - */ |
| - text: null, |
| - |
| - /** |
| - * Filter subscriptions the filter belongs to |
| - * @type Subscription[] |
| - */ |
| - subscriptions: null, |
| - |
| - /** |
| - * Filter type as a string, e.g. "blocking". |
| - * @type String |
| - */ |
| - get type() |
| - { |
| - throw new Error("Please define filter type in the subclass"); |
| - }, |
| - |
| - /** |
| - * Serializes the filter to an array of strings for writing out on the disk. |
| - * @param {string[]} buffer buffer to push the serialization results into |
| - */ |
| - serialize: function(buffer) |
| - { |
| - buffer.push("[Filter]"); |
| - buffer.push("text=" + this.text); |
| - }, |
| - |
| - toString: function() |
| - { |
| - return this.text; |
| - } |
| -}; |
| - |
| -/** |
| - * Cache for known filters, maps string representation to filter objects. |
| - * @type Object |
| - */ |
| -Filter.knownFilters = Object.create(null); |
| - |
| -/** |
| - * Regular expression that element hiding filters should match |
| - * @type RegExp |
| - */ |
| -Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; |
| -/** |
| - * Regular expression that RegExp filters specified as RegExps should match |
| - * @type RegExp |
| - */ |
| -Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; |
| -/** |
| - * Regular expression that options on a RegExp filter should match |
| - * @type RegExp |
| - */ |
| -Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; |
| -/** |
| - * Regular expression that CSS property filters should match |
| - * Properties must not contain " or ' |
| - * @type RegExp |
| - */ |
| -Filter.csspropertyRegExp = /\[\-abp\-properties=(["'])([^"']+)\1\]/; |
| - |
| -/** |
| - * Creates a filter of correct type from its text representation - does the basic parsing and |
| - * calls the right constructor then. |
| - * |
| - * @param {String} text as in Filter() |
| - * @return {Filter} |
| - */ |
| -Filter.fromText = function(text) |
| -{ |
| - if (text in Filter.knownFilters) |
| - return Filter.knownFilters[text]; |
| - |
| - let ret; |
| - let match = (text.indexOf("#") >= 0 ? Filter.elemhideRegExp.exec(text) : null); |
| - if (match) |
| - ret = ElemHideBase.fromText(text, match[1], !!match[2], match[3], match[4], match[5]); |
| - else if (text[0] == "!") |
| - ret = new CommentFilter(text); |
| - else |
| - ret = RegExpFilter.fromText(text); |
| - |
| - Filter.knownFilters[ret.text] = ret; |
| - return ret; |
| -}; |
| - |
| -/** |
| - * Deserializes a filter |
| - * |
| - * @param {Object} obj map of serialized properties and their values |
| - * @return {Filter} filter or null if the filter couldn't be created |
| - */ |
| -Filter.fromObject = function(obj) |
| -{ |
| - let ret = Filter.fromText(obj.text); |
| - if (ret instanceof ActiveFilter) |
| - { |
| - if ("disabled" in obj) |
| - ret._disabled = (obj.disabled == "true"); |
| - if ("hitCount" in obj) |
| - ret._hitCount = parseInt(obj.hitCount) || 0; |
| - if ("lastHit" in obj) |
| - ret._lastHit = parseInt(obj.lastHit) || 0; |
| - } |
| - return ret; |
| -}; |
| - |
| -/** |
| - * Removes unnecessary whitespaces from filter text, will only return null if |
| - * the input parameter is null. |
| - */ |
| -Filter.normalize = function(/**String*/ text) /**String*/ |
| -{ |
| - if (!text) |
| - return text; |
| - |
| - // Remove line breaks and such |
| - text = text.replace(/[^\S ]/g, ""); |
| - |
| - if (/^\s*!/.test(text)) |
| - { |
| - // Don't remove spaces inside comments |
| - return text.trim(); |
| - } |
| - else if (Filter.elemhideRegExp.test(text)) |
| - { |
| - // Special treatment for element hiding filters, right side is allowed to contain spaces |
| - let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text); |
| - return domain.replace(/\s/g, "") + separator + selector.trim(); |
| - } |
| - else |
| - return text.replace(/\s/g, ""); |
| -}; |
| - |
| -/** |
| - * Converts filter text into regular expression string |
| - * @param {String} text as in Filter() |
| - * @return {String} regular expression representation of filter text |
| - */ |
| -Filter.toRegExp = function(text) |
| -{ |
| - return text |
| - .replace(/\*+/g, "*") // remove multiple wildcards |
| - .replace(/\^\|$/, "^") // remove anchors following separator placeholder |
| - .replace(/\W/g, "\\$&") // escape special symbols |
| - .replace(/\\\*/g, ".*") // replace wildcards by .* |
| - // process separator placeholders (all ANSI characters but alphanumeric characters and _%.-) |
| - .replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)") |
| - .replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") // process extended anchor at expression start |
| - .replace(/^\\\|/, "^") // process anchor at expression start |
| - .replace(/\\\|$/, "$") // process anchor at expression end |
| - .replace(/^(\.\*)/, "") // remove leading wildcards |
| - .replace(/(\.\*)$/, ""); // remove trailing wildcards |
| -} |
| - |
| -/** |
| - * Class for invalid filters |
| - * @param {String} text see Filter() |
| - * @param {String} reason Reason why this filter is invalid |
| - * @constructor |
| - * @augments Filter |
| - */ |
| -function InvalidFilter(text, reason) |
| -{ |
| - Filter.call(this, text); |
| - |
| - this.reason = reason; |
| -} |
| -exports.InvalidFilter = InvalidFilter; |
| - |
| -InvalidFilter.prototype = |
| -{ |
| - __proto__: Filter.prototype, |
| - |
| - type: "invalid", |
| - |
| - /** |
| - * Reason why this filter is invalid |
| - * @type String |
| - */ |
| - reason: null, |
| - |
| - /** |
| - * See Filter.serialize() |
| - */ |
| - serialize: function(buffer) {} |
| -}; |
| - |
| -/** |
| - * Class for comments |
| - * @param {String} text see Filter() |
| - * @constructor |
| - * @augments Filter |
| - */ |
| -function CommentFilter(text) |
| -{ |
| - Filter.call(this, text); |
| -} |
| -exports.CommentFilter = CommentFilter; |
| - |
| -CommentFilter.prototype = |
| -{ |
| - __proto__: Filter.prototype, |
| - |
| - type: "comment", |
| - |
| - /** |
| - * See Filter.serialize() |
| - */ |
| - serialize: function(buffer) {} |
| -}; |
| - |
| -/** |
| - * Abstract base class for filters that can get hits |
| - * @param {String} text see Filter() |
| - * @param {String} [domains] Domains that the filter is restricted to separated by domainSeparator e.g. "foo.com|bar.com|~baz.com" |
| - * @constructor |
| - * @augments Filter |
| - */ |
| -function ActiveFilter(text, domains) |
| -{ |
| - Filter.call(this, text); |
| - |
| - this.domainSource = domains; |
| -} |
| -exports.ActiveFilter = ActiveFilter; |
| - |
| -ActiveFilter.prototype = |
| -{ |
| - __proto__: Filter.prototype, |
| - |
| - _disabled: false, |
| - _hitCount: 0, |
| - _lastHit: 0, |
| - |
| - /** |
| - * Defines whether the filter is disabled |
| - * @type Boolean |
| - */ |
| - get disabled() |
| - { |
| - return this._disabled; |
| - }, |
| - set disabled(value) |
| - { |
| - if (value != this._disabled) |
| - { |
| - let oldValue = this._disabled; |
| - this._disabled = value; |
| - FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); |
| - } |
| - return this._disabled; |
| - }, |
| - |
| - /** |
| - * Number of hits on the filter since the last reset |
| - * @type Number |
| - */ |
| - get hitCount() |
| - { |
| - return this._hitCount; |
| - }, |
| - set hitCount(value) |
| - { |
| - if (value != this._hitCount) |
| - { |
| - let oldValue = this._hitCount; |
| - this._hitCount = value; |
| - FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); |
| - } |
| - return this._hitCount; |
| - }, |
| - |
| - /** |
| - * Last time the filter had a hit (in milliseconds since the beginning of the epoch) |
| - * @type Number |
| - */ |
| - get lastHit() |
| - { |
| - return this._lastHit; |
| - }, |
| - set lastHit(value) |
| - { |
| - if (value != this._lastHit) |
| - { |
| - let oldValue = this._lastHit; |
| - this._lastHit = value; |
| - FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); |
| - } |
| - return this._lastHit; |
| - }, |
| - |
| - /** |
| - * String that the domains property should be generated from |
| - * @type String |
| - */ |
| - domainSource: null, |
| - |
| - /** |
| - * Separator character used in domainSource property, must be overridden by subclasses |
| - * @type String |
| - */ |
| - domainSeparator: null, |
| - |
| - /** |
| - * Determines whether the trailing dot in domain names isn't important and |
| - * should be ignored, must be overridden by subclasses. |
| - * @type Boolean |
| - */ |
| - ignoreTrailingDot: true, |
| - |
| - /** |
| - * Determines whether domainSource is already upper-case, |
| - * can be overridden by subclasses. |
| - * @type Boolean |
| - */ |
| - domainSourceIsUpperCase: false, |
| - |
| - /** |
| - * Map containing domains that this filter should match on/not match on or null if the filter should match on all domains |
| - * @type Object |
| - */ |
| - get domains() |
| - { |
| - // Despite this property being cached, the getter is called |
| - // several times on Safari, due to WebKit bug 132872 |
| - let prop = Object.getOwnPropertyDescriptor(this, "domains"); |
| - if (prop) |
| - return prop.value; |
| - |
| - let domains = null; |
| - |
| - if (this.domainSource) |
| - { |
| - let source = this.domainSource; |
| - if (!this.domainSourceIsUpperCase) { |
| - // RegExpFilter already have uppercase domains |
| - source = source.toUpperCase(); |
| - } |
| - let list = source.split(this.domainSeparator); |
| - if (list.length == 1 && list[0][0] != "~") |
| - { |
| - // Fast track for the common one-domain scenario |
| - domains = {__proto__: null, "": false}; |
| - if (this.ignoreTrailingDot) |
| - list[0] = list[0].replace(/\.+$/, ""); |
| - domains[list[0]] = true; |
| - } |
| - else |
| - { |
| - let hasIncludes = false; |
| - for (let i = 0; i < list.length; i++) |
| - { |
| - let domain = list[i]; |
| - if (this.ignoreTrailingDot) |
| - domain = domain.replace(/\.+$/, ""); |
| - if (domain == "") |
| - continue; |
| - |
| - let include; |
| - if (domain[0] == "~") |
| - { |
| - include = false; |
| - domain = domain.substr(1); |
| - } |
| - else |
| - { |
| - include = true; |
| - hasIncludes = true; |
| - } |
| - |
| - if (!domains) |
| - domains = Object.create(null); |
| - |
| - domains[domain] = include; |
| - } |
| - domains[""] = !hasIncludes; |
| - } |
| - |
| - this.domainSource = null; |
| - } |
| - |
| - Object.defineProperty(this, "domains", {value: domains, enumerable: true}); |
| - return this.domains; |
| - }, |
| - |
| - /** |
| - * Array containing public keys of websites that this filter should apply to |
| - * @type string[] |
| - */ |
| - sitekeys: null, |
| - |
| - /** |
| - * Checks whether this filter is active on a domain. |
| - * @param {String} docDomain domain name of the document that loads the URL |
| - * @param {String} [sitekey] public key provided by the document |
| - * @return {Boolean} true in case of the filter being active |
| - */ |
| - isActiveOnDomain: function(docDomain, sitekey) |
| - { |
| - // Sitekeys are case-sensitive so we shouldn't convert them to upper-case to avoid false |
| - // positives here. Instead we need to change the way filter options are parsed. |
| - if (this.sitekeys && (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase()) < 0)) |
| - return false; |
| - |
| - // If no domains are set the rule matches everywhere |
| - if (!this.domains) |
| - return true; |
| - |
| - // If the document has no host name, match only if the filter isn't restricted to specific domains |
| - if (!docDomain) |
| - return this.domains[""]; |
| - |
| - if (this.ignoreTrailingDot) |
| - docDomain = docDomain.replace(/\.+$/, ""); |
| - docDomain = docDomain.toUpperCase(); |
| - |
| - while (true) |
| - { |
| - if (docDomain in this.domains) |
| - return this.domains[docDomain]; |
| - |
| - let nextDot = docDomain.indexOf("."); |
| - if (nextDot < 0) |
| - break; |
| - docDomain = docDomain.substr(nextDot + 1); |
| - } |
| - return this.domains[""]; |
| - }, |
| - |
| - /** |
| - * Checks whether this filter is active only on a domain and its subdomains. |
| - */ |
| - isActiveOnlyOnDomain: function(/**String*/ docDomain) /**Boolean*/ |
| - { |
| - if (!docDomain || !this.domains || this.domains[""]) |
| - return false; |
| - |
| - if (this.ignoreTrailingDot) |
| - docDomain = docDomain.replace(/\.+$/, ""); |
| - docDomain = docDomain.toUpperCase(); |
| - |
| - for (let domain in this.domains) |
| - if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) |
| - return false; |
| - |
| - return true; |
| - }, |
| - |
| - /** |
| - * Checks whether this filter is generic or specific |
| - */ |
| - isGeneric: function() /**Boolean*/ |
| - { |
| - return !(this.sitekeys && this.sitekeys.length) && |
| - (!this.domains || this.domains[""]); |
| - }, |
| - |
| - /** |
| - * See Filter.serialize() |
| - */ |
| - serialize: function(buffer) |
| - { |
| - if (this._disabled || this._hitCount || this._lastHit) |
| - { |
| - Filter.prototype.serialize.call(this, buffer); |
| - if (this._disabled) |
| - buffer.push("disabled=true"); |
| - if (this._hitCount) |
| - buffer.push("hitCount=" + this._hitCount); |
| - if (this._lastHit) |
| - buffer.push("lastHit=" + this._lastHit); |
| - } |
| - } |
| -}; |
| - |
| -/** |
| - * Abstract base class for RegExp-based filters |
| - * @param {String} text see Filter() |
| - * @param {String} regexpSource filter part that the regular expression should be build from |
| - * @param {Number} [contentType] Content types the filter applies to, combination of values from RegExpFilter.typeMap |
| - * @param {Boolean} [matchCase] Defines whether the filter should distinguish between lower and upper case 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 |
| - * @augments ActiveFilter |
| - */ |
| -function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) |
| -{ |
| - ActiveFilter.call(this, text, domains, sitekeys); |
| - |
| - if (contentType != null) |
| - this.contentType = contentType; |
| - if (matchCase) |
| - this.matchCase = matchCase; |
| - if (thirdParty != null) |
| - this.thirdParty = thirdParty; |
| - if (sitekeys != null) |
| - this.sitekeySource = sitekeys; |
| - |
| - if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpSource.length - 1] == "/") |
| - { |
| - // The filter is a regular expression - convert it immediately to catch syntax errors |
| - 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.regexpSource = regexpSource; |
| - } |
| -} |
| -exports.RegExpFilter = RegExpFilter; |
| - |
| -RegExpFilter.prototype = |
| -{ |
| - __proto__: ActiveFilter.prototype, |
| - |
| - /** |
| - * @see ActiveFilter.domainSourceIsUpperCase |
| - */ |
| - domainSourceIsUpperCase: true, |
| - |
| - /** |
| - * Number of filters contained, will always be 1 (required to optimize Matcher). |
| - * @type Integer |
| - */ |
| - length: 1, |
| - |
| - /** |
| - * @see ActiveFilter.domainSeparator |
| - */ |
| - domainSeparator: "|", |
| - |
| - /** |
| - * Expression from which a regular expression should be generated - for delayed creation of the regexp property |
| - * @type String |
| - */ |
| - regexpSource: null, |
| - /** |
| - * Regular expression to be used when testing against this filter |
| - * @type RegExp |
| - */ |
| - get regexp() |
| - { |
| - // Despite this property being cached, the getter is called |
| - // several times on Safari, due to WebKit bug 132872 |
| - let prop = Object.getOwnPropertyDescriptor(this, "regexp"); |
| - if (prop) |
| - return prop.value; |
| - |
| - let source = Filter.toRegExp(this.regexpSource); |
| - let regexp = new RegExp(source, this.matchCase ? "" : "i"); |
| - Object.defineProperty(this, "regexp", {value: regexp}); |
| - return regexp; |
| - }, |
| - /** |
| - * Content types the filter applies to, combination of values from RegExpFilter.typeMap |
| - * @type Number |
| - */ |
| - contentType: 0x7FFFFFFF, |
| - /** |
| - * Defines whether the filter should distinguish between lower and upper case letters |
| - * @type Boolean |
| - */ |
| - matchCase: false, |
| - /** |
| - * Defines whether the filter should apply to third-party or first-party content only. Can be null (apply to all content). |
| - * @type Boolean |
| - */ |
| - thirdParty: null, |
| - |
| - /** |
| - * String that the sitekey property should be generated from |
| - * @type String |
| - */ |
| - sitekeySource: null, |
| - |
| - /** |
| - * Array containing public keys of websites that this filter should apply to |
| - * @type string[] |
| - */ |
| - get sitekeys() |
| - { |
| - // Despite this property being cached, the getter is called |
| - // several times on Safari, due to WebKit bug 132872 |
| - let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); |
| - if (prop) |
| - return prop.value; |
| - |
| - let sitekeys = null; |
| - |
| - if (this.sitekeySource) |
| - { |
| - sitekeys = this.sitekeySource.split("|"); |
| - this.sitekeySource = null; |
| - } |
| - |
| - Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true}); |
| - return this.sitekeys; |
| - }, |
| - |
| - /** |
| - * Tests whether the URL matches this filter |
| - * @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 |
| - * @return {Boolean} true in case of a match |
| - */ |
| - matches: function(location, typeMask, docDomain, thirdParty, sitekey) |
| - { |
| - if (this.contentType & typeMask && |
| - (this.thirdParty == null || this.thirdParty == thirdParty) && |
| - this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) |
| - { |
| - return true; |
| - } |
| - |
| - return false; |
| - } |
| -}; |
| - |
| -// Required to optimize Matcher, see also RegExpFilter.prototype.length |
| -Object.defineProperty(RegExpFilter.prototype, "0", |
| -{ |
| - get: function() { return this; } |
| -}); |
| - |
| -/** |
| - * Creates a RegExp filter from its text representation |
| - * @param {String} text same as in Filter() |
| - */ |
| -RegExpFilter.fromText = function(text) |
| -{ |
| - let blocking = true; |
| - let origText = text; |
| - if (text.indexOf("@@") == 0) |
| - { |
| - blocking = false; |
| - text = text.substr(2); |
| - } |
| - |
| - let contentType = null; |
| - let matchCase = null; |
| - let domains = null; |
| - let sitekeys = null; |
| - let thirdParty = null; |
| - let collapse = null; |
| - let options; |
| - let match = (text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null); |
| - if (match) |
| - { |
| - options = match[1].toUpperCase().split(","); |
| - text = match.input.substr(0, match.index); |
| - for (let option of options) |
| - { |
| - let value = null; |
| - let separatorIndex = option.indexOf("="); |
| - if (separatorIndex >= 0) |
| - { |
| - value = option.substr(separatorIndex + 1); |
| - option = option.substr(0, separatorIndex); |
| - } |
| - option = option.replace(/-/, "_"); |
| - if (option in RegExpFilter.typeMap) |
| - { |
| - if (contentType == null) |
| - contentType = 0; |
| - contentType |= RegExpFilter.typeMap[option]; |
| - } |
| - else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) |
| - { |
| - if (contentType == null) |
| - contentType = RegExpFilter.prototype.contentType; |
| - contentType &= ~RegExpFilter.typeMap[option.substr(1)]; |
| - } |
| - else if (option == "MATCH_CASE") |
| - matchCase = true; |
| - else if (option == "~MATCH_CASE") |
| - matchCase = false; |
| - else if (option == "DOMAIN" && typeof value != "undefined") |
| - domains = value; |
| - else if (option == "THIRD_PARTY") |
| - thirdParty = true; |
| - else if (option == "~THIRD_PARTY") |
| - thirdParty = false; |
| - else if (option == "COLLAPSE") |
| - collapse = true; |
| - else if (option == "~COLLAPSE") |
| - collapse = false; |
| - else if (option == "SITEKEY" && typeof value != "undefined") |
| - sitekeys = value; |
| - else |
| - return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); |
| - } |
| - } |
| - |
| - try |
| - { |
| - if (blocking) |
| - return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); |
| - else |
| - return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); |
| - } |
| - catch (e) |
| - { |
| - return new InvalidFilter(origText, e); |
| - } |
| -}; |
| - |
| -/** |
| - * Maps type strings like "SCRIPT" or "OBJECT" to bit masks |
| - */ |
| -RegExpFilter.typeMap = { |
| - OTHER: 1, |
| - SCRIPT: 2, |
| - IMAGE: 4, |
| - STYLESHEET: 8, |
| - OBJECT: 16, |
| - SUBDOCUMENT: 32, |
| - DOCUMENT: 64, |
| - XBL: 1, |
| - PING: 1024, |
| - XMLHTTPREQUEST: 2048, |
| - OBJECT_SUBREQUEST: 4096, |
| - DTD: 1, |
| - MEDIA: 16384, |
| - FONT: 32768, |
| - |
| - BACKGROUND: 4, // Backwards compat, same as IMAGE |
| - |
| - POPUP: 0x10000000, |
| - GENERICBLOCK: 0x20000000, |
| - ELEMHIDE: 0x40000000, |
| - GENERICHIDE: 0x80000000 |
| -}; |
| - |
| -// DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't |
| -// be there by default |
| -RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.DOCUMENT | |
| - RegExpFilter.typeMap.ELEMHIDE | |
| - RegExpFilter.typeMap.POPUP | |
| - RegExpFilter.typeMap.GENERICHIDE | |
| - RegExpFilter.typeMap.GENERICBLOCK); |
| - |
| -/** |
| - * Class for blocking filters |
| - * @param {String} text see Filter() |
| - * @param {String} regexpSource see RegExpFilter() |
| - * @param {Number} contentType see RegExpFilter() |
| - * @param {Boolean} matchCase see RegExpFilter() |
| - * @param {String} domains see RegExpFilter() |
| - * @param {Boolean} thirdParty see RegExpFilter() |
| - * @param {String} sitekeys see RegExpFilter() |
| - * @param {Boolean} collapse defines whether the filter should collapse blocked content, can be null |
| - * @constructor |
| - * @augments RegExpFilter |
| - */ |
| -function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) |
| -{ |
| - RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); |
| - |
| - this.collapse = collapse; |
| -} |
| -exports.BlockingFilter = BlockingFilter; |
| - |
| -BlockingFilter.prototype = |
| -{ |
| - __proto__: RegExpFilter.prototype, |
| - |
| - type: "blocking", |
| - |
| - /** |
| - * Defines whether the filter should collapse blocked content. Can be null (use the global preference). |
| - * @type Boolean |
| - */ |
| - collapse: null |
| -}; |
| - |
| -/** |
| - * Class for whitelist filters |
| - * @param {String} text see Filter() |
| - * @param {String} regexpSource see RegExpFilter() |
| - * @param {Number} contentType see RegExpFilter() |
| - * @param {Boolean} matchCase see RegExpFilter() |
| - * @param {String} domains see RegExpFilter() |
| - * @param {Boolean} thirdParty see RegExpFilter() |
| - * @param {String} sitekeys see RegExpFilter() |
| - * @constructor |
| - * @augments RegExpFilter |
| - */ |
| -function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) |
| -{ |
| - RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); |
| -} |
| -exports.WhitelistFilter = WhitelistFilter; |
| - |
| -WhitelistFilter.prototype = |
| -{ |
| - __proto__: RegExpFilter.prototype, |
| - |
| - type: "whitelist" |
| -}; |
| - |
| -/** |
| - * Base class for element hiding filters |
| - * @param {String} text see Filter() |
| - * @param {String} [domains] Host names or domains the filter should be restricted to |
| - * @param {String} selector CSS selector for the HTML elements that should be hidden |
| - * @constructor |
| - * @augments ActiveFilter |
| - */ |
| -function ElemHideBase(text, domains, selector) |
| -{ |
| - ActiveFilter.call(this, text, domains || null); |
| - |
| - if (domains) |
| - this.selectorDomain = domains.replace(/,~[^,]+/g, "").replace(/^~[^,]+,?/, "").toLowerCase(); |
| - this.selector = selector; |
| -} |
| -exports.ElemHideBase = ElemHideBase; |
| - |
| -ElemHideBase.prototype = |
| -{ |
| - __proto__: ActiveFilter.prototype, |
| - |
| - /** |
| - * @see ActiveFilter.domainSeparator |
| - */ |
| - domainSeparator: ",", |
| - |
| - /** |
| - * @see ActiveFilter.ignoreTrailingDot |
| - */ |
| - ignoreTrailingDot: false, |
| - |
| - /** |
| - * Host name or domain the filter should be restricted to (can be null for no restriction) |
| - * @type String |
| - */ |
| - selectorDomain: null, |
| - /** |
| - * CSS selector for the HTML elements that should be hidden |
| - * @type String |
| - */ |
| - selector: null |
| -}; |
| - |
| -/** |
| - * Creates an element hiding filter from a pre-parsed text representation |
| - * |
| - * @param {String} text same as in Filter() |
| - * @param {String} domain domain part of the text representation (can be empty) |
| - * @param {Boolean} isException exception rule indicator |
| - * @param {String} tagName tag name part (can be empty) |
| - * @param {String} attrRules attribute matching rules (can be empty) |
| - * @param {String} selector raw CSS selector (can be empty) |
| - * @return {ElemHideFilter|ElemHideException|CSSPropertyFilter|InvalidFilter} |
| - */ |
| -ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector) |
| -{ |
| - if (!selector) |
| - { |
| - if (tagName == "*") |
| - tagName = ""; |
| - |
| - let id = null; |
| - let additional = ""; |
| - if (attrRules) |
| - { |
| - attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); |
| - for (let rule of attrRules) |
| - { |
| - rule = rule.substr(1, rule.length - 2); |
| - let separatorPos = rule.indexOf("="); |
| - if (separatorPos > 0) |
| - { |
| - rule = rule.replace(/=/, '="') + '"'; |
| - additional += "[" + rule + "]"; |
| - } |
| - else |
| - { |
| - if (id) |
| - return new InvalidFilter(text, Utils.getString("filter_elemhide_duplicate_id")); |
| - |
| - id = rule; |
| - } |
| - } |
| - } |
| - |
| - if (id) |
| - selector = tagName + "." + id + additional + "," + tagName + "#" + id + additional; |
| - else if (tagName || additional) |
| - selector = tagName + additional; |
| - else |
| - return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria")); |
| - } |
| - |
| - if (isException) |
| - return new ElemHideException(text, domain, selector); |
| - |
| - let match = Filter.csspropertyRegExp.exec(selector); |
| - if (match) |
| - { |
| - // CSS property filters are inefficient so we need to make sure that |
| - // they're only applied if they specify active domains |
| - if (!/,[^~][^,.]*\.[^,]/.test("," + domain)) |
| - return new InvalidFilter(text, Utils.getString("filter_cssproperty_nodomain")); |
| - |
| - return new CSSPropertyFilter(text, domain, selector, match[2], |
| - selector.substr(0, match.index), |
| - selector.substr(match.index + match[0].length)); |
| - } |
| - |
| - return new ElemHideFilter(text, domain, selector); |
| -}; |
| - |
| -/** |
| - * Class for element hiding filters |
| - * @param {String} text see Filter() |
| - * @param {String} domains see ElemHideBase() |
| - * @param {String} selector see ElemHideBase() |
| - * @constructor |
| - * @augments ElemHideBase |
| - */ |
| -function ElemHideFilter(text, domains, selector) |
| -{ |
| - ElemHideBase.call(this, text, domains, selector); |
| -} |
| -exports.ElemHideFilter = ElemHideFilter; |
| - |
| -ElemHideFilter.prototype = |
| -{ |
| - __proto__: ElemHideBase.prototype, |
| - |
| - type: "elemhide" |
| -}; |
| - |
| -/** |
| - * Class for element hiding exceptions |
| - * @param {String} text see Filter() |
| - * @param {String} domains see ElemHideBase() |
| - * @param {String} selector see ElemHideBase() |
| - * @constructor |
| - * @augments ElemHideBase |
| - */ |
| -function ElemHideException(text, domains, selector) |
| -{ |
| - ElemHideBase.call(this, text, domains, selector); |
| -} |
| -exports.ElemHideException = ElemHideException; |
| - |
| -ElemHideException.prototype = |
| -{ |
| - __proto__: ElemHideBase.prototype, |
| - |
| - type: "elemhideexception" |
| -}; |
| - |
| -/** |
| - * Class for CSS property filters |
| - * @param {String} text see Filter() |
| - * @param {String} domains see ElemHideBase() |
| - * @param {String} selector see ElemHideBase() |
| - * @param {String} regexpSource see CSSPropertyFilter.regexpSource |
| - * @param {String} selectorPrefix see CSSPropertyFilter.selectorPrefix |
| - * @param {String} selectorSuffix see CSSPropertyFilter.selectorSuffix |
| - * @constructor |
| - * @augments ElemHideBase |
| - */ |
| -function CSSPropertyFilter(text, domains, selector, regexpSource, |
| - selectorPrefix, selectorSuffix) |
| -{ |
| - ElemHideBase.call(this, text, domains, selector); |
| - |
| - this.regexpSource = regexpSource; |
| - this.selectorPrefix = selectorPrefix; |
| - this.selectorSuffix = selectorSuffix; |
| -} |
| -exports.CSSPropertyFilter = CSSPropertyFilter; |
| - |
| -CSSPropertyFilter.prototype = |
| -{ |
| - __proto__: ElemHideBase.prototype, |
| - |
| - type: "cssproperty", |
| - |
| - /** |
| - * Expression from which a regular expression should be generated for matching |
| - * CSS properties - for delayed creation of the regexpString property |
| - * @type String |
| - */ |
| - regexpSource: null, |
| - /** |
| - * Substring of CSS selector before properties for the HTML elements that |
| - * should be hidden |
| - * @type String |
| - */ |
| - selectorPrefix: null, |
| - /** |
| - * Substring of CSS selector after properties for the HTML elements that |
| - * should be hidden |
| - * @type String |
| - */ |
| - selectorSuffix: null, |
| - |
| - /** |
| - * Raw regular expression string to be used when testing CSS properties |
| - * against this filter |
| - * @type String |
| - */ |
| - get regexpString() |
| - { |
| - // Despite this property being cached, the getter is called |
| - // several times on Safari, due to WebKit bug 132872 |
| - let prop = Object.getOwnPropertyDescriptor(this, "regexpString"); |
| - if (prop) |
| - return prop.value; |
| - |
| - let regexp = Filter.toRegExp(this.regexpSource); |
| - Object.defineProperty(this, "regexpString", {value: regexp}); |
| - return regexp; |
| - } |
| -}; |