| Index: lib/content/elemHideEmulation.js |
| =================================================================== |
| --- a/lib/content/elemHideEmulation.js |
| +++ b/lib/content/elemHideEmulation.js |
| @@ -234,73 +234,75 @@ |
| 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) |
| +class PlainSelector |
| { |
| - this._selector = selector; |
| - this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector); |
| - this.dependsOnDOM = this.maybeDependsOnAttributes; |
| - this.maybeContainsSiblingCombinators = /[~+]/.test(selector); |
| -} |
| + constructor(selector) |
| + { |
| + this._selector = selector; |
| + this.maybeDependsOnAttributes = /[#.]|\[.+\]/.test(selector); |
| + this.dependsOnDOM = this.maybeDependsOnAttributes; |
| + this.maybeContainsSiblingCombinators = /[~+]/.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. |
| * @param {StringifiedStyle[]} styles the stringified style objects. |
| * @param {Node[]} [targets] the nodes we are interested in. |
| */ |
| *getSelectors(prefix, subtree, styles, targets) |
| { |
| yield [prefix + this._selector, subtree]; |
| } |
| -}; |
| +} |
| const incompletePrefixRegexp = /[\s>+~]$/; |
| -function HasSelector(selectors) |
| +class HasSelector |
| { |
| - this._innerSelectors = selectors; |
| -} |
| + constructor(selectors) |
| + { |
| + this.dependsOnDOM = true; |
| -HasSelector.prototype = { |
| - dependsOnDOM: true, |
| + this._innerSelectors = selectors; |
| + } |
| 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, targets) |
| { |
| 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. |
| */ |
| @@ -333,32 +335,33 @@ |
| } |
| yield null; |
| if (testInfo) |
| testInfo.lastProcessedElements.add(element); |
| } |
| } |
| } |
| -}; |
| - |
| -function ContainsSelector(textContent) |
| -{ |
| - this._regexp = makeRegExpParameter(textContent); |
| } |
| -ContainsSelector.prototype = { |
| - dependsOnDOM: true, |
| - dependsOnCharacterData: true, |
| +class ContainsSelector |
| +{ |
| + constructor(textContent) |
| + { |
| + this.dependsOnDOM = true; |
| + this.dependsOnCharacterData = true; |
| + |
| + this._regexp = makeRegExpParameter(textContent); |
| + } |
| *getSelectors(prefix, subtree, styles, targets) |
| { |
| for (let element of this.getElements(prefix, subtree, styles, targets)) |
| yield [makeSelector(element), subtree]; |
| - }, |
| + } |
| *getElements(prefix, subtree, styles, targets) |
| { |
| let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ? |
| prefix + "*" : prefix; |
| let elements = scopedQuerySelectorAll(subtree, actualPrefix); |
| @@ -390,36 +393,37 @@ |
| else |
| yield null; |
| if (testInfo) |
| testInfo.lastProcessedElements.add(element); |
| } |
| } |
| } |
| -}; |
| - |
| -function PropsSelector(propertyExpression) |
| -{ |
| - let regexpString; |
| - if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && |
| - propertyExpression[propertyExpression.length - 1] == "/") |
| - { |
| - regexpString = propertyExpression.slice(1, -1) |
| - .replace("\\7B ", "{").replace("\\7D ", "}"); |
| - } |
| - else |
| - regexpString = filterToRegExp(propertyExpression); |
| - |
| - this._regexp = new RegExp(regexpString, "i"); |
| } |
| -PropsSelector.prototype = { |
| - dependsOnStyles: true, |
| - dependsOnDOM: true, |
| +class PropsSelector |
| +{ |
| + constructor(propertyExpression) |
| + { |
| + this.dependsOnStyles = true; |
| + this.dependsOnDOM = true; |
| + |
| + let regexpString; |
| + if (propertyExpression.length >= 2 && propertyExpression[0] == "/" && |
| + propertyExpression[propertyExpression.length - 1] == "/") |
| + { |
| + regexpString = propertyExpression.slice(1, -1) |
| + .replace("\\7B ", "{").replace("\\7D ", "}"); |
| + } |
| + else |
| + regexpString = filterToRegExp(propertyExpression); |
| + |
| + this._regexp = new RegExp(regexpString, "i"); |
| + } |
| *findPropsSelectors(styles, prefix, regexp) |
| { |
| for (let style of styles) |
| if (regexp.test(style.style)) |
| for (let subSelector of style.subSelectors) |
| { |
| if (subSelector.startsWith("*") && |
| @@ -427,92 +431,93 @@ |
| { |
| subSelector = subSelector.substr(1); |
| } |
| let idx = subSelector.lastIndexOf("::"); |
| if (idx != -1) |
| subSelector = subSelector.substr(0, idx); |
| yield qualifySelector(subSelector, prefix); |
| } |
| - }, |
| + } |
| *getSelectors(prefix, subtree, styles, targets) |
| { |
| for (let selector of this.findPropsSelectors(styles, prefix, this._regexp)) |
| yield [selector, subtree]; |
| } |
| -}; |
| - |
| -function Pattern(selectors, text) |
| -{ |
| - this.selectors = selectors; |
| - this.text = text; |
| } |
| -Pattern.prototype = { |
| +class Pattern |
| +{ |
| + constructor(selectors, text) |
| + { |
| + this.selectors = selectors; |
| + this.text = text; |
| + } |
| + |
| get dependsOnStyles() |
| { |
| return getCachedPropertyValue( |
| this, "_dependsOnStyles", |
| () => this.selectors.some(selector => selector.dependsOnStyles) |
| ); |
| - }, |
| + } |
| get dependsOnDOM() |
| { |
| return getCachedPropertyValue( |
| this, "_dependsOnDOM", |
| () => this.selectors.some(selector => selector.dependsOnDOM) |
| ); |
| - }, |
| + } |
| get dependsOnStylesAndDOM() |
| { |
| return getCachedPropertyValue( |
| this, "_dependsOnStylesAndDOM", |
| () => this.selectors.some(selector => selector.dependsOnStyles && |
| selector.dependsOnDOM) |
| ); |
| - }, |
| + } |
| get maybeDependsOnAttributes() |
| { |
| // 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 getCachedPropertyValue( |
| this, "_maybeDependsOnAttributes", |
| () => this.selectors.some( |
| selector => selector.maybeDependsOnAttributes || |
| (selector instanceof HasSelector && |
| selector.dependsOnStyles) |
| ) |
| ); |
| - }, |
| + } |
| get dependsOnCharacterData() |
| { |
| // Observe changes to character data only if there's a contains selector in |
| // one of the patterns. |
| return getCachedPropertyValue( |
| this, "_dependsOnCharacterData", |
| () => this.selectors.some(selector => selector.dependsOnCharacterData) |
| ); |
| - }, |
| + } |
| get maybeContainsSiblingCombinators() |
| { |
| return getCachedPropertyValue( |
| this, "_maybeContainsSiblingCombinators", |
| () => this.selectors.some(selector => |
| selector.maybeContainsSiblingCombinators) |
| ); |
| - }, |
| + } |
| matchesMutationTypes(mutationTypes) |
| { |
| let mutationTypeMatchMap = getCachedPropertyValue( |
| this, "_mutationTypeMatchMap", |
| () => new Map([ |
| // All types of DOM-dependent patterns are affected by mutations of |
| // type "childList". |
| @@ -525,17 +530,17 @@ |
| for (let mutationType of mutationTypes) |
| { |
| if (mutationTypeMatchMap.get(mutationType)) |
| return true; |
| } |
| return false; |
| } |
| -}; |
| +} |
| function extractMutationTypes(mutations) |
| { |
| let types = new Set(); |
| for (let mutation of mutations) |
| { |
| types.add(mutation.type); |
| @@ -593,37 +598,42 @@ |
| return patterns.some(pattern => pattern.maybeDependsOnAttributes); |
| } |
| function shouldObserveCharacterData(patterns) |
| { |
| return patterns.some(pattern => pattern.dependsOnCharacterData); |
| } |
| -function ElemHideEmulation(addSelectorsFunc, hideElemsFunc) |
| +class ElemHideEmulation |
| { |
| - this.document = document; |
| - this.addSelectorsFunc = addSelectorsFunc; |
| - this.hideElemsFunc = hideElemsFunc; |
| - this.observer = new MutationObserver(this.observe.bind(this)); |
| -} |
| + constructor(addSelectorsFunc, hideElemsFunc) |
| + { |
| + this._filteringInProgress = false; |
| + this._lastInvocation = -MIN_INVOCATION_INTERVAL; |
| + this._scheduledProcessing = null; |
| -ElemHideEmulation.prototype = { |
| + this.document = document; |
| + this.addSelectorsFunc = addSelectorsFunc; |
| + this.hideElemsFunc = hideElemsFunc; |
| + this.observer = new MutationObserver(this.observe.bind(this)); |
| + } |
| + |
| isSameOrigin(stylesheet) |
| { |
| try |
| { |
| return new URL(stylesheet.href).origin == this.document.location.origin; |
| } |
| catch (e) |
| { |
| // Invalid URL, assume that it is first-party. |
| return true; |
| } |
| - }, |
| + } |
| /** Parse the selector |
| * @param {string} selector the selector to parse |
| * @return {Array} selectors is an array of objects, |
| * or null in case of errors. |
| */ |
| parseSelector(selector) |
| { |
| @@ -676,17 +686,17 @@ |
| if (selectors.length == 1 && selectors[0] instanceof ContainsSelector) |
| { |
| console.error(new SyntaxError("Failed to parse Adblock Plus " + |
| `selector ${selector}, can't ` + |
| "have a lonely :-abp-contains().")); |
| return null; |
| } |
| return selectors; |
| - }, |
| + } |
| /** |
| * 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. |
| @@ -812,33 +822,29 @@ |
| return; |
| } |
| } |
| pattern = null; |
| return processPatterns(); |
| }; |
| processPatterns(); |
| - }, |
| + } |
| // This property is only used in the tests |
| // to shorten the invocation interval |
| get MIN_INVOCATION_INTERVAL() |
| { |
| return MIN_INVOCATION_INTERVAL; |
| - }, |
| + } |
| set MIN_INVOCATION_INTERVAL(interval) |
| { |
| MIN_INVOCATION_INTERVAL = interval; |
| - }, |
| - |
| - _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 full reprocessing. |
| * @param {MutationRecord[]} [mutations] |
| * new DOM mutations to be processed. This parameter should be omitted |
| @@ -910,24 +916,24 @@ |
| }; |
| this.document.addEventListener("DOMContentLoaded", handler); |
| } |
| else |
| { |
| this._filteringInProgress = true; |
| this._addSelectors(stylesheets, mutations, completion); |
| } |
| - }, |
| + } |
| onLoad(event) |
| { |
| let stylesheet = event.target.sheet; |
| if (stylesheet) |
| this.queueFiltering([stylesheet]); |
| - }, |
| + } |
| observe(mutations) |
| { |
| if (testInfo) |
| { |
| // In test mode, filter out any mutations likely done by us |
| // (i.e. style="display: none !important"). This makes it easier to |
| // observe how the code responds to DOM mutations. |
| @@ -937,17 +943,17 @@ |
| newValue.display == "none" && oldValue.display != "none") |
| ); |
| if (mutations.length == 0) |
| return; |
| } |
| this.queueFiltering(null, mutations); |
| - }, |
| + } |
| apply(patterns) |
| { |
| this.patterns = []; |
| for (let pattern of patterns) |
| { |
| let selectors = this.parseSelector(pattern.selector); |
| if (selectors != null && selectors.length > 0) |
| @@ -964,11 +970,11 @@ |
| attributes: shouldObserveAttributes(this.patterns), |
| characterData: shouldObserveCharacterData(this.patterns), |
| subtree: true |
| } |
| ); |
| this.document.addEventListener("load", this.onLoad.bind(this), true); |
| } |
| } |
| -}; |
| +} |
| exports.ElemHideEmulation = ElemHideEmulation; |