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

Unified Diff: lib/elemHide.js

Issue 29893618: Issue 6957 - Return common style sheet for unknown domains (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Slightly refactored Created Sept. 28, 2018, 3:50 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 | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/elemHide.js
===================================================================
--- a/lib/elemHide.js
+++ b/lib/elemHide.js
@@ -58,29 +58,62 @@
/**
* The default style sheet that applies on all domains. This is based on the
* value of <code>{@link unconditionalSelectors}</code>.
* @type {?string}
*/
let defaultStyleSheet = null;
/**
+ * The common style sheet that applies on all unknown domains. This is a
+ * concatenation of the default style sheet and an additional style sheet based
+ * on selectors from all generic filters that are not in the
+ * <code>{@link unconditionalSelectors}</code> list.
+ * @type {?string}
+ */
+let commonStyleSheet = 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
* @type {Set.<ElemHideFilter>}
*/
let knownFilters = 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) &&
+ !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.
* @param {Filter} filter
*/
function addToFiltersByDomain(filter)
{
let domains = filter.domains || defaultDomains;
for (let [domain, isIncluded] of domains)
{
@@ -107,27 +140,27 @@
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.
*
* @param {string} domain The domain.
- * @param {boolean} [specificOnly=false] Whether selectors from generic filters
- * should be included.
+ * @param {boolean} specificOnly Whether selectors from generic filters should
+ * be included.
*
* @returns {Array.<string>} The list of selectors.
*/
-function getConditionalSelectorsForDomain(domain, specificOnly = false)
+function getConditionalSelectors(domain, specificOnly)
{
let selectors = [];
let excluded = new Set();
- let currentDomain = domain ? domain.replace(/\.+$/, "").toLowerCase() : "";
+ let currentDomain = domain;
// 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;
@@ -169,18 +202,47 @@
function getDefaultStyleSheet()
{
if (!defaultStyleSheet)
defaultStyleSheet = createStyleSheet(getUnconditionalSelectors());
return defaultStyleSheet;
}
-ElemHideExceptions.on("added", ({selector}) =>
+/**
+ * Returns the common style sheet that applies on all unknown domains.
+ * @returns {string}
+ */
+function getCommonStyleSheet()
{
+ if (!commonStyleSheet)
+ {
+ commonStyleSheet = getDefaultStyleSheet() +
+ createStyleSheet(getConditionalSelectors("", false));
+ }
+
+ return commonStyleSheet;
+}
+
+ElemHideExceptions.on("added", ({domains, selector}) =>
+{
+ commonStyleSheet = null;
+
+ if (domains)
+ {
+ for (let domain of domains.keys())
+ {
+ // Note: Once an exception domain is known it never becomes unknown, even
+ // when all the exceptions containing that domain are removed. This is a
+ // 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)
{
addToFiltersByDomain(unconditionalFilterForSelector);
filterBySelector.delete(selector);
unconditionalSelectors = null;
@@ -193,34 +255,41 @@
* @class
*/
exports.ElemHide = {
/**
* Removes all known filters
*/
clear()
{
- for (let collection of [filtersByDomain, filterBySelector, knownFilters])
+ commonStyleSheet = null;
+
+ for (let collection of [filtersByDomain, filterBySelector, knownFilters,
+ 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))
return;
+ commonStyleSheet = 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;
defaultStyleSheet = null;
@@ -239,16 +308,18 @@
* Removes an element hiding filter
* @param {ElemHideFilter} filter
*/
remove(filter)
{
if (!knownFilters.has(filter))
return;
+ commonStyleSheet = null;
+
let {selector} = filter;
// Unconditially applied element hiding filters
if (filterBySelector.get(selector) == filter)
{
filterBySelector.delete(selector);
unconditionalSelectors = null;
defaultStyleSheet = null;
@@ -288,22 +359,39 @@
* @param {boolean} [specificOnly=false] Whether selectors from generic
* filters should be included.
*
* @returns {ElemHideStyleSheet} An object containing the CSS code and the
* list of selectors.
*/
generateStyleSheetForDomain(domain, specificOnly = false)
{
- let selectors = getConditionalSelectorsForDomain(domain, specificOnly);
- let code = specificOnly ? createStyleSheet(selectors) :
- (getDefaultStyleSheet() + createStyleSheet(selectors));
+ let code = null;
+ let selectors = null;
+
+ if (domain[domain.length - 1] == ".")
+ domain = domain.replace(/\.+$/, "");
+
+ domain = domain.toLowerCase();
- if (!specificOnly)
+ if (specificOnly)
+ {
+ selectors = getConditionalSelectors(domain, true);
+ code = createStyleSheet(selectors);
+ }
+ else
+ {
+ let knownSuffix = getKnownSuffix(domain);
+
+ selectors = getConditionalSelectors(knownSuffix, false);
+ code = knownSuffix == "" ? getCommonStyleSheet() :
+ (getDefaultStyleSheet() + createStyleSheet(selectors));
+
selectors = getUnconditionalSelectors().concat(selectors);
+ }
return {code, selectors};
}
};
/**
* Splits a list of selectors into groups determined by the value of
* <code>{@link selectorGroupSize}</code>.
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld