Index: lib/elemHide.js |
diff --git a/lib/elemHide.js b/lib/elemHide.js |
index 2c81a2324f579247d53eca5eeb6ac459210a937a..9c34678d41b306fd3e71c5c6e7be48026986ead3 100644 |
--- a/lib/elemHide.js |
+++ b/lib/elemHide.js |
@@ -36,14 +36,6 @@ var filterByKey = []; |
var keyByFilter = Object.create(null); |
/** |
- * Indicates whether we are using the getSelectorsForDomain function and |
- * therefore mainting the required filtersByDomain, filtersBySelector and |
- * unconditionalSelectors lookups. (Will be false for Firefox) |
- * @type Boolean |
- */ |
-var usingGetSelectorsForDomain = !("nsIStyleSheetService" in Ci); |
- |
-/** |
* Nested lookup table, filter (or false if inactive) by filter key by domain. |
* (Only contains filters that aren't unconditionally matched for all domains.) |
* @type Object |
@@ -51,19 +43,27 @@ var usingGetSelectorsForDomain = !("nsIStyleSheetService" in Ci); |
var filtersByDomain = Object.create(null); |
/** |
- * Lookup table, filters by selector. (Only contains filters that have a |
+ * Lookup table, filter keys by selector. (Only contains filters that have a |
* selector that is unconditionally matched for all domains.) |
*/ |
-var filtersBySelector = Object.create(null); |
+var filterKeysBySelector = Object.create(null); |
/** |
- * This array caches the keys of filtersBySelector table (selectors which |
+ * This array caches the keys of filterKeysBySelector table (selectors which |
* unconditionally apply on all domains). It will be null if the cache needs to |
* be rebuilt. |
*/ |
var unconditionalSelectors = null; |
/** |
+ * This array caches the values of filterKeysBySelector table (filterIds for |
+ * selectors which unconditionally apply on all domains). It will be null if the |
+ * cache needs to be rebuilt. Note: Only the first filter key for each selector |
+ * is cached. |
+ */ |
+var unconditionalFilterKeys = null; |
+ |
+/** |
* Object to be used instead when a filter has a blank domains property. |
*/ |
var defaultDomains = Object.create(null); |
@@ -95,16 +95,15 @@ var ElemHide = exports.ElemHide = |
filterByKey = []; |
keyByFilter = Object.create(null); |
filtersByDomain = Object.create(null); |
- filtersBySelector = Object.create(null); |
- unconditionalSelectors = null; |
+ filterKeysBySelector = Object.create(null); |
+ unconditionalSelectors = unconditionalFilterKeys = null; |
knownExceptions = Object.create(null); |
exceptions = Object.create(null); |
FilterNotifier.emit("elemhideupdate"); |
}, |
- _addToFiltersByDomain: function(filter) |
+ _addToFiltersByDomain: function(key, filter) |
{ |
- let key = keyByFilter[filter.text]; |
let domains = filter.domains || defaultDomains; |
for (let domain in domains) |
{ |
@@ -135,19 +134,16 @@ var ElemHide = exports.ElemHide = |
exceptions[selector] = []; |
exceptions[selector].push(filter); |
- if (usingGetSelectorsForDomain) |
+ // If this is the first exception for a previously unconditionally |
+ // applied element hiding selector we need to take care to update the |
+ // lookups. |
+ let filterKeys = filterKeysBySelector[selector]; |
+ if (filterKeys) |
{ |
- // If this is the first exception for a previously unconditionally |
- // applied element hiding selector we need to take care to update the |
- // lookups. |
- let unconditionalFilters = filtersBySelector[selector]; |
- if (unconditionalFilters) |
- { |
- for (let f of unconditionalFilters) |
- this._addToFiltersByDomain(f); |
- delete filtersBySelector[selector]; |
- unconditionalSelectors = null; |
- } |
+ for (let filterKey of filterKeys) |
+ this._addToFiltersByDomain(filterKey, filterByKey[filterKey]); |
+ delete filterKeysBySelector[selector]; |
+ unconditionalSelectors = unconditionalFilterKeys = null; |
} |
knownExceptions[filter.text] = true; |
@@ -160,33 +156,64 @@ var ElemHide = exports.ElemHide = |
let key = filterByKey.push(filter) - 1; |
keyByFilter[filter.text] = key; |
- if (usingGetSelectorsForDomain) |
+ if (!(filter.domains || filter.selector in exceptions)) |
{ |
- if (!(filter.domains || filter.selector in exceptions)) |
+ // The new filter's selector is unconditionally applied to all domains |
+ let filterKeys = filterKeysBySelector[filter.selector]; |
+ if (filterKeys) |
{ |
- // The new filter's selector is unconditionally applied to all domains |
- let filters = filtersBySelector[filter.selector]; |
- if (filters) |
- { |
- filters.push(filter); |
- } |
- else |
- { |
- filtersBySelector[filter.selector] = [filter]; |
- unconditionalSelectors = null; |
- } |
+ filterKeys.push(key); |
} |
else |
{ |
- // The new filter's selector only applies to some domains |
- this._addToFiltersByDomain(filter); |
+ filterKeysBySelector[filter.selector] = [key]; |
+ unconditionalSelectors = unconditionalFilterKeys = null; |
} |
} |
+ else |
+ { |
+ // The new filter's selector only applies to some domains |
+ this._addToFiltersByDomain(key, filter); |
+ } |
} |
FilterNotifier.emit("elemhideupdate"); |
}, |
+ _removeFilterKey: function(key, filter) |
+ { |
+ let filterKeys = filterKeysBySelector[filter.selector]; |
+ if (filterKeys) |
+ { |
+ let index = filterKeys.indexOf(key); |
+ if (index >= 0) |
+ { |
+ if (filterKeys.length > 1) |
+ { |
+ filterKeys.splice(index, 1); |
+ if (index == 0) |
+ unconditionalFilterKeys = null; |
+ } |
+ else |
+ { |
+ delete filterKeysBySelector[filter.selector]; |
+ unconditionalSelectors = unconditionalFilterKeys = null; |
+ } |
+ return; |
+ } |
+ } |
+ |
+ // We haven't found this filter in unconditional filters, look in |
+ // filtersByDomain. |
+ let domains = filter.domains || defaultDomains; |
+ for (let domain in domains) |
+ { |
+ let filters = filtersByDomain[domain]; |
+ if (filters) |
+ delete filters[key]; |
+ } |
+ }, |
+ |
/** |
* Removes an element hiding filter |
* @param {ElemHideFilter} filter |
@@ -212,34 +239,7 @@ var ElemHide = exports.ElemHide = |
let key = keyByFilter[filter.text]; |
delete filterByKey[key]; |
delete keyByFilter[filter.text]; |
- |
- if (usingGetSelectorsForDomain) |
- { |
- let filters = filtersBySelector[filter.selector]; |
- if (filters) |
- { |
- if (filters.length > 1) |
- { |
- let index = filters.indexOf(filter); |
- filters.splice(index, 1); |
- } |
- else |
- { |
- delete filtersBySelector[filter.selector]; |
- unconditionalSelectors = null; |
- } |
- } |
- else |
- { |
- let domains = filter.domains || defaultDomains; |
- for (let domain in domains) |
- { |
- let filters = filtersByDomain[domain]; |
- if (filters) |
- delete filters[key]; |
- } |
- } |
- } |
+ this._removeFilterKey(key, filter); |
} |
FilterNotifier.emit("elemhideupdate"); |
@@ -265,7 +265,7 @@ var ElemHide = exports.ElemHide = |
/** |
* Retrieves an element hiding filter by the corresponding protocol key |
*/ |
- getFilterByKey: function(/**String*/ key) /**Filter*/ |
+ getFilterByKey: function(/**Number*/ key) /**Filter*/ |
{ |
return (key in filterByKey ? filterByKey[key] : null); |
}, |
@@ -298,18 +298,83 @@ var ElemHide = exports.ElemHide = |
}, |
/** |
- * Returns a list of all selectors active on a particular domain, must not be |
- * used in Firefox (when usingGetSelectorsForDomain is false). |
+ * Returns a list of selectors that apply on each website unconditionally. |
+ * @returns {String[]} |
*/ |
- getSelectorsForDomain: function(/**String*/ domain, /**Boolean*/ specificOnly) |
+ getUnconditionalSelectors: function() |
{ |
- if (!usingGetSelectorsForDomain) |
- throw new Error("getSelectorsForDomain can not be used in Firefox!"); |
- |
if (!unconditionalSelectors) |
- unconditionalSelectors = Object.keys(filtersBySelector); |
- let selectors = specificOnly ? [] : unconditionalSelectors.slice(); |
+ unconditionalSelectors = Object.keys(filterKeysBySelector); |
+ return unconditionalSelectors.slice(); |
+ }, |
+ |
+ /** |
+ * Returns a list of all selectors active on a particular domain. |
+ * Returns a list of filterKeys for selectors that apply on each website |
+ * unconditionally. |
+ * @returns {Number[]} |
+ */ |
+ getUnconditionalFilterKeys: function() |
+ { |
+ if (!unconditionalFilterKeys) |
+ { |
+ let selectors = this.getUnconditionalSelectors(); |
+ unconditionalFilterKeys = []; |
+ for (let selector of selectors) |
+ unconditionalFilterKeys.push(filterKeysBySelector[selector][0]); |
+ } |
+ return unconditionalFilterKeys.slice(); |
+ }, |
+ |
+ /** |
+ * Constant used by getSelectorsForDomain to return all selectors applying to |
+ * a particular hostname. |
+ */ |
+ ALL_MATCHING: 0, |
+ |
+ /** |
+ * Constant used by getSelectorsForDomain to exclude selectors which apply to |
+ * all websites without exception. |
+ */ |
+ NO_UNCONDITIONAL: 1, |
+ |
+ /** |
+ * Constant used by getSelectorsForDomain to return only selectors for filters |
+ * which specifically match the given host name. |
+ */ |
+ SPECIFIC_ONLY: 2, |
+ |
+ /** |
+ * Determines from the current filter list which selectors should be applied |
+ * on a particular host name. Optionally returns the corresponding filter |
+ * keys. |
+ * @param {String} domain |
+ * @param {Number} [criteria] |
+ * One of the following: ElemHide.ALL_MATCHING, ElemHide.NO_UNCONDITIONAL or |
+ * ElemHide.SPECIFIC_ONLY. |
+ * @param {Boolean} [provideFilterKeys] |
+ * If true, the function will return a list of corresponding filter keys in |
+ * addition to selectors. |
+ * @returns {string[]|Array.<string[]>} |
+ * List of selectors or an array with two elements (list of selectors and |
+ * list of corresponding keys) if provideFilterKeys is true. |
+ */ |
+ getSelectorsForDomain: function(domain, criteria, provideFilterKeys) |
+ { |
+ let filterKeys = []; |
+ let selectors = []; |
+ |
+ if (typeof criteria == "undefined") |
+ criteria = ElemHide.ALL_MATCHING; |
+ if (criteria < ElemHide.NO_UNCONDITIONAL) |
+ { |
+ selectors = this.getUnconditionalSelectors(); |
+ if (provideFilterKeys) |
+ filterKeys = this.getUnconditionalFilterKeys(); |
+ } |
+ |
+ let specificOnly = (criteria >= ElemHide.SPECIFIC_ONLY); |
let seenFilters = Object.create(null); |
let currentDomain = domain ? domain.toUpperCase() : ""; |
while (true) |
@@ -328,7 +393,11 @@ var ElemHide = exports.ElemHide = |
let filter = filters[filterKey]; |
if (filter && !this.getException(filter, domain)) |
+ { |
selectors.push(filter.selector); |
+ // It is faster to always push the key, even if not required. |
+ filterKeys.push(filterKey); |
+ } |
} |
} |
@@ -339,6 +408,9 @@ var ElemHide = exports.ElemHide = |
currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); |
} |
- return selectors; |
+ if (provideFilterKeys) |
+ return [selectors, filterKeys]; |
+ else |
+ return selectors; |
} |
}; |