| Index: lib/filterClasses.js | 
| =================================================================== | 
| --- a/lib/filterClasses.js | 
| +++ b/lib/filterClasses.js | 
| @@ -16,16 +16,17 @@ | 
| */ | 
|  | 
| /** | 
| * @fileOverview Definition of Filter class and its subclasses. | 
| */ | 
|  | 
| let {FilterNotifier} = require("filterNotifier"); | 
| let {extend} = require("coreUtils"); | 
| +let {elemHideEmulationFeatureMap, filterToRegExp} = require("common"); | 
|  | 
| /** | 
| * Abstract base class for filters | 
| * | 
| * @param {String} text   string representation of the filter | 
| * @constructor | 
| */ | 
| function Filter(text) | 
| @@ -90,22 +91,16 @@ | 
| * @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} | 
| */ | 
| @@ -171,35 +166,19 @@ | 
| 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 | 
| + * @see filterToRegExp | 
| */ | 
| -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 | 
| -} | 
| +Filter.toRegExp = filterToRegExp; | 
|  | 
| /** | 
| * Class for invalid filters | 
| * @param {String} text see Filter() | 
| * @param {String} reason Reason why this filter is invalid | 
| * @constructor | 
| * @augments Filter | 
| */ | 
| @@ -878,17 +857,17 @@ | 
| * 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} | 
| + * @return {ElemHideFilter|ElemHideException|ElemHideEmulationFilter|InvalidFilter} | 
| */ | 
| ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector) | 
| { | 
| if (!selector) | 
| { | 
| if (tagName == "*") | 
| tagName = ""; | 
|  | 
| @@ -928,27 +907,31 @@ | 
| // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that | 
| // changes this must be changed too. | 
| if (domain && /(^|,)~?(,|$)/.test(domain)) | 
| return new InvalidFilter(text, "filter_invalid_domain"); | 
|  | 
| if (isException) | 
| return new ElemHideException(text, domain, selector); | 
|  | 
| -  let match = Filter.csspropertyRegExp.exec(selector); | 
| -  if (match) | 
| +  let emulatedFeatures = 0; | 
| +  if (selector.indexOf("[-abp-properties") != -1) | 
| +    emulatedFeatures |= ElemHideEmulationFilter.featureMap.PROPERTY_SELECTOR; | 
| +  if (selector.indexOf(":has(") != -1) | 
| +    emulatedFeatures |= ElemHideEmulationFilter.featureMap.HAS_PSEUDO_CLASS; | 
| + | 
| +  if (emulatedFeatures != 0) | 
| { | 
| -    // CSS property filters are inefficient so we need to make sure that | 
| -    // they're only applied if they specify active domains | 
| +    // Element hiding emulation 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, "filter_cssproperty_nodomain"); | 
| +      return new InvalidFilter(text, "filter_elemhideemulation_nodomain"); | 
|  | 
| -    return new CSSPropertyFilter(text, domain, selector, match[2], | 
| -      selector.substr(0, match.index), | 
| -      selector.substr(match.index + match[0].length)); | 
| +    return new ElemHideEmulationFilter(text, domain, selector, | 
| +                                       emulatedFeatures); | 
| } | 
|  | 
| return new ElemHideFilter(text, domain, selector); | 
| }; | 
|  | 
| /** | 
| * Class for element hiding filters | 
| * @param {String} text see Filter() | 
| @@ -981,69 +964,39 @@ | 
| } | 
| exports.ElemHideException = ElemHideException; | 
|  | 
| ElemHideException.prototype = extend(ElemHideBase, { | 
| type: "elemhideexception" | 
| }); | 
|  | 
| /** | 
| - * Class for CSS property filters | 
| + * Class for element hiding emulation 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 | 
| + * @param {Integer} features      see ElemHideEmulationFilter.features | 
| * @constructor | 
| * @augments ElemHideBase | 
| */ | 
| -function CSSPropertyFilter(text, domains, selector, regexpSource, | 
| -  selectorPrefix, selectorSuffix) | 
| +function ElemHideEmulationFilter(text, domains, selector, features) | 
| { | 
| ElemHideBase.call(this, text, domains, selector); | 
|  | 
| -  this.regexpSource = regexpSource; | 
| -  this.selectorPrefix = selectorPrefix; | 
| -  this.selectorSuffix = selectorSuffix; | 
| +  this.features = features; | 
| } | 
| -exports.CSSPropertyFilter = CSSPropertyFilter; | 
| +exports.ElemHideEmulationFilter = ElemHideEmulationFilter; | 
|  | 
| -CSSPropertyFilter.prototype = extend(ElemHideBase, { | 
| -  type: "cssproperty", | 
| +ElemHideEmulationFilter.prototype = extend(ElemHideBase, { | 
| +  type: "elemhideemulation", | 
|  | 
| /** | 
| -   * Expression from which a regular expression should be generated for matching | 
| -   * CSS properties - for delayed creation of the regexpString property | 
| -   * @type String | 
| +   * Features used in this filter, combination of values from | 
| +   * ElemHideEmulationFilter.featureMap | 
| +   * @type Integer | 
| */ | 
| -  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, | 
| +  features: 0 | 
| +}); | 
|  | 
| -  /** | 
| -   * 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; | 
| -  } | 
| -}); | 
| +/** | 
| + * @see elemHideEmulationFeatureMap | 
| + */ | 
| +ElemHideEmulationFilter.featureMap = elemHideEmulationFeatureMap; | 
|  |