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

Unified Diff: lib/content/elemHideEmulation.js

Issue 29714601: Issue 6437 - Skip elements not affected by DOM mutations (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Rebase Created March 20, 2018, 10:04 a.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
@@ -197,30 +197,30 @@
return new RegExp(pattern, flags);
}
catch (e)
{
}
return null;
}
-function* evaluate(chain, index, prefix, subtree, styles)
+function* evaluate(chain, index, prefix, subtree, styles, targets)
{
if (index >= chain.length)
{
yield prefix;
return;
}
for (let [selector, element] of
- chain[index].getSelectors(prefix, subtree, styles))
+ chain[index].getSelectors(prefix, subtree, styles, targets))
{
if (selector == null)
yield null;
else
- yield* evaluate(chain, index + 1, selector, element, styles);
+ yield* evaluate(chain, index + 1, selector, element, styles, targets);
}
// Just in case the getSelectors() generator above had to run some heavy
// document.querySelectorAll() call which didn't produce any results, make
// sure there is at least one point where execution can pause.
yield null;
}
function PlainSelector(selector)
@@ -231,18 +231,19 @@
PlainSelector.prototype = {
/**
* Generator function returning a pair of selector
* string and subtree.
* @param {string} prefix the prefix for the selector.
* @param {Node} subtree the subtree we work on.
* @param {StringifiedStyle[]} styles the stringified style objects.
+ * @param {Node[]} [targets] the nodes we are interested in.
*/
- *getSelectors(prefix, subtree, styles)
+ *getSelectors(prefix, subtree, styles, targets)
{
yield [prefix + this._selector, subtree];
}
};
const incompletePrefixRegexp = /[\s>+~]$/;
function HasSelector(selectors)
@@ -268,38 +269,49 @@
get maybeDependsOnAttributes()
{
return this._innerSelectors.some(
selector => selector.maybeDependsOnAttributes
);
},
- *getSelectors(prefix, subtree, styles)
+ *getSelectors(prefix, subtree, styles, targets)
{
- for (let element of this.getElements(prefix, subtree, styles))
+ for (let element of this.getElements(prefix, subtree, styles, targets))
yield [makeSelector(element, ""), element];
},
/**
* Generator function returning selected elements.
* @param {string} prefix the prefix for the selector.
* @param {Node} subtree the subtree we work on.
* @param {StringifiedStyle[]} styles the stringified style objects.
+ * @param {Node[]} [targets] the nodes we are interested in.
*/
- *getElements(prefix, subtree, styles)
+ *getElements(prefix, subtree, styles, targets)
{
let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
prefix + "*" : prefix;
let elements = scopedQuerySelectorAll(subtree, actualPrefix);
if (elements)
{
for (let element of elements)
{
- let iter = evaluate(this._innerSelectors, 0, "", element, styles);
+ // If the element is neither an ancestor nor a descendant of one of the
+ // targets, we can skip it.
+ if (targets && !targets.some(target => element.contains(target) ||
+ target.contains(element)))
+ {
+ yield null;
+ continue;
+ }
+
+ let iter = evaluate(this._innerSelectors, 0, "", element, styles,
+ targets);
for (let selector of iter)
{
if (selector == null)
yield null;
else if (scopedQuerySelector(element, selector))
yield element;
}
yield null;
@@ -313,32 +325,39 @@
this._regexp = makeRegExpParameter(textContent);
}
ContainsSelector.prototype = {
requiresHiding: true,
dependsOnDOM: true,
dependsOnCharacterData: true,
- *getSelectors(prefix, subtree, styles)
+ *getSelectors(prefix, subtree, styles, targets)
{
- for (let element of this.getElements(prefix, subtree, styles))
+ for (let element of this.getElements(prefix, subtree, styles, targets))
yield [makeSelector(element, ""), subtree];
},
- *getElements(prefix, subtree, styles)
+ *getElements(prefix, subtree, styles, targets)
{
let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
prefix + "*" : prefix;
let elements = scopedQuerySelectorAll(subtree, actualPrefix);
if (elements)
{
for (let element of elements)
{
+ if (targets && !targets.some(target => element.contains(target) ||
Manish Jethani 2018/03/20 10:21:57 By the way this may not seem like such a great ide
+ target.contains(element)))
+ {
+ yield null;
+ continue;
+ }
+
if (this._regexp && this._regexp.test(element.textContent))
yield element;
else
yield null;
}
}
}
};
@@ -375,17 +394,17 @@
}
let idx = subSelector.lastIndexOf("::");
if (idx != -1)
subSelector = subSelector.substr(0, idx);
yield prefix + subSelector;
}
},
- *getSelectors(prefix, subtree, styles)
+ *getSelectors(prefix, subtree, styles, targets)
{
for (let selector of this.findPropsSelectors(styles, prefix, this._regexp))
yield [selector, subtree];
}
};
function Pattern(selectors, text)
{
@@ -451,16 +470,47 @@
// one of the patterns.
return getCachedPropertyValue(
this, "_dependsOnCharacterData",
() => this.selectors.some(selector => selector.dependsOnCharacterData)
);
}
};
+function extractMutationTargets(mutations)
+{
+ if (!mutations)
+ return null;
+
+ let targets = new Set();
+
+ for (let mutation of mutations)
+ {
+ if (mutation.type == "childList")
+ {
+ // When new nodes are added, we're interested in the added nodes rather
+ // than the parent.
+ for (let node of mutation.addedNodes)
+ targets.add(node);
+
+ // Ideally we would also be interested in removed nodes, but since we
+ // never unhide an element once hidden we can simply ignore any removed
+ // nodes. Note that this will change once we start using CSS selectors
+ // for -abp-has and -abp-contains, i.e. we'll have to remove the
+ // selectors for any removed nodes.
+ }
+ else
+ {
+ targets.add(mutation.target);
+ }
+ }
+
+ return [...targets];
+}
+
function filterPatterns(patterns, {stylesheets, mutations})
{
if (!stylesheets && !mutations)
return patterns.slice();
return patterns.filter(
pattern => (stylesheets && pattern.dependsOnStyles) ||
(mutations && pattern.dependsOnDOM)
@@ -618,16 +668,18 @@
{
if (rule.type != rule.STYLE_RULE)
continue;
cssStyles.push(stringifyStyle(rule));
}
}
+ let targets = extractMutationTargets(mutations);
+
let pattern = null;
let generator = null;
let processPatterns = () =>
{
let cycleStart = performance.now();
if (!pattern)
@@ -641,17 +693,17 @@
if (typeof done == "function")
done();
return;
}
pattern = patterns.shift();
generator = evaluate(pattern.selectors, 0, "",
- this.document, cssStyles);
+ this.document, cssStyles, targets);
}
for (let selector of generator)
{
if (selector != null)
{
if (pattern.isSelectorHidingOnlyPattern())
{
selectors.push(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