| Index: lib/filterClasses.js | 
| diff --git a/lib/filterClasses.js b/lib/filterClasses.js | 
| index 5a8ac6d77bf116ace79ceebbbf3df1b02dc11fc6..b8be92b981f5631bf690edc28110c75527796142 100644 | 
| --- a/lib/filterClasses.js | 
| +++ b/lib/filterClasses.js | 
| @@ -20,6 +20,7 @@ | 
| */ | 
|  | 
| let {FilterNotifier} = require("filterNotifier"); | 
| +let {desc} = require("coreUtils"); | 
|  | 
| /** | 
| * Abstract base class for filters | 
| @@ -210,10 +211,7 @@ function InvalidFilter(text, reason) | 
| } | 
| exports.InvalidFilter = InvalidFilter; | 
|  | 
| -InvalidFilter.prototype = | 
| -{ | 
| -  __proto__: Filter.prototype, | 
| - | 
| +InvalidFilter.prototype = Object.create(Filter.prototype, desc({ | 
| type: "invalid", | 
|  | 
| /** | 
| @@ -226,7 +224,7 @@ InvalidFilter.prototype = | 
| * See Filter.serialize() | 
| */ | 
| serialize: function(buffer) {} | 
| -}; | 
| +})); | 
|  | 
| /** | 
| * Class for comments | 
| @@ -240,17 +238,14 @@ function CommentFilter(text) | 
| } | 
| exports.CommentFilter = CommentFilter; | 
|  | 
| -CommentFilter.prototype = | 
| -{ | 
| -  __proto__: Filter.prototype, | 
| - | 
| +CommentFilter.prototype = Object.create(Filter.prototype, desc({ | 
| type: "comment", | 
|  | 
| /** | 
| * See Filter.serialize() | 
| */ | 
| serialize: function(buffer) {} | 
| -}; | 
| +})); | 
|  | 
| /** | 
| * Abstract base class for filters that can get hits | 
| @@ -267,10 +262,7 @@ function ActiveFilter(text, domains) | 
| } | 
| exports.ActiveFilter = ActiveFilter; | 
|  | 
| -ActiveFilter.prototype = | 
| -{ | 
| -  __proto__: Filter.prototype, | 
| - | 
| +ActiveFilter.prototype = Object.create(Filter.prototype, desc({ | 
| _disabled: false, | 
| _hitCount: 0, | 
| _lastHit: 0, | 
| @@ -279,57 +271,66 @@ ActiveFilter.prototype = | 
| * Defines whether the filter is disabled | 
| * @type Boolean | 
| */ | 
| -  get disabled() | 
| -  { | 
| -    return this._disabled; | 
| -  }, | 
| -  set disabled(value) | 
| -  { | 
| -    if (value != this._disabled) | 
| +  disabled: { | 
| +    get: function() | 
| { | 
| -      let oldValue = this._disabled; | 
| -      this._disabled = value; | 
| -      FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); | 
| +      return this._disabled; | 
| +    }, | 
| +    set: function(value) | 
| +    { | 
| +      if (value != this._disabled) | 
| +      { | 
| +        let oldValue = this._disabled; | 
| +        this._disabled = value; | 
| +        FilterNotifier.triggerListeners("filter.disabled", this, | 
| +        value, oldValue); | 
| +      } | 
| +      return this._disabled; | 
| } | 
| -    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) | 
| +  hitCount: { | 
| +    get: function() | 
| { | 
| -      let oldValue = this._hitCount; | 
| -      this._hitCount = value; | 
| -      FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); | 
| +      return this._hitCount; | 
| +    }, | 
| +    set: function(value) | 
| +    { | 
| +      if (value != this._hitCount) | 
| +      { | 
| +        let oldValue = this._hitCount; | 
| +        this._hitCount = value; | 
| +        FilterNotifier.triggerListeners("filter.hitCount", this, | 
| +        value, oldValue); | 
| +      } | 
| +      return this._hitCount; | 
| } | 
| -    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) | 
| +  lastHit: { | 
| +    get: function() | 
| +    { | 
| +      return this._lastHit; | 
| +    }, | 
| +    set: function(value) | 
| { | 
| -      let oldValue = this._lastHit; | 
| -      this._lastHit = value; | 
| -      FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); | 
| +      if (value != this._lastHit) | 
| +      { | 
| +        let oldValue = this._lastHit; | 
| +        this._lastHit = value; | 
| +        FilterNotifier.triggerListeners("filter.lastHit", this, | 
| +        value, oldValue); | 
| +      } | 
| +      return this._lastHit; | 
| } | 
| -    return this._lastHit; | 
| }, | 
|  | 
| /** | 
| @@ -362,69 +363,71 @@ ActiveFilter.prototype = | 
| * 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; | 
| +  domains: { | 
| +    get: function() | 
| +    { | 
| +      // 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; | 
| +      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 = Object.create(null); | 
| -        domains[""] = false; | 
| -        if (this.ignoreTrailingDot) | 
| -          list[0] = list[0].replace(/\.+$/, ""); | 
| -        domains[list[0]] = true; | 
| -      } | 
| -      else | 
| +      if (this.domainSource) | 
| { | 
| -        let hasIncludes = false; | 
| -        for (let i = 0; i < list.length; i++) | 
| +        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] != "~") | 
| { | 
| -          let domain = list[i]; | 
| +          // Fast track for the common one-domain scenario | 
| +          domains = Object.create(null); | 
| +          domains[""] = false; | 
| if (this.ignoreTrailingDot) | 
| -            domain = domain.replace(/\.+$/, ""); | 
| -          if (domain == "") | 
| -            continue; | 
| - | 
| -          let include; | 
| -          if (domain[0] == "~") | 
| -          { | 
| -            include = false; | 
| -            domain = domain.substr(1); | 
| -          } | 
| -          else | 
| +            list[0] = list[0].replace(/\.+$/, ""); | 
| +          domains[list[0]] = true; | 
| +        } | 
| +        else | 
| +        { | 
| +          let hasIncludes = false; | 
| +          for (let i = 0; i < list.length; i++) | 
| { | 
| -            include = true; | 
| -            hasIncludes = true; | 
| +            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; | 
| } | 
| - | 
| -          if (!domains) | 
| -            domains = Object.create(null); | 
| - | 
| -          domains[domain] = include; | 
| +          domains[""] = !hasIncludes; | 
| } | 
| -        domains[""] = !hasIncludes; | 
| + | 
| +        this.domainSource = null; | 
| } | 
|  | 
| -      this.domainSource = null; | 
| +      Object.defineProperty(this, "domains", {value: domains, enumerable: true}); | 
| +      return this.domains; | 
| } | 
| - | 
| -    Object.defineProperty(this, "domains", {value: domains, enumerable: true}); | 
| -    return this.domains; | 
| }, | 
|  | 
| /** | 
| @@ -515,7 +518,7 @@ ActiveFilter.prototype = | 
| buffer.push("lastHit=" + this._lastHit); | 
| } | 
| } | 
| -}; | 
| +})); | 
|  | 
| /** | 
| * Abstract base class for RegExp-based filters | 
| @@ -556,10 +559,7 @@ function RegExpFilter(text, regexpSource, contentType, matchCase, domains, third | 
| } | 
| exports.RegExpFilter = RegExpFilter; | 
|  | 
| -RegExpFilter.prototype = | 
| -{ | 
| -  __proto__: ActiveFilter.prototype, | 
| - | 
| +RegExpFilter.prototype = Object.create(ActiveFilter.prototype, desc({ | 
| /** | 
| * @see ActiveFilter.domainSourceIsUpperCase | 
| */ | 
| @@ -585,19 +585,22 @@ RegExpFilter.prototype = | 
| * 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; | 
| +  regexp: { | 
| +    get: function() | 
| +    { | 
| +      // 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 | 
| @@ -624,24 +627,26 @@ RegExpFilter.prototype = | 
| * 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; | 
| +  sitekeys: { | 
| +    get: function() | 
| +    { | 
| +      // 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; | 
| +      let sitekeys = null; | 
|  | 
| -    if (this.sitekeySource) | 
| -    { | 
| -      sitekeys = this.sitekeySource.split("|"); | 
| -      this.sitekeySource = null; | 
| -    } | 
| +      if (this.sitekeySource) | 
| +      { | 
| +        sitekeys = this.sitekeySource.split("|"); | 
| +        this.sitekeySource = null; | 
| +      } | 
|  | 
| -    Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true}); | 
| -    return this.sitekeys; | 
| +      Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true}); | 
| +      return this.sitekeys; | 
| +    } | 
| }, | 
|  | 
| /** | 
| @@ -663,14 +668,13 @@ RegExpFilter.prototype = | 
| } | 
|  | 
| return false; | 
| -  } | 
| -}; | 
| +  }, | 
|  | 
| -// Required to optimize Matcher, see also RegExpFilter.prototype.length | 
| -Object.defineProperty(RegExpFilter.prototype, "0", | 
| -{ | 
| -  get: function() { return this; } | 
| -}); | 
| +  // Required to optimize Matcher, see also RegExpFilter.prototype.length | 
| +  0: { | 
| +    get: function() { return this; } | 
| +  } | 
| +})); | 
|  | 
| /** | 
| * Creates a RegExp filter from its text representation | 
| @@ -810,10 +814,7 @@ function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thi | 
| } | 
| exports.BlockingFilter = BlockingFilter; | 
|  | 
| -BlockingFilter.prototype = | 
| -{ | 
| -  __proto__: RegExpFilter.prototype, | 
| - | 
| +BlockingFilter.prototype = Object.create(RegExpFilter.prototype, desc({ | 
| type: "blocking", | 
|  | 
| /** | 
| @@ -821,7 +822,7 @@ BlockingFilter.prototype = | 
| * @type Boolean | 
| */ | 
| collapse: null | 
| -}; | 
| +})); | 
|  | 
| /** | 
| * Class for whitelist filters | 
| @@ -841,12 +842,9 @@ function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, th | 
| } | 
| exports.WhitelistFilter = WhitelistFilter; | 
|  | 
| -WhitelistFilter.prototype = | 
| -{ | 
| -  __proto__: RegExpFilter.prototype, | 
| - | 
| +WhitelistFilter.prototype = Object.create(RegExpFilter.prototype, desc({ | 
| type: "whitelist" | 
| -}; | 
| +})); | 
|  | 
| /** | 
| * Base class for element hiding filters | 
| @@ -866,10 +864,7 @@ function ElemHideBase(text, domains, selector) | 
| } | 
| exports.ElemHideBase = ElemHideBase; | 
|  | 
| -ElemHideBase.prototype = | 
| -{ | 
| -  __proto__: ActiveFilter.prototype, | 
| - | 
| +ElemHideBase.prototype = Object.create(ActiveFilter.prototype, desc({ | 
| /** | 
| * @see ActiveFilter.domainSeparator | 
| */ | 
| @@ -890,7 +885,7 @@ ElemHideBase.prototype = | 
| * @type String | 
| */ | 
| selector: null | 
| -}; | 
| +})); | 
|  | 
| /** | 
| * Creates an element hiding filter from a pre-parsed text representation | 
| @@ -975,12 +970,9 @@ function ElemHideFilter(text, domains, selector) | 
| } | 
| exports.ElemHideFilter = ElemHideFilter; | 
|  | 
| -ElemHideFilter.prototype = | 
| -{ | 
| -  __proto__: ElemHideBase.prototype, | 
| - | 
| +ElemHideFilter.prototype = Object.create(ElemHideBase.prototype, desc({ | 
| type: "elemhide" | 
| -}; | 
| +})); | 
|  | 
| /** | 
| * Class for element hiding exceptions | 
| @@ -996,12 +988,9 @@ function ElemHideException(text, domains, selector) | 
| } | 
| exports.ElemHideException = ElemHideException; | 
|  | 
| -ElemHideException.prototype = | 
| -{ | 
| -  __proto__: ElemHideBase.prototype, | 
| - | 
| +ElemHideException.prototype = Object.create(ElemHideBase.prototype, desc({ | 
| type: "elemhideexception" | 
| -}; | 
| +})); | 
|  | 
| /** | 
| * Class for CSS property filters | 
| @@ -1025,10 +1014,7 @@ function CSSPropertyFilter(text, domains, selector, regexpSource, | 
| } | 
| exports.CSSPropertyFilter = CSSPropertyFilter; | 
|  | 
| -CSSPropertyFilter.prototype = | 
| -{ | 
| -  __proto__: ElemHideBase.prototype, | 
| - | 
| +CSSPropertyFilter.prototype = Object.create(ElemHideBase.prototype, desc({ | 
| type: "cssproperty", | 
|  | 
| /** | 
| @@ -1055,16 +1041,18 @@ CSSPropertyFilter.prototype = | 
| * 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; | 
| +  regexpString: { | 
| +    get: function() | 
| +    { | 
| +      // 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; | 
| +    } | 
| } | 
| -}; | 
| +})); | 
|  |