| Index: lib/elemHide.js | 
| =================================================================== | 
| --- a/lib/elemHide.js | 
| +++ b/lib/elemHide.js | 
| @@ -16,17 +16,17 @@ | 
| */ | 
|  | 
| "use strict"; | 
|  | 
| /** | 
| * @fileOverview Element hiding implementation. | 
| */ | 
|  | 
| -const {ElemHideException} = require("./filterClasses"); | 
| +const {ElemHideExceptions} = require("./elemHideExceptions"); | 
| const {FilterNotifier} = require("./filterNotifier"); | 
|  | 
| /** | 
| * Lookup table, active flag, by filter by domain. | 
| * (Only contains filters that aren't unconditionally matched for all domains.) | 
| * @type {Map.<string,Map.<Filter,boolean>>} | 
| */ | 
| let filtersByDomain = new Map(); | 
| @@ -49,28 +49,22 @@ | 
| /** | 
| * Map to be used instead when a filter has a blank domains property. | 
| * @type {Map.<string,boolean>} | 
| * @const | 
| */ | 
| let defaultDomains = new Map([["", true]]); | 
|  | 
| /** | 
| - * Set containing known element hiding and exception filters | 
| - * @type {Set.<ElemHideBase>} | 
| + * Set containing known element hiding filters | 
| + * @type {Set.<ElemHideFilter>} | 
| */ | 
| let knownFilters = new Set(); | 
|  | 
| /** | 
| - * Lookup table, lists of element hiding exceptions by selector | 
| - * @type {Map.<string,Filter[]>} | 
| - */ | 
| -let exceptions = new Map(); | 
| - | 
| -/** | 
| * Adds a filter to the lookup table of filters by domain. | 
| * @param {Filter} filter | 
| */ | 
| function addToFiltersByDomain(filter) | 
| { | 
| let domains = filter.domains || defaultDomains; | 
| for (let [domain, isIncluded] of domains) | 
| { | 
| @@ -92,66 +86,58 @@ | 
| function getUnconditionalSelectors() | 
| { | 
| if (!unconditionalSelectors) | 
| unconditionalSelectors = [...filterBySelector.keys()]; | 
|  | 
| return unconditionalSelectors; | 
| } | 
|  | 
| +ElemHideExceptions.on("added", ({selector}) => | 
| +{ | 
| +  // If this is the first exception for a previously unconditionally applied | 
| +  // element hiding selector we need to take care to update the lookups. | 
| +  let unconditionalFilterForSelector = filterBySelector.get(selector); | 
| +  if (unconditionalFilterForSelector) | 
| +  { | 
| +    addToFiltersByDomain(unconditionalFilterForSelector); | 
| +    filterBySelector.delete(selector); | 
| +    unconditionalSelectors = null; | 
| +  } | 
| +}); | 
| + | 
| /** | 
| * Container for element hiding filters | 
| * @class | 
| */ | 
| exports.ElemHide = { | 
| /** | 
| * Removes all known filters | 
| */ | 
| clear() | 
| { | 
| -    for (let collection of [filtersByDomain, filterBySelector, | 
| -                            knownFilters, exceptions]) | 
| -    { | 
| +    for (let collection of [filtersByDomain, filterBySelector, knownFilters]) | 
| collection.clear(); | 
| -    } | 
| + | 
| unconditionalSelectors = null; | 
| FilterNotifier.emit("elemhideupdate"); | 
| }, | 
|  | 
| /** | 
| * Add a new element hiding filter | 
| -   * @param {ElemHideBase} filter | 
| +   * @param {ElemHideFilter} filter | 
| */ | 
| add(filter) | 
| { | 
| if (knownFilters.has(filter)) | 
| return; | 
|  | 
| let {selector} = filter; | 
|  | 
| -    if (filter instanceof ElemHideException) | 
| -    { | 
| -      let list = exceptions.get(selector); | 
| -      if (list) | 
| -        list.push(filter); | 
| -      else | 
| -        exceptions.set(selector, [filter]); | 
| - | 
| -      // If this is the first exception for a previously unconditionally | 
| -      // applied element hiding selector we need to take care to update the | 
| -      // lookups. | 
| -      let unconditionalFilterForSelector = filterBySelector.get(selector); | 
| -      if (unconditionalFilterForSelector) | 
| -      { | 
| -        addToFiltersByDomain(unconditionalFilterForSelector); | 
| -        filterBySelector.delete(selector); | 
| -        unconditionalSelectors = null; | 
| -      } | 
| -    } | 
| -    else if (!(filter.domains || exceptions.has(selector))) | 
| +    if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) | 
| { | 
| // The new filter's selector is unconditionally applied to all domains | 
| filterBySelector.set(selector, filter); | 
| unconditionalSelectors = null; | 
| } | 
| else | 
| { | 
| // The new filter's selector only applies to some domains | 
| @@ -159,35 +145,27 @@ | 
| } | 
|  | 
| knownFilters.add(filter); | 
| FilterNotifier.emit("elemhideupdate"); | 
| }, | 
|  | 
| /** | 
| * Removes an element hiding filter | 
| -   * @param {ElemHideBase} filter | 
| +   * @param {ElemHideFilter} filter | 
| */ | 
| remove(filter) | 
| { | 
| if (!knownFilters.has(filter)) | 
| return; | 
|  | 
| let {selector} = filter; | 
|  | 
| -    // Whitelisting filters | 
| -    if (filter instanceof ElemHideException) | 
| -    { | 
| -      let list = exceptions.get(selector); | 
| -      let index = list.indexOf(filter); | 
| -      if (index >= 0) | 
| -        list.splice(index, 1); | 
| -    } | 
| // Unconditially applied element hiding filters | 
| -    else if (filterBySelector.get(selector) == filter) | 
| +    if (filterBySelector.get(selector) == filter) | 
| { | 
| filterBySelector.delete(selector); | 
| unconditionalSelectors = null; | 
| } | 
| // Conditionally applied element hiding filters | 
| else | 
| { | 
| let domains = filter.domains || defaultDomains; | 
| @@ -199,38 +177,16 @@ | 
| } | 
| } | 
|  | 
| knownFilters.delete(filter); | 
| FilterNotifier.emit("elemhideupdate"); | 
| }, | 
|  | 
| /** | 
| -   * Checks whether an exception rule is registered for a selector on a | 
| -   * particular domain. | 
| -   * @param {string} selector | 
| -   * @param {?string} docDomain | 
| -   * @return {?ElemHideException} | 
| -   */ | 
| -  getException(selector, docDomain) | 
| -  { | 
| -    let list = exceptions.get(selector); | 
| -    if (!list) | 
| -      return null; | 
| - | 
| -    for (let i = list.length - 1; i >= 0; i--) | 
| -    { | 
| -      if (list[i].isActiveOnDomain(docDomain)) | 
| -        return list[i]; | 
| -    } | 
| - | 
| -    return null; | 
| -  }, | 
| - | 
| -  /** | 
| * Determines from the current filter list which selectors should be applied | 
| * on a particular host name. | 
| * @param {string} domain | 
| * @param {boolean} [specificOnly] true if generic filters should not apply. | 
| * @returns {string[]} List of selectors. | 
| */ | 
| getSelectorsForDomain(domain, specificOnly = false) | 
| { | 
| @@ -254,17 +210,17 @@ | 
| if (!isIncluded) | 
| { | 
| excluded.add(filter); | 
| } | 
| else | 
| { | 
| let {selector} = filter; | 
| if ((excluded.size == 0 || !excluded.has(filter)) && | 
| -                !this.getException(selector, domain)) | 
| +                !ElemHideExceptions.getException(selector, domain)) | 
| { | 
| selectors.push(selector); | 
| } | 
| } | 
| } | 
| } | 
|  | 
| if (currentDomain == "") | 
|  |