Index: lib/content/elemHideEmulation.js |
=================================================================== |
--- a/lib/content/elemHideEmulation.js |
+++ b/lib/content/elemHideEmulation.js |
@@ -190,16 +190,17 @@ |
// 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) |
{ |
this._selector = selector; |
+ this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector); |
} |
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. |
@@ -221,16 +222,30 @@ |
HasSelector.prototype = { |
requiresHiding: true, |
get dependsOnStyles() |
{ |
return this._innerSelectors.some(selector => selector.dependsOnStyles); |
}, |
+ get dependsOnCharacterData() |
+ { |
+ return this._innerSelectors.some( |
+ selector => selector.dependsOnCharacterData |
+ ); |
+ }, |
+ |
+ get maybeDependsOnAttributes() |
+ { |
+ return this._innerSelectors.some( |
+ selector => selector.maybeDependsOnAttributes |
+ ); |
+ }, |
+ |
*getSelectors(prefix, subtree, styles) |
{ |
for (let element of this.getElements(prefix, subtree, styles)) |
yield [makeSelector(element, ""), element]; |
}, |
/** |
* Generator function returning selected elements. |
@@ -263,16 +278,17 @@ |
function ContainsSelector(textContent) |
{ |
this._text = textContent; |
} |
ContainsSelector.prototype = { |
requiresHiding: true, |
+ dependsOnCharacterData: true, |
*getSelectors(prefix, subtree, stylesheet) |
{ |
for (let element of this.getElements(prefix, subtree, stylesheet)) |
yield [makeSelector(element, ""), subtree]; |
}, |
*getElements(prefix, subtree, stylesheet) |
@@ -339,16 +355,43 @@ |
}; |
function isSelectorHidingOnlyPattern(pattern) |
{ |
return pattern.selectors.some(s => s.preferHideWithSelector) && |
!pattern.selectors.some(s => s.requiresHiding); |
} |
+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( |
+ pattern => pattern.selectors.some( |
+ selector => selector.maybeDependsOnAttributes || |
+ (selector instanceof HasSelector && |
+ selector.dependsOnStyles) |
+ ) |
+ ); |
+} |
+ |
+function shouldObserveCharacterData(patterns) |
+{ |
+ // Observe changes to character data only if there's a contains selector in |
+ // one of the patterns. |
+ return patterns.some( |
+ pattern => pattern.selectors.some( |
+ selector => selector.dependsOnCharacterData |
+ ) |
+ ); |
+} |
+ |
function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) |
{ |
this.document = document; |
this.addSelectorsFunc = addSelectorsFunc; |
this.hideElemsFunc = hideElemsFunc; |
this.observer = new MutationObserver(this.observe.bind(this)); |
} |
@@ -637,18 +680,18 @@ |
if (this.patterns.length > 0) |
{ |
this.queueFiltering(); |
this.observer.observe( |
this.document, |
{ |
childList: true, |
- attributes: true, |
- characterData: true, |
+ attributes: shouldObserveAttributes(this.patterns), |
+ characterData: shouldObserveCharacterData(this.patterns), |
subtree: true |
} |
); |
this.document.addEventListener("load", this.onLoad.bind(this), true); |
} |
} |
}; |