Index: lib/elemHide.js |
=================================================================== |
--- a/lib/elemHide.js |
+++ b/lib/elemHide.js |
@@ -20,16 +20,25 @@ |
/** |
* @fileOverview Element hiding implementation. |
*/ |
const {ElemHideExceptions} = require("./elemHideExceptions"); |
const {filterNotifier} = require("./filterNotifier"); |
/** |
+ * The maximum number of selectors in a CSS rule. This is used by |
+ * <code>{@link ElemHide.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>>} |
*/ |
let filtersByDomain = new Map(); |
/** |
* Lookup table, filter by selector. (Only used for selectors that are |
@@ -55,16 +64,54 @@ |
/** |
* Set containing known element hiding filters |
* @type {Set.<ElemHideFilter>} |
*/ |
let knownFilters = new Set(); |
/** |
+ * Splits a list of selectors into groups determined by the value of |
+ * <code>{@link selectorGroupSize}</code>. |
+ * |
+ * @param {Array.<string>} selectors |
+ * @yields {Array.<string>} |
+ */ |
+function* splitSelectors(selectors) |
+{ |
+ // Chromium's Blink engine supports only up to 8,192 simple selectors, and |
+ // even fewer compound selectors, in a rule. The exact number of selectors |
+ // that would work depends on their sizes (e.g. "#foo .bar" has a size of 2). |
+ // Since we don't know the sizes of the selectors here, we simply split them |
+ // into groups of 1,024, based on the reasonable assumption that the average |
+ // selector won't have a size greater than 8. The alternative would be to |
+ // calculate the sizes of the selectors and divide them up accordingly, but |
+ // this approach is more efficient and has worked well in practice. In theory |
+ // this could still lead to some selectors not working on Chromium, but it is |
+ // highly unlikely. |
+ // See issue #6298 and https://crbug.com/804179 |
+ for (let i = 0; i < selectors.length; i += selectorGroupSize) |
+ yield selectors.slice(i, i + selectorGroupSize); |
Sebastian Noack
2018/09/17 18:59:57
Apparently this code is currently already using ge
|
+} |
+ |
+/** |
+ * Creates element hiding CSS rules for a given list of selectors. Each rule |
+ * contains no more than the maximum number of selectors as determined by the |
+ * value of <code>{@link selectorGroupSize}</code>. |
+ * |
+ * @param {Array.<string>} selectors |
+ * @yields {string} |
+ */ |
+function* createRules(selectors) |
+{ |
+ for (let selectorGroup of splitSelectors(selectors)) |
+ yield selectorGroup.join(", ") + " {display: none !important;}"; |
+} |
+ |
+/** |
* 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) |
{ |
@@ -234,10 +281,20 @@ |
let nextDot = currentDomain.indexOf("."); |
currentDomain = nextDot == -1 ? "" : currentDomain.substr(nextDot + 1); |
} |
if (!specificOnly) |
selectors = getUnconditionalSelectors().concat(selectors); |
return selectors; |
+ }, |
+ |
+ /** |
+ * Creates an element hiding CSS style sheet from a given list of selectors. |
+ * @param {Array.<string>} selectors |
+ * @returns {string} |
+ */ |
+ createStyleSheet(selectors) |
+ { |
+ return [...createRules(selectors)].join("\n"); |
} |
}; |