| Index: lib/elemHide.js |
| diff --git a/lib/elemHide.js b/lib/elemHide.js |
| index 574ef91dff6340a1e9408571fef1b31726cec78c..014de60b508aa16a5b3bc9965f26e4e218140e6c 100644 |
| --- a/lib/elemHide.js |
| +++ b/lib/elemHide.js |
| @@ -40,17 +40,35 @@ var filterByKey = []; |
| var keyByFilter = Object.create(null); |
| /** |
| - * Nested lookup table, filter (or false if inactive) by filter text by domain |
| + * 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 |
| */ |
| var filtersByDomain = Object.create(null); |
| /** |
| - * Indicates whether we are using (and maintaining) the filtersByDomain lookup. |
| - * (Will be false for Firefox) |
| - * @type Boolean |
| + * Lookup table, filters by selector. (Only contains filters that have a |
| + * selector that is unconditionally matched for all domains.) |
| + */ |
| +var filtersBySelector = Object.create(null); |
| + |
| +/** |
| + * Array of selectors which unconditionally apply to all domains. |
| + */ |
| +var unconditionalSelectors = []; |
| + |
| +/** |
| + * Indicates that the unconditionalSelectors Array needs to be regenerated. |
| */ |
| -var usingFiltersByDomain = !("nsIStyleSheetService" in Ci); |
| +var unconditionalSelectorsDirty = false; |
| /** |
| * Object to be used instead when a filter has a blank domains property. |
| @@ -119,12 +137,32 @@ var ElemHide = exports.ElemHide = |
| filterByKey = []; |
| keyByFilter = Object.create(null); |
| filtersByDomain = Object.create(null); |
| + filtersBySelector = Object.create(null); |
| + unconditionalSelectors = []; |
| + unconditionalSelectorsDirty = false; |
| knownExceptions = Object.create(null); |
| exceptions = Object.create(null); |
| ElemHide.isDirty = false; |
| ElemHide.unapply(); |
| }, |
| + _addToFiltersByDomain: function(filter) |
| + { |
| + let key = keyByFilter[filter.text]; |
| + let domains = filter.domains || defaultDomains; |
| + for (let domain in domains) |
| + { |
| + let filters = filtersByDomain[domain]; |
| + if (!filters) |
| + filters = filtersByDomain[domain] = Object.create(null); |
| + |
| + if (domains[domain]) |
| + filters[key] = filter; |
| + else |
| + filters[key] = false; |
| + } |
| + }, |
| + |
| /** |
| * Add a new element hiding filter |
| * @param {ElemHideFilter} filter |
| @@ -140,6 +178,22 @@ var ElemHide = exports.ElemHide = |
| if (!(selector in exceptions)) |
| 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 unconditionalFilters = filtersBySelector[selector]; |
| + if (unconditionalFilters) |
| + { |
| + for (let f of unconditionalFilters) |
| + this._addToFiltersByDomain(f); |
| + delete filtersBySelector[selector]; |
| + unconditionalSelectorsDirty = true; |
| + } |
| + } |
| + |
| knownExceptions[filter.text] = true; |
| } |
| else |
| @@ -150,19 +204,26 @@ var ElemHide = exports.ElemHide = |
| let key = filterByKey.push(filter) - 1; |
| keyByFilter[filter.text] = key; |
| - if (usingFiltersByDomain) |
| + if (usingGetSelectorsForDomain) |
| { |
| - let domains = filter.domains || defaultDomains; |
| - for (let domain in domains) |
| + // The new filter's selector is unconditionally applied to all domains |
| + if (!filter.domains && !exceptions[filter.selector]) |
| { |
| - let filters = filtersByDomain[domain]; |
| - if (!filters) |
| - filters = filtersByDomain[domain] = Object.create(null); |
| - |
| - if (domains[domain]) |
| - filters[filter.text] = filter; |
| + let filters = filtersBySelector[filter.selector]; |
| + if (filters) |
| + { |
| + filters.push(filter); |
| + } |
| else |
| - filters[filter.text] = false; |
| + { |
| + filtersBySelector[filter.selector] = [filter]; |
| + unconditionalSelectorsDirty = true; |
| + } |
| + } |
| + // The new filter's selector only applies to some domains |
| + else |
| + { |
| + this._addToFiltersByDomain(filter); |
| } |
| } |
| @@ -197,14 +258,31 @@ var ElemHide = exports.ElemHide = |
| delete keyByFilter[filter.text]; |
| ElemHide.isDirty = true; |
| - if (usingFiltersByDomain) |
| + if (usingGetSelectorsForDomain) |
| { |
| - let domains = filter.domains || defaultDomains; |
| - for (let domain in domains) |
| + let filters = filtersBySelector[filter.selector]; |
| + if (filters) |
| { |
| - let filters = filtersByDomain[domain]; |
| - if (filters) |
| - delete filters[filter.text]; |
| + if (filters.length > 1) |
| + { |
| + let index = filters.indexOf(filter); |
| + filters.splice(index, 1); |
| + } |
| + else |
| + { |
| + delete filtersBySelector[filter.selector]; |
| + unconditionalSelectorsDirty = true; |
| + } |
| + } |
| + else |
| + { |
| + let domains = filter.domains || defaultDomains; |
| + for (let domain in domains) |
| + { |
| + let filters = filtersByDomain[domain]; |
| + if (filters) |
| + delete filters[key]; |
| + } |
| } |
| } |
| } |
| @@ -416,11 +494,11 @@ var ElemHide = exports.ElemHide = |
| /** |
| * Returns a list of all selectors active on a particular domain, must not be |
| - * used in Firefox (when usingFiltersByDomain is false). |
| + * used in Firefox (when usingGetSelectorsForDomain is false). |
| */ |
| getSelectorsForDomain: function(/**String*/ domain, /**Boolean*/ specificOnly) |
| { |
| - if (!usingFiltersByDomain) |
| + if (!usingGetSelectorsForDomain) |
| throw new Error("getSelectorsForDomain can not be used in Firefox!"); |
| let selectors = []; |
| @@ -435,13 +513,13 @@ var ElemHide = exports.ElemHide = |
| let filters = filtersByDomain[currentDomain]; |
| if (filters) |
| { |
| - for (let filterText in filters) |
| + for (let filterKey in filters) |
| { |
| - if (filterText in seenFilters) |
| + if (filterKey in seenFilters) |
| continue; |
| - seenFilters[filterText] = true; |
| + seenFilters[filterKey] = true; |
| - let filter = filters[filterText]; |
| + let filter = filters[filterKey]; |
| if (filter && !this.getException(filter, domain)) |
| selectors.push(filter.selector); |
| } |
| @@ -454,6 +532,14 @@ var ElemHide = exports.ElemHide = |
| currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); |
| } |
| - return selectors; |
| + if (specificOnly) |
| + return selectors; |
| + |
| + if (unconditionalSelectorsDirty) |
| + { |
| + unconditionalSelectors = Object.keys(filtersBySelector); |
| + unconditionalSelectorsDirty = false; |
| + } |
| + return unconditionalSelectors.concat(selectors); |
| } |
| }; |