| 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 |
| + * 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,26 +211,37 @@ |
| */ |
| getSelectorsForDomain(domain, specificOnly = false) |
| { |
| let selectors = []; |
| let excluded = new Set(); |
| let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : ""; |
| + let commonCase = !specificOnly; |
| + |
| // 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; |
| + if (commonCase && currentDomain != "" && |
| + ElemHideExceptions.domainHasExceptions(currentDomain)) |
| + { |
| + commonCase = false; |
| + } |
| + |
| let filters = filtersByDomain.get(currentDomain); |
| if (filters) |
| { |
| + if (currentDomain != "") |
| + commonCase = false; |
| + |
| for (let [filter, isIncluded] of filters) |
| { |
| if (!isIncluded) |
| { |
| excluded.add(filter); |
| } |
| else |
| { |
| @@ -231,13 +258,30 @@ |
| 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) |
| + { |
| + commonSelectors = |
| + Object.freeze(getUnconditionalSelectors().concat(selectors)); |
| + } |
| + |
| + selectors = commonSelectors; |
| + } |
| + else |
| + { |
| + selectors = getUnconditionalSelectors().concat(selectors); |
| + } |
| + } |
| return selectors; |
| } |
| }; |