| Index: lib/elemHide.js |
| diff --git a/lib/elemHide.js b/lib/elemHide.js |
| index 2c81a2324f579247d53eca5eeb6ac459210a937a..1ca6f7910a2d3cd8953a05c4bebb0e0af001c5ef 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,28 +156,25 @@ 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"); |
| @@ -213,31 +206,42 @@ var ElemHide = exports.ElemHide = |
| delete filterByKey[key]; |
| delete keyByFilter[filter.text]; |
| - if (usingGetSelectorsForDomain) |
| + let unconditional = !(filter.domains || filter.selector in exceptions); |
| + if (unconditional) |
| { |
| - let filters = filtersBySelector[filter.selector]; |
| - if (filters) |
| + let filterKeys = filterKeysBySelector[filter.selector]; |
| + if (filterKeys) |
| { |
| - if (filters.length > 1) |
| + if (filterKeys.length > 1) |
| { |
| - let index = filters.indexOf(filter); |
| - filters.splice(index, 1); |
| + let index = filterKeys.indexOf(key); |
| + filterKeys.splice(index, 1); |
| + if (index == 0) |
| + unconditionalFilterKeys = null; |
| } |
| else |
| { |
| - delete filtersBySelector[filter.selector]; |
| - unconditionalSelectors = null; |
| + delete filterKeysBySelector[filter.selector]; |
| + unconditionalSelectors = unconditionalFilterKeys = null; |
| } |
| } |
| else |
| { |
| - let domains = filter.domains || defaultDomains; |
| - for (let domain in domains) |
| - { |
| - let filters = filtersByDomain[domain]; |
| - if (filters) |
| - delete filters[key]; |
| - } |
| + // Even if this filter appears unconditional its selector might not |
| + // be. In that case we need to take care to remove it from |
| + // filtersByDomain instead. |
| + unconditional = false; |
| + } |
| + } |
| + |
| + if (!unconditional) |
| + { |
| + let domains = filter.domains || defaultDomains; |
| + for (let domain in domains) |
| + { |
| + let filters = filtersByDomain[domain]; |
| + if (filters) |
| + delete filters[key]; |
| } |
| } |
| } |
| @@ -265,7 +269,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 +302,56 @@ 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(); |
| + }, |
| + |
| + /** |
| + * Returns a list of all selectors active on a particular domain. Optionally |
| + * a list of corresponding filter keys for the selectors can be returned too. |
| + * The optional criteria parameter restricts the results as follows: |
| + * 0 - All selectors allowed (default) |
| + * 1 - No unconditionally matching selectors allowed |
| + * 2 - Only specifically matching selectors allowed |
| + */ |
| + getSelectorsForDomain: function(/**String*/ domain, |
| + /**Number*/ criteria, |
| + /**Boolean*/ provideFilterKeys) |
| + { |
| + let filterKeys = []; |
| + let selectors = []; |
| + if (!criteria) |
| + { |
| + selectors = this.getUnconditionalSelectors(); |
| + if (provideFilterKeys) |
| + filterKeys = this.getUnconditionalFilterKeys(); |
| + } |
| + |
| + let specificOnly = criteria == 2; |
| let seenFilters = Object.create(null); |
| let currentDomain = domain ? domain.toUpperCase() : ""; |
| while (true) |
| @@ -328,7 +370,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 +385,9 @@ var ElemHide = exports.ElemHide = |
| currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); |
| } |
| - return selectors; |
| + if (provideFilterKeys) |
| + return [selectors, filterKeys.map(key => parseInt(key, 10))]; |
|
kzar
2016/09/20 14:23:49
(Since filtersByDomain[domain] is an Object we end
|
| + else |
| + return selectors; |
| } |
| }; |