Index: lib/elemHide.js |
=================================================================== |
--- a/lib/elemHide.js |
+++ b/lib/elemHide.js |
@@ -42,16 +42,24 @@ |
* 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; |
/** |
+ * Cached list of selectors that apply on any domain that neither has any |
Manish Jethani
2018/09/17 09:28:43
Here we are introducing the idea of "common" selec
|
+ * domain-specific filters or exceptions nor is specifically excluded from any |
+ * generic filters or exceptions. |
+ * @type {?string[]} |
+ */ |
+let commonSelectors = 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 |
@@ -88,16 +96,18 @@ |
if (!unconditionalSelectors) |
unconditionalSelectors = [...filterBySelector.keys()]; |
return unconditionalSelectors; |
} |
ElemHideExceptions.on("added", ({selector}) => |
{ |
+ commonSelectors = null; |
+ |
// 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) |
{ |
addToFiltersByDomain(unconditionalFilterForSelector); |
filterBySelector.delete(selector); |
unconditionalSelectors = null; |
@@ -112,29 +122,33 @@ |
/** |
* Removes all known filters |
*/ |
clear() |
{ |
for (let collection of [filtersByDomain, filterBySelector, knownFilters]) |
collection.clear(); |
+ commonSelectors = null; |
unconditionalSelectors = null; |
+ |
filterNotifier.emit("elemhideupdate"); |
}, |
/** |
* Add a new element hiding filter |
* @param {ElemHideFilter} filter |
*/ |
add(filter) |
{ |
if (knownFilters.has(filter)) |
return; |
+ commonSelectors = null; |
+ |
let {selector} = filter; |
if (!(filter.domains || ElemHideExceptions.hasExceptions(selector))) |
{ |
// The new filter's selector is unconditionally applied to all domains |
filterBySelector.set(selector, filter); |
unconditionalSelectors = null; |
} |
@@ -152,16 +166,18 @@ |
* Removes an element hiding filter |
* @param {ElemHideFilter} filter |
*/ |
remove(filter) |
{ |
if (!knownFilters.has(filter)) |
return; |
+ commonSelectors = null; |
+ |
let {selector} = filter; |
// Unconditially applied element hiding filters |
if (filterBySelector.get(selector) == filter) |
{ |
filterBySelector.delete(selector); |
unconditionalSelectors = null; |
} |
@@ -195,49 +211,75 @@ |
*/ |
getSelectorsForDomain(domain, specificOnly = false) |
{ |
let selectors = []; |
let excluded = new Set(); |
let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : ""; |
+ let commonCase = true; |
+ |
// 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); |
if (filters) |
{ |
+ if (currentDomain != "") |
+ commonCase = false; |
+ |
for (let [filter, isIncluded] of filters) |
{ |
if (!isIncluded) |
{ |
excluded.add(filter); |
} |
else |
{ |
let {selector} = filter; |
- if ((excluded.size == 0 || !excluded.has(filter)) && |
- !ElemHideExceptions.getException(selector, domain)) |
+ if (excluded.size == 0 || !excluded.has(filter)) |
{ |
- selectors.push(selector); |
+ let exception = ElemHideExceptions.getException(selector, domain); |
+ if (!exception) |
+ selectors.push(selector); |
+ else if (commonCase && exception.domains) |
+ commonCase = false; |
} |
} |
} |
} |
if (currentDomain == "") |
break; |
let nextDot = currentDomain.indexOf("."); |
currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); |
} |
if (!specificOnly) |
- selectors = getUnconditionalSelectors().concat(selectors); |
+ { |
+ // Use the cache of common selectors if there are no filters or |
+ // exceptions that apply or don't apply specifically to this domain. |
+ if (commonCase) |
+ { |
+ if (!commonSelectors) |
+ { |
+ let unconditional = getUnconditionalSelectors(); |
+ commonSelectors = Object.freeze(unconditional.concat(selectors)); |
+ } |
+ |
+ selectors = commonSelectors; |
+ } |
+ else |
+ { |
+ let unconditional = getUnconditionalSelectors(); |
+ selectors = Object.freeze(unconditional.concat(selectors)); |
+ } |
+ } |
return selectors; |
} |
}; |