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); |
} |
}; |