Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: lib/elemHide.js

Issue 29773570: Issue 6652 - Implement fast selector lookups for unknown domains (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Created May 7, 2018, 3:38 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | test/elemHide.js » ('j') | test/elemHide.js » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/elemHide.js
===================================================================
--- a/lib/elemHide.js
+++ b/lib/elemHide.js
@@ -22,17 +22,17 @@
*/
const {ElemHideException} = require("./filterClasses");
const {FilterNotifier} = require("./filterNotifier");
/**
* 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>>}
+ * @type {Map.<string,?Map.<Filter,boolean>>}
Manish Jethani 2018/05/07 16:03:02 The value here can now be null.
*/
let filtersByDomain = new Map();
/**
* Lookup table, filter by selector. (Only used for selectors that are
* unconditionally matched for all domains.)
* @type {Map.<string,Filter>}
*/
@@ -57,69 +57,141 @@
let knownFilters = new Set();
/**
* Lookup table, lists of element hiding exceptions by selector
* @type {Map.<string,Filter>}
*/
let exceptions = new Map();
+/*
+ * Set containing selectors with generic exceptions
+ * @type {Set.<string>}
+ */
+let genericExceptionSelectors = new Set();
+
+/*
+ * Checks if a domain is known
+ * @param {string} domain
+ * @returns {boolean}
+ */
+function isDomainKnown(domain)
+{
+ while (domain)
+ {
+ // A domain is "known" if we have seen any filters that would apply to it.
+ // For example, given the filters "##foo" and "example.com#@#foo",
+ // example.com is a known domain, as is mail.example.com and any other
+ // subdomains of example.com.
+ if (filtersByDomain.has(domain))
+ return true;
+
+ let nextDot = domain.indexOf(".");
+ domain = nextDot == -1 ? null : domain.substring(nextDot + 1);
+ }
+
+ return false;
+}
+
+/*
+ * Returns a list of selectors that apply on any unknown domain
Manish Jethani 2018/05/07 16:03:02 There are generic selectors that are "conditional"
+ * @returns {string[]}
+ */
+function getConditionalGenericSelectors()
Manish Jethani 2018/05/07 16:10:33 To give you some numbers, there are 18,300 uncondi
+{
+ let selectors = [];
+
+ let filters = filtersByDomain.get("");
+ if (!filters)
+ return selectors;
+
+ for (let {selector} of filters.keys())
+ {
+ if (!genericExceptionSelectors.has(selector))
Manish Jethani 2018/05/07 16:03:02 Here we have to check for generic exceptions like
+ selectors.push(selector);
+ }
+
+ return selectors;
+}
+
/**
* Container for element hiding filters
* @class
*/
let ElemHide = exports.ElemHide = {
/**
* Removes all known filters
*/
clear()
{
for (let collection of [filtersByDomain, filterBySelector,
- knownFilters, exceptions])
+ knownFilters, exceptions,
+ genericExceptionSelectors])
{
collection.clear();
}
unconditionalSelectors = null;
FilterNotifier.emit("elemhideupdate");
},
_addToFiltersByDomain(filter)
{
let domains = filter.domains || defaultDomains;
- for (let [domain, isIncluded] of domains)
+ if (filter instanceof ElemHideException)
{
- // There's no need to note that a filter is generically disabled.
- if (!isIncluded && domain == "")
- continue;
+ for (let domain of domains.keys())
+ {
+ // Add an entry for each domain, but without any filters. This makes
+ // the domain "known" and helps us avoid the optimized path (which
+ // would give incorrect results).
+ if (domain != "" && !filtersByDomain.has(domain))
Manish Jethani 2018/05/07 16:03:02 We could have had a separate knownExceptionDomains
+ filtersByDomain.set(domain, null);
+ }
+ }
+ else
+ {
+ for (let [domain, isIncluded] of domains)
+ {
+ // 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 filters = filtersByDomain.get(domain);
+ if (!filters)
+ filtersByDomain.set(domain, filters = new Map());
+ filters.set(filter, isIncluded);
+ }
}
},
/**
* Add a new element hiding filter
* @param {ElemHideBase} filter
*/
add(filter)
{
if (knownFilters.has(filter))
return;
if (filter instanceof ElemHideException)
{
- let {selector} = filter;
+ let {selector, domains} = filter;
+
let list = exceptions.get(selector);
if (list)
list.push(filter);
else
exceptions.set(selector, [filter]);
+ if (domains)
Manish Jethani 2018/05/07 16:03:02 For exceptions too we should remember the domains,
+ this._addToFiltersByDomain(filter);
+
+ if (filter.isGeneric())
+ genericExceptionSelectors.add(filter.selector);
+
// 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)
{
this._addToFiltersByDomain(unconditionalFilterForSelector);
filterBySelector.delete(selector);
@@ -153,16 +225,19 @@
// Whitelisting filters
if (filter instanceof ElemHideException)
{
let list = exceptions.get(filter.selector);
let index = list.indexOf(filter);
if (index >= 0)
list.splice(index, 1);
+
Manish Jethani 2018/05/07 16:03:02 Note that we don't bother "unknowing" a domain onc
+ if (filter.isGeneric())
+ genericExceptionSelectors.delete(filter.selector);
}
// Unconditially applied element hiding filters
else if (filterBySelector.get(filter.selector) == filter)
{
filterBySelector.delete(filter.selector);
unconditionalSelectors = null;
}
// Conditionally applied element hiding filters
@@ -247,45 +322,53 @@
let selectors = [];
if (typeof criteria == "undefined")
criteria = ElemHide.ALL_MATCHING;
if (criteria < ElemHide.NO_UNCONDITIONAL)
selectors = this.getUnconditionalSelectors();
let specificOnly = (criteria >= ElemHide.SPECIFIC_ONLY);
- let excluded = new Set();
let currentDomain = domain ? domain.toUpperCase() : "";
- // 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 (isDomainKnown(currentDomain))
Manish Jethani 2018/05/07 16:03:02 The call to isDomainKnown is the only additional c
{
- if (specificOnly && currentDomain == "")
- break;
+ let excluded = new Set();
- let filters = filtersByDomain.get(currentDomain);
- if (filters)
+ // This code is a performance hot-spot, which is why we've made certain
+ // micro-optimisations. Please be careful before making changes.
+ while (true)
{
- for (let [filter, isIncluded] of filters)
+ if (specificOnly && currentDomain == "")
+ break;
+
+ let filters = filtersByDomain.get(currentDomain);
+ if (filters)
{
- if (!isIncluded)
+ for (let [filter, isIncluded] of filters)
{
- excluded.add(filter);
- }
- else if ((excluded.size == 0 || !excluded.has(filter)) &&
- !this.getException(filter, domain))
- {
- selectors.push(filter.selector);
+ if (!isIncluded)
+ {
+ excluded.add(filter);
+ }
+ else if ((excluded.size == 0 || !excluded.has(filter)) &&
+ !this.getException(filter, domain))
+ {
+ selectors.push(filter.selector);
+ }
}
}
- }
+
+ if (currentDomain == "")
+ break;
- if (currentDomain == "")
- break;
-
- let nextDot = currentDomain.indexOf(".");
- currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1);
+ let nextDot = currentDomain.indexOf(".");
+ currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1);
+ }
+ }
+ else if (!specificOnly)
+ {
+ selectors = selectors.concat(getConditionalGenericSelectors());
Manish Jethani 2018/05/07 16:03:02 We might want to cache the value returned by getCo
}
return selectors;
}
};
« no previous file with comments | « no previous file | test/elemHide.js » ('j') | test/elemHide.js » ('J')

Powered by Google App Engine
This is Rietveld