Index: lib/elemHide.js |
=================================================================== |
--- a/lib/elemHide.js |
+++ b/lib/elemHide.js |
@@ -20,170 +20,195 @@ |
/** |
* @fileOverview Element hiding implementation. |
*/ |
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(); |
- |
-/** |
- * Lookup table, filter by selector. (Only used for selectors that are |
- * unconditionally matched for all domains.) |
- * @type {Map.<string,Filter>} |
- */ |
-let filterBySelector = new Map(); |
- |
-/** |
- * This array caches the keys of filterBySelector table (selectors |
- * which unconditionally apply on all domains). It will be null if the |
- * cache needs to be rebuilt. |
- * @type {?string[]} |
- */ |
-let unconditionalSelectors = null; |
- |
-/** |
* 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 filters |
- * @type {Set.<ElemHideFilter>} |
+ * Template for element hiding and element hiding emulation filter containers |
+ * @class |
*/ |
-let knownFilters = new Set(); |
- |
-/** |
- * Adds a filter to the lookup table of filters by domain. |
- * @param {Filter} filter |
- */ |
-function addToFiltersByDomain(filter) |
+function ElemHideTemplate() |
{ |
- let domains = filter.domains || defaultDomains; |
- for (let [domain, isIncluded] of domains) |
- { |
- // There's no need to note that a filter is generically disabled. |
- if (!isIncluded && domain == "") |
- continue; |
+ this._filtersByDomain = new Map(); |
+ this._filterBySelector = new Map(); |
+ this._unconditionalSelectors = null; |
+ this._knownFilters = new Set(); |
- let filters = filtersByDomain.get(domain); |
- if (!filters) |
- filtersByDomain.set(domain, filters = new Map()); |
- filters.set(filter, isIncluded); |
- } |
+ ElemHideExceptions.on("added", this._onExceptionAdded.bind(this)); |
} |
-/** |
- * Returns a list of selectors that apply on each website unconditionally. |
- * @returns {string[]} |
- */ |
-function getUnconditionalSelectors() |
-{ |
- if (!unconditionalSelectors) |
- unconditionalSelectors = [...filterBySelector.keys()]; |
+ElemHideTemplate.prototype = { |
+ /** |
+ * 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>>} |
+ */ |
+ _filtersByDomain: null, |
- return unconditionalSelectors; |
-} |
+ /** |
+ * Lookup table, filter by selector. (Only used for selectors that are |
+ * unconditionally matched for all domains.) |
+ * @type {Map.<string,Filter>} |
+ */ |
+ _filterBySelector: null, |
+ |
+ /** |
+ * This array caches the keys of _filterBySelector table (selectors |
+ * which unconditionally apply on all domains). It will be null if the |
+ * cache needs to be rebuilt. |
+ * @type {?string[]} |
+ */ |
+ _unconditionalSelectors: null, |
+ |
+ /** |
+ * Set containing known element hiding filters |
+ * @type {Set.<ElemHideFilter>} |
+ */ |
+ _knownFilters: null, |
-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) |
+ /** |
+ * Adds a filter to the lookup table of filters by domain. |
+ * @param {Filter} filter |
+ */ |
+ _addToFiltersByDomain(filter) |
+ { |
+ let domains = filter.domains || defaultDomains; |
+ for (let [domain, isIncluded] of domains) |
+ { |
+ // There's no need to note that a filter is generically disabled. |
+ if (!isIncluded && domain == "") |
+ continue; |
+ |
+ let filters = this._filtersByDomain.get(domain); |
+ if (!filters) |
+ this._filtersByDomain.set(domain, filters = new Map()); |
+ filters.set(filter, isIncluded); |
+ } |
+ }, |
+ |
+ /** |
+ * Returns a list of selectors that apply on each website unconditionally. |
+ * @returns {string[]} |
+ */ |
+ _getUnconditionalSelectors() |
{ |
- addToFiltersByDomain(unconditionalFilterForSelector); |
- filterBySelector.delete(selector); |
- unconditionalSelectors = null; |
- } |
-}); |
+ if (!this._unconditionalSelectors) |
+ this._unconditionalSelectors = [...this._filterBySelector.keys()]; |
+ |
+ return this._unconditionalSelectors; |
+ }, |
-/** |
- * Container for element hiding filters |
- * @class |
- */ |
-exports.ElemHide = { |
+ /** |
+ * Handles the event when a new element hiding exception has been added |
+ * @param {ElemHideException} exception |
+ */ |
+ _onExceptionAdded(exception) |
+ { |
+ let {selector} = exception; |
+ |
+ // 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 = this._filterBySelector.get(selector); |
+ if (unconditionalFilterForSelector) |
+ { |
+ this._addToFiltersByDomain(unconditionalFilterForSelector); |
+ this._filterBySelector.delete(selector); |
+ this._unconditionalSelectors = null; |
+ } |
+ }, |
+ |
/** |
* Removes all known filters |
*/ |
clear() |
{ |
- for (let collection of [filtersByDomain, filterBySelector, |
- knownFilters]) |
+ for (let collection of [this._filtersByDomain, this._filterBySelector, |
+ this._knownFilters]) |
{ |
collection.clear(); |
} |
- unconditionalSelectors = null; |
+ this._unconditionalSelectors = null; |
FilterNotifier.emit("elemhideupdate"); |
}, |
/** |
* Add a new element hiding filter |
* @param {ElemHideFilter} filter |
*/ |
add(filter) |
{ |
- if (knownFilters.has(filter)) |
+ if (this._knownFilters.has(filter)) |
return; |
if (!(filter.domains || ElemHideExceptions.hasExceptions(filter.selector))) |
{ |
// The new filter's selector is unconditionally applied to all domains |
- filterBySelector.set(filter.selector, filter); |
- unconditionalSelectors = null; |
+ this._filterBySelector.set(filter.selector, filter); |
+ this._unconditionalSelectors = null; |
} |
else |
{ |
// The new filter's selector only applies to some domains |
- addToFiltersByDomain(filter); |
+ this._addToFiltersByDomain(filter); |
} |
- knownFilters.add(filter); |
+ this._knownFilters.add(filter); |
FilterNotifier.emit("elemhideupdate"); |
}, |
/** |
* Removes an element hiding filter |
* @param {ElemHideFilter} filter |
*/ |
remove(filter) |
{ |
- if (!knownFilters.has(filter)) |
+ if (!this._knownFilters.has(filter)) |
return; |
// Unconditially applied element hiding filters |
- if (filterBySelector.get(filter.selector) == filter) |
+ if (this._filterBySelector.get(filter.selector) == filter) |
{ |
- filterBySelector.delete(filter.selector); |
- unconditionalSelectors = null; |
+ this._filterBySelector.delete(filter.selector); |
+ this._unconditionalSelectors = null; |
} |
// Conditionally applied element hiding filters |
else |
{ |
let domains = filter.domains || defaultDomains; |
for (let domain of domains.keys()) |
{ |
- let filters = filtersByDomain.get(domain); |
+ let filters = this._filtersByDomain.get(domain); |
if (filters) |
filters.delete(filter); |
} |
} |
- knownFilters.delete(filter); |
+ this._knownFilters.delete(filter); |
FilterNotifier.emit("elemhideupdate"); |
- }, |
+ } |
+}; |
+ |
+exports.ElemHideTemplate = ElemHideTemplate; |
+/** |
+ * Container for element hiding filters |
+ * @class |
+ */ |
+exports.ElemHide = Object.assign(new ElemHideTemplate(), { |
/** |
* 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) |
Manish Jethani
2018/05/17 05:51:54
Code that is specific to ElemHide is still in the
|
@@ -195,17 +220,17 @@ |
// This code is a performance hot-spot, which is why we've made certain |
// micro-optimisations. Please be careful before making changes. |
while (true) |
{ |
if (specificOnly && currentDomain == "") |
break; |
- let filters = filtersByDomain.get(currentDomain); |
+ let filters = this._filtersByDomain.get(currentDomain); |
if (filters) |
{ |
for (let [filter, isIncluded] of filters) |
{ |
if (!isIncluded) |
{ |
excluded.add(filter); |
} |
@@ -220,13 +245,13 @@ |
if (currentDomain == "") |
break; |
let nextDot = currentDomain.indexOf("."); |
currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); |
} |
if (!specificOnly) |
- selectors = getUnconditionalSelectors().concat(selectors); |
+ selectors = this._getUnconditionalSelectors().concat(selectors); |
return selectors; |
} |
-}; |
+}); |