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

Unified Diff: lib/content/elemHideEmulation.js

Issue 29712655: Issue 6437 - Skip styles-only patterns on DOM mutations (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Minor changes Created March 1, 2018, 5:48 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/content/elemHideEmulation.js
===================================================================
--- a/lib/content/elemHideEmulation.js
+++ b/lib/content/elemHideEmulation.js
@@ -239,16 +239,17 @@
function HasSelector(selectors)
{
this._innerSelectors = selectors;
}
HasSelector.prototype = {
requiresHiding: true,
+ dependsOnDOM: true,
get dependsOnStyles()
{
return this._innerSelectors.some(selector => selector.dependsOnStyles);
},
get dependsOnCharacterData()
{
@@ -301,16 +302,17 @@
function ContainsSelector(textContent)
{
this._regexp = makeRegExpParameter(textContent);
}
ContainsSelector.prototype = {
requiresHiding: true,
+ dependsOnDOM: true,
dependsOnCharacterData: true,
*getSelectors(prefix, subtree, stylesheet)
{
for (let element of this.getElements(prefix, subtree, stylesheet))
yield [makeSelector(element, ""), subtree];
},
@@ -378,16 +380,42 @@
};
function isSelectorHidingOnlyPattern(pattern)
{
return pattern.selectors.some(s => s.preferHideWithSelector) &&
!pattern.selectors.some(s => s.requiresHiding);
}
+function patternDependsOnStyles(pattern)
+{
+ return pattern.selectors.some(s => s.dependsOnStyles);
+}
+
+function patternDependsOnDOM(pattern)
+{
+ return pattern.selectors.some(s => s.dependsOnDOM);
+}
+
+function patternDependsOnStylesAndDOM(pattern)
+{
+ return pattern.selectors.some(s => s.dependsOnStyles && s.dependsOnDOM);
+}
+
+function filterPatterns(patterns, {stylesheets, mutations})
+{
+ if (stylesheets && !mutations)
+ return patterns.filter(patternDependsOnStyles);
+
+ if (!stylesheets && mutations)
+ return patterns.filter(patternDependsOnDOM);
+
+ return patterns.slice();
+}
+
function shouldObserveAttributes(patterns)
{
// Observe changes to attributes if either there's a plain selector that
// looks like an ID selector, class selector, or attribute selector in one of
// the patterns (e.g. "a[href='https://example.com/']")
// or there's a properties selector nested inside a has selector
// (e.g. "div:-abp-has(:-abp-properties(color: blue))")
return patterns.some(
@@ -497,34 +525,51 @@
/**
* Processes the current document and applies all rules to it.
* @param {CSSStyleSheet[]} [stylesheets]
* The list of new stylesheets that have been added to the document and
* made reprocessing necessary. This parameter shouldn't be passed in for
* the initial processing, all of document's stylesheets will be considered
* then and all rules, including the ones not dependent on styles.
+ * @param {MutationRecord[]} [mutations]
+ * The list of DOM mutations that have been applied to the document and
+ * made reprocessing necessary. This parameter shouldn't be passed in for
+ * the initial processing, the entire document will be considered
+ * then and all rules, including the ones not dependent on the DOM.
* @param {function} [done]
* Callback to call when done.
*/
- _addSelectors(stylesheets, done)
+ _addSelectors(stylesheets, mutations, done)
{
+ let patterns = filterPatterns(this.patterns, {stylesheets, mutations});
+
let selectors = [];
let selectorFilters = [];
let elements = [];
let elementFilters = [];
let cssStyles = [];
- let stylesheetOnlyChange = !!stylesheets;
- if (!stylesheets)
+ // If neither any style sheets nor any DOM mutations have been specified,
+ // do full processing.
+ if (!stylesheets && !mutations)
stylesheets = this.document.styleSheets;
- for (let stylesheet of stylesheets)
+ // If there are any DOM mutations and any of the patterns depends on both
+ // style sheets and the DOM (e.g. -abp-has(-abp-properties)), find all the
+ // rules in every style sheet in the document, because we need to run
+ // querySelectorAll afterwards. On the other hand, if we only have patterns
+ // that depend on either styles or DOM both not both
+ // (e.g. -abp-properties or -abp-contains), we can skip this part.
+ if (mutations && patterns.some(patternDependsOnStylesAndDOM))
+ stylesheets = this.document.styleSheets;
+
+ for (let stylesheet of stylesheets || [])
{
// Explicitly ignore third-party stylesheets to ensure consistent behavior
// between Firefox and Chrome.
if (!this.isSameOrigin(stylesheet))
continue;
let rules = stylesheet.cssRules;
if (!rules)
@@ -534,17 +579,16 @@
{
if (rule.type != rule.STYLE_RULE)
continue;
cssStyles.push(stringifyStyle(rule));
}
}
- let patterns = this.patterns.slice();
let pattern = null;
let generator = null;
let processPatterns = () =>
{
let cycleStart = performance.now();
if (!pattern)
@@ -555,22 +599,16 @@
this.hideElemsFunc(elements, elementFilters);
if (typeof done == "function")
done();
return;
}
pattern = patterns.shift();
- if (stylesheetOnlyChange &&
- !pattern.selectors.some(selector => selector.dependsOnStyles))
- {
- pattern = null;
- return processPatterns();
- }
generator = evaluate(pattern.selectors, 0, "",
this.document, cssStyles);
}
for (let selector of generator)
{
if (selector != null)
{
if (isSelectorHidingOnlyPattern(pattern))
@@ -615,85 +653,103 @@
_filteringInProgress: false,
_lastInvocation: -MIN_INVOCATION_INTERVAL,
_scheduledProcessing: null,
/**
* Re-run filtering either immediately or queued.
* @param {CSSStyleSheet[]} [stylesheets]
* new stylesheets to be processed. This parameter should be omitted
- * for DOM modification (full reprocessing required).
+ * for full reprocessing.
+ * @param {MutationRecord[]} [mutations]
+ * new DOM mutations to be processed. This parameter should be omitted
+ * for full reprocessing.
*/
- queueFiltering(stylesheets)
+ queueFiltering(stylesheets, mutations)
{
let completion = () =>
{
this._lastInvocation = performance.now();
this._filteringInProgress = false;
if (this._scheduledProcessing)
{
- let newStylesheets = this._scheduledProcessing.stylesheets;
+ let {stylesheets, mutations} = this._scheduledProcessing;
this._scheduledProcessing = null;
- this.queueFiltering(newStylesheets);
+ this.queueFiltering(stylesheets, mutations);
}
};
if (this._scheduledProcessing)
{
- if (!stylesheets)
- this._scheduledProcessing.stylesheets = null;
- else if (this._scheduledProcessing.stylesheets)
- this._scheduledProcessing.stylesheets.push(...stylesheets);
+ if (!stylesheets && !mutations)
+ {
+ this._scheduledProcessing = {};
+ }
+ else
+ {
+ if (stylesheets)
+ {
+ if (!this._scheduledProcessing.stylesheets)
+ this._scheduledProcessing.stylesheets = [];
+ this._scheduledProcessing.stylesheets.push(...stylesheets);
+ }
+ if (mutations)
+ {
+ if (!this._scheduledProcessing.mutations)
+ this._scheduledProcessing.mutations = [];
+ this._scheduledProcessing.mutations.push(...mutations);
+ }
+ }
}
else if (this._filteringInProgress)
{
- this._scheduledProcessing = {stylesheets};
+ this._scheduledProcessing = {stylesheets, mutations};
}
else if (performance.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL)
{
- this._scheduledProcessing = {stylesheets};
+ this._scheduledProcessing = {stylesheets, mutations};
setTimeout(() =>
{
- let newStylesheets = this._scheduledProcessing.stylesheets;
+ let {stylesheets, mutations} = this._scheduledProcessing;
this._filteringInProgress = true;
this._scheduledProcessing = null;
- this._addSelectors(newStylesheets, completion);
+ this._addSelectors(stylesheets, mutations, completion);
},
MIN_INVOCATION_INTERVAL - (performance.now() - this._lastInvocation));
}
else if (this.document.readyState == "loading")
{
- this._scheduledProcessing = {stylesheets};
+ this._scheduledProcessing = {stylesheets, mutations};
let handler = () =>
{
document.removeEventListener("DOMContentLoaded", handler);
- let newStylesheets = this._scheduledProcessing.stylesheets;
+ let {stylesheets, mutations} = this._scheduledProcessing;
this._filteringInProgress = true;
this._scheduledProcessing = null;
- this._addSelectors(newStylesheets, completion);
+ this._addSelectors(stylesheets, mutations, completion);
};
document.addEventListener("DOMContentLoaded", handler);
}
else
{
this._filteringInProgress = true;
- this._addSelectors(stylesheets, completion);
+ this._addSelectors(stylesheets, mutations, completion);
}
},
onLoad(event)
{
let stylesheet = event.target.sheet;
if (stylesheet)
this.queueFiltering([stylesheet]);
},
observe(mutations)
{
- this.queueFiltering();
+ this.queueFiltering(null, mutations);
},
apply(patterns)
{
this.patterns = [];
for (let pattern of patterns)
{
let selectors = this.parseSelector(pattern.selector);
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld