| Index: lib/elemHide.js |
| =================================================================== |
| --- a/lib/elemHide.js |
| +++ b/lib/elemHide.js |
| @@ -16,44 +16,46 @@ |
| */ |
| "use strict"; |
| /** |
| * @fileOverview Element hiding implementation. |
| */ |
| +const {Filter} = require("./filterClasses"); |
| const {ElemHideExceptions} = require("./elemHideExceptions"); |
| const {filterNotifier} = require("./filterNotifier"); |
| /** |
| * The maximum number of selectors in a CSS rule. This is used by |
| * <code>{@link createStyleSheet}</code> to split up a long list of selectors |
| * into multiple rules. |
| * @const {number} |
| * @default |
| */ |
| const selectorGroupSize = 1024; |
| /** |
| - * Lookup table, active flag, by filter by domain. |
| - * (Only contains filters that aren't unconditionally matched for all domains.) |
| - * @type {Map.<string,Map.<Filter,boolean>>} |
| + * Lookup table, active flag, by filter text by domain. |
| + * (Only contains text for filters that aren't unconditionally matched for all |
| + * domains.) |
| + * @type {Map.<string,Map.<string,boolean>>} |
| */ |
| -let filtersByDomain = new Map(); |
| +let filterTextByDomain = new Map(); |
| /** |
| - * Lookup table, filter by selector. (Only used for selectors that are |
| + * Lookup table, filter text by selector. (Only used for selectors that are |
| * unconditionally matched for all domains.) |
| - * @type {Map.<string,Filter>} |
| + * @type {Map.<string,string>} |
| */ |
| -let filterBySelector = new Map(); |
| +let filterTextBySelector = new Map(); |
| /** |
| - * This array caches the keys of filterBySelector table (selectors |
| + * This array caches the keys of filterTextBySelector table (selectors |
| * which unconditionally apply on all domains). It will be null if the |
| * cache needs to be rebuilt. |
| * @type {?string[]} |
| */ |
| let unconditionalSelectors = null; |
| /** |
| * The default style sheet that applies on all domains. This is based on the |
| @@ -74,73 +76,73 @@ |
| /** |
| * 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 |
| - * @type {Set.<ElemHideFilter>} |
| + * Set containing the text of known element hiding filters |
| + * @type {Set.<string>} |
| */ |
| -let knownFilters = new Set(); |
| +let knownFilterText = new Set(); |
| /** |
| * All domains known to occur in exceptions |
| * @type {Set.<string>} |
| */ |
| let knownExceptionDomains = new Set(); |
| /** |
| * Returns the suffix of the given domain that is known. If no suffix is known, |
| * an empty string is returned. |
| * @param {?string} domain |
| * @returns {string} |
| */ |
| function getKnownSuffix(domain) |
| { |
| - while (domain && !filtersByDomain.has(domain) && |
| + while (domain && !filterTextByDomain.has(domain) && |
| !knownExceptionDomains.has(domain)) |
| { |
| let index = domain.indexOf("."); |
| domain = index == -1 ? "" : domain.substring(index + 1); |
| } |
| return domain; |
| } |
| /** |
| - * Adds a filter to the lookup table of filters by domain. |
| + * Adds a filter to the lookup table of filter text by domain. |
| * @param {Filter} filter |
| * @param {?Map.<string,boolean>} [domains] |
| */ |
| function addToFiltersByDomain(filter, domains = filter.domains) |
| { |
| for (let [domain, isIncluded] of domains || defaultDomains) |
| { |
| // There's no need to note that a filter is generically disabled. |
| if (!isIncluded && domain == "") |
| continue; |
| - let filters = filtersByDomain.get(domain); |
| - if (!filters) |
| - filtersByDomain.set(domain, filters = new Map()); |
| - filters.set(filter, isIncluded); |
| + let map = filterTextByDomain.get(domain); |
| + if (!map) |
| + filterTextByDomain.set(domain, map = new Map()); |
| + map.set(filter.text, isIncluded ? filter.selector : null); |
| } |
| } |
| /** |
| * Returns a list of selectors that apply on each website unconditionally. |
| * @returns {string[]} |
| */ |
| function getUnconditionalSelectors() |
| { |
| if (!unconditionalSelectors) |
| - unconditionalSelectors = [...filterBySelector.keys()]; |
| + unconditionalSelectors = [...filterTextBySelector.keys()]; |
| return unconditionalSelectors; |
| } |
| /** |
| * Returns the list of selectors that apply on a given domain from the subset |
| * of filters that do not apply unconditionally on all domains. |
| * |
| @@ -159,33 +161,29 @@ |
| // 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) |
| + let map = filterTextByDomain.get(currentDomain); |
| + if (map) |
| { |
| - for (let [filter, isIncluded] of filters) |
| + for (let [text, selector] of map) |
| { |
| - if (!isIncluded) |
| + if (selector == null) |
| { |
| - excluded.add(filter); |
| + excluded.add(text); |
| } |
| - else |
| + else if ((excluded.size == 0 || !excluded.has(text)) && |
| + !ElemHideExceptions.getException(selector, domain)) |
| { |
| - let {selector} = filter; |
| - if ((excluded.size == 0 || !excluded.has(filter)) && |
| - !ElemHideExceptions.getException(selector, domain)) |
| - { |
| - selectors.push(selector); |
| - } |
| + selectors.push(selector); |
| } |
| } |
| } |
| if (currentDomain == "") |
| break; |
| let nextDot = currentDomain.indexOf("."); |
| @@ -235,21 +233,21 @@ |
| // best-case optimization. |
| if (domain != "") |
| knownExceptionDomains.add(domain); |
| } |
| } |
| // 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) |
| + let unconditionalFilterTextForSelector = filterTextBySelector.get(selector); |
| + if (unconditionalFilterTextForSelector) |
| { |
| - addToFiltersByDomain(unconditionalFilterForSelector); |
| - filterBySelector.delete(selector); |
| + addToFiltersByDomain(Filter.fromText(unconditionalFilterTextForSelector)); |
|
hub
2018/11/28 23:21:31
So here, we call Filter.fromText() while before we
Manish Jethani
2019/04/07 18:05:09
Yes, unfortunately this is the price.
I looked at
Manish Jethani
2019/04/07 18:55:11
OK, so I looked into this [1], and parsing of gene
|
| + filterTextBySelector.delete(selector); |
| unconditionalSelectors = null; |
| defaultStyleSheet = null; |
| } |
| }); |
| /** |
| * Container for element hiding filters |
| * @class |
| @@ -257,96 +255,100 @@ |
| exports.ElemHide = { |
| /** |
| * Removes all known filters |
| */ |
| clear() |
| { |
| commonStyleSheet = null; |
| - for (let collection of [filtersByDomain, filterBySelector, knownFilters, |
| - knownExceptionDomains]) |
| + for (let collection of [filterTextByDomain, filterTextBySelector, |
| + knownFilterText, knownExceptionDomains]) |
| { |
| collection.clear(); |
| } |
| unconditionalSelectors = null; |
| defaultStyleSheet = null; |
| filterNotifier.emit("elemhideupdate"); |
| }, |
| /** |
| * Add a new element hiding filter |
| * @param {ElemHideFilter} filter |
| */ |
| add(filter) |
| { |
| - if (knownFilters.has(filter)) |
| + let {text} = filter; |
| + |
| + if (knownFilterText.has(text)) |
| return; |
| commonStyleSheet = null; |
| let {domains, selector} = filter; |
| if (!(domains || ElemHideExceptions.hasExceptions(selector))) |
| { |
| // The new filter's selector is unconditionally applied to all domains |
| - filterBySelector.set(selector, filter); |
| + filterTextBySelector.set(selector, text); |
| unconditionalSelectors = null; |
| defaultStyleSheet = null; |
| } |
| else |
| { |
| // The new filter's selector only applies to some domains |
| addToFiltersByDomain(filter, domains); |
| } |
| - knownFilters.add(filter); |
| + knownFilterText.add(text); |
| filterNotifier.emit("elemhideupdate"); |
| }, |
| /** |
| * Removes an element hiding filter |
| * @param {ElemHideFilter} filter |
| */ |
| remove(filter) |
| { |
| - if (!knownFilters.has(filter)) |
| + let {text} = filter; |
| + |
| + if (!knownFilterText.has(text)) |
| return; |
| commonStyleSheet = null; |
| let {selector} = filter; |
| // Unconditially applied element hiding filters |
| - if (filterBySelector.get(selector) == filter) |
| + if (filterTextBySelector.get(selector) == text) |
| { |
| - filterBySelector.delete(selector); |
| + filterTextBySelector.delete(selector); |
| unconditionalSelectors = null; |
| defaultStyleSheet = null; |
| } |
| // Conditionally applied element hiding filters |
| else |
| { |
| let domains = filter.domains || defaultDomains; |
| for (let domain of domains.keys()) |
| { |
| - let filters = filtersByDomain.get(domain); |
| - if (filters) |
| + let map = filterTextByDomain.get(domain); |
| + if (map) |
| { |
| - filters.delete(filter); |
| + map.delete(text); |
| - if (filters.size == 0) |
| - filtersByDomain.delete(domain); |
| + if (map.size == 0) |
| + filterTextByDomain.delete(domain); |
| } |
| } |
| } |
| - knownFilters.delete(filter); |
| + knownFilterText.delete(text); |
| filterNotifier.emit("elemhideupdate"); |
| }, |
| /** |
| * @typedef {object} ElemHideStyleSheet |
| * @property {string} code CSS code. |
| * @property {Array.<string>} selectors List of selectors. |
| */ |