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

Unified Diff: chrome/content/elemHideEmulation.js

Issue 29517687: Issue 5079, 5516 - Use webpack for browser tests, modules for content scripts (Closed)
Patch Set: Addressed Wladimir's and Hubert's initial feedback Created Aug. 17, 2017, 12:36 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
Index: chrome/content/elemHideEmulation.js
diff --git a/chrome/content/elemHideEmulation.js b/chrome/content/elemHideEmulation.js
deleted file mode 100644
index 447a479a6fc9e835291506c7a3127b809d17a4de..0000000000000000000000000000000000000000
--- a/chrome/content/elemHideEmulation.js
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * This file is part of Adblock Plus <https://adblockplus.org/>,
- * Copyright (C) 2006-2017 eyeo GmbH
- *
- * Adblock Plus is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3 as
- * published by the Free Software Foundation.
- *
- * Adblock Plus is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/* globals filterToRegExp */
-
-"use strict";
-
-const MIN_INVOCATION_INTERVAL = 3000;
-const abpSelectorRegexp = /:-abp-([\w-]+)\(/i;
-
-function splitSelector(selector)
-{
- if (selector.indexOf(",") == -1)
- return [selector];
-
- let selectors = [];
- let start = 0;
- let level = 0;
- let sep = "";
-
- for (let i = 0; i < selector.length; i++)
- {
- let chr = selector[i];
-
- if (chr == "\\") // ignore escaped characters
- i++;
- else if (chr == sep) // don't split within quoted text
- sep = ""; // e.g. [attr=","]
- else if (sep == "")
- {
- if (chr == '"' || chr == "'")
- sep = chr;
- else if (chr == "(") // don't split between parentheses
- level++; // e.g. :matches(div,span)
- else if (chr == ")")
- level = Math.max(0, level - 1);
- else if (chr == "," && level == 0)
- {
- selectors.push(selector.substring(start, i));
- start = i + 1;
- }
- }
- }
-
- selectors.push(selector.substring(start));
- return selectors;
-}
-
-/** Return position of node from parent.
- * @param {Node} node the node to find the position of.
- * @return {number} One-based index like for :nth-child(), or 0 on error.
- */
-function positionInParent(node)
-{
- let {children} = node.parentNode;
- for (let i = 0; i < children.length; i++)
- if (children[i] == node)
- return i + 1;
- return 0;
-}
-
-function makeSelector(node, selector)
-{
- if (!node.parentElement)
- {
- let newSelector = ":root";
- if (selector)
- newSelector += " > " + selector;
- return newSelector;
- }
- let idx = positionInParent(node);
- if (idx > 0)
- {
- let newSelector = `${node.tagName}:nth-child(${idx})`;
- if (selector)
- newSelector += " > " + selector;
- return makeSelector(node.parentElement, newSelector);
- }
-
- return selector;
-}
-
-function parseSelectorContent(content, startIndex)
-{
- let parens = 1;
- let quote = null;
- let i = startIndex;
- for (; i < content.length; i++)
- {
- let c = content[i];
- if (c == "\\")
- {
- // Ignore escaped characters
- i++;
- }
- else if (quote)
- {
- if (c == quote)
- quote = null;
- }
- else if (c == "'" || c == '"')
- quote = c;
- else if (c == "(")
- parens++;
- else if (c == ")")
- {
- parens--;
- if (parens == 0)
- break;
- }
- }
-
- if (parens > 0)
- return null;
- return {text: content.substring(startIndex, i), end: i};
-}
-
-/** Stringified style objects
- * @typedef {Object} StringifiedStyle
- * @property {string} style CSS style represented by a string.
- * @property {string[]} subSelectors selectors the CSS properties apply to.
- */
-
-/**
- * Produce a string representation of the stylesheet entry.
- * @param {CSSStyleRule} rule the CSS style rule.
- * @return {StringifiedStyle} the stringified style.
- */
-function stringifyStyle(rule)
-{
- let styles = [];
- for (let i = 0; i < rule.style.length; i++)
- {
- let property = rule.style.item(i);
- let value = rule.style.getPropertyValue(property);
- let priority = rule.style.getPropertyPriority(property);
- styles.push(`${property}: ${value}${priority ? " !" + priority : ""};`);
- }
- styles.sort();
- return {
- style: styles.join(" "),
- subSelectors: splitSelector(rule.selectorText)
- };
-}
-
-function* evaluate(chain, index, prefix, subtree, styles)
-{
- if (index >= chain.length)
- {
- yield prefix;
- return;
- }
- for (let [selector, element] of
- chain[index].getSelectors(prefix, subtree, styles))
- yield* evaluate(chain, index + 1, selector, element, styles);
-}
-
-function PlainSelector(selector)
-{
- this._selector = 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.
- */
- *getSelectors(prefix, subtree, styles)
- {
- yield [prefix + this._selector, subtree];
- }
-};
-
-const incompletePrefixRegexp = /[\s>+~]$/;
-
-function HasSelector(selectors)
-{
- this._innerSelectors = selectors;
-}
-
-HasSelector.prototype = {
- requiresHiding: true,
-
- get dependsOnStyles()
- {
- return this._innerSelectors.some(selector => selector.dependsOnStyles);
- },
-
- *getSelectors(prefix, subtree, styles)
- {
- for (let element of this.getElements(prefix, subtree, styles))
- 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.
- */
- *getElements(prefix, subtree, styles)
- {
- let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
- prefix + "*" : prefix;
- let elements = subtree.querySelectorAll(actualPrefix);
- for (let element of elements)
- {
- let iter = evaluate(this._innerSelectors, 0, "", element, styles);
- for (let selector of iter)
- if (element.querySelector(selector))
- yield element;
- }
- }
-};
-
-function ContainsSelector(textContent)
-{
- this._text = textContent;
-}
-
-ContainsSelector.prototype = {
- requiresHiding: true,
-
- *getSelectors(prefix, subtree, stylesheet)
- {
- for (let element of this.getElements(prefix, subtree, stylesheet))
- yield [makeSelector(element, ""), subtree];
- },
-
- *getElements(prefix, subtree, stylesheet)
- {
- let actualPrefix = (!prefix || incompletePrefixRegexp.test(prefix)) ?
- prefix + "*" : prefix;
- let elements = subtree.querySelectorAll(actualPrefix);
- for (let element of elements)
- if (element.textContent.includes(this._text))
- yield element;
- }
-};
-
-function PropsSelector(propertyExpression)
-{
- let regexpString;
- if (propertyExpression.length >= 2 && propertyExpression[0] == "/" &&
- propertyExpression[propertyExpression.length - 1] == "/")
- {
- regexpString = propertyExpression.slice(1, -1)
- .replace("\\x7B ", "{").replace("\\x7D ", "}");
- }
- else
- regexpString = filterToRegExp(propertyExpression);
-
- this._regexp = new RegExp(regexpString, "i");
-}
-
-PropsSelector.prototype = {
- preferHideWithSelector: true,
- dependsOnStyles: true,
-
- *findPropsSelectors(styles, prefix, regexp)
- {
- for (let style of styles)
- if (regexp.test(style.style))
- for (let subSelector of style.subSelectors)
- {
- let idx = subSelector.lastIndexOf("::");
- if (idx != -1)
- subSelector = subSelector.substr(0, idx);
- yield prefix + subSelector;
- }
- },
-
- *getSelectors(prefix, subtree, styles)
- {
- for (let selector of this.findPropsSelectors(styles, prefix, this._regexp))
- yield [selector, subtree];
- }
-};
-
-function ElemHideEmulation(window, getFiltersFunc, addSelectorsFunc,
- hideElemsFunc)
-{
- this.window = window;
- this.getFiltersFunc = getFiltersFunc;
- this.addSelectorsFunc = addSelectorsFunc;
- this.hideElemsFunc = hideElemsFunc;
-}
-
-ElemHideEmulation.prototype = {
- isSameOrigin(stylesheet)
- {
- try
- {
- return new URL(stylesheet.href).origin == this.window.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)
- {
- if (selector.length == 0)
- return [];
-
- let match = abpSelectorRegexp.exec(selector);
- if (!match)
- return [new PlainSelector(selector)];
-
- let selectors = [];
- if (match.index > 0)
- selectors.push(new PlainSelector(selector.substr(0, match.index)));
-
- let startIndex = match.index + match[0].length;
- let content = parseSelectorContent(selector, startIndex);
- if (!content)
- {
- this.window.console.error(
- new SyntaxError("Failed to parse Adblock Plus " +
- `selector ${selector} ` +
- "due to unmatched parentheses."));
- return null;
- }
- if (match[1] == "properties")
- selectors.push(new PropsSelector(content.text));
- else if (match[1] == "has")
- {
- let hasSelectors = this.parseSelector(content.text);
- if (hasSelectors == null)
- return null;
- selectors.push(new HasSelector(hasSelectors));
- }
- else if (match[1] == "contains")
- selectors.push(new ContainsSelector(content.text));
- else
- {
- // this is an error, can't parse selector.
- this.window.console.error(
- new SyntaxError("Failed to parse Adblock Plus " +
- `selector ${selector}, invalid ` +
- `pseudo-class :-abp-${match[1]}().`));
- return null;
- }
-
- let suffix = this.parseSelector(selector.substr(content.end + 1));
- if (suffix == null)
- return null;
-
- selectors.push(...suffix);
-
- if (selectors.length == 1 && selectors[0] instanceof ContainsSelector)
- {
- this.window.console.error(
- new SyntaxError("Failed to parse Adblock Plus " +
- `selector ${selector}, can't ` +
- "have a lonely :-abp-contains()."));
- return null;
- }
- return selectors;
- },
-
- _lastInvocation: 0,
-
- /**
- * 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.
- */
- addSelectors(stylesheets)
- {
- this._lastInvocation = Date.now();
-
- let selectors = [];
- let selectorFilters = [];
-
- let elements = [];
- let elementFilters = [];
-
- let cssStyles = [];
-
- let stylesheetOnlyChange = !!stylesheets;
- if (!stylesheets)
- stylesheets = this.window.document.styleSheets;
-
- // Chrome < 51 doesn't have an iterable StyleSheetList
- // https://issues.adblockplus.org/ticket/5381
- for (let i = 0; i < stylesheets.length; i++)
- {
- let stylesheet = stylesheets[i];
- // Explicitly ignore third-party stylesheets to ensure consistent behavior
- // between Firefox and Chrome.
- if (!this.isSameOrigin(stylesheet))
- continue;
-
- let rules = stylesheet.cssRules;
- if (!rules)
- continue;
-
- for (let rule of rules)
- {
- if (rule.type != rule.STYLE_RULE)
- continue;
-
- cssStyles.push(stringifyStyle(rule));
- }
- }
-
- let {document} = this.window;
- for (let pattern of this.patterns)
- {
- if (stylesheetOnlyChange &&
- !pattern.selectors.some(selector => selector.dependsOnStyles))
- {
- continue;
- }
-
- for (let selector of evaluate(pattern.selectors,
- 0, "", document, cssStyles))
- {
- if (pattern.selectors.some(s => s.preferHideWithSelector) &&
- !pattern.selectors.some(s => s.requiresHiding))
- {
- selectors.push(selector);
- selectorFilters.push(pattern.text);
- }
- else
- {
- for (let element of document.querySelectorAll(selector))
- {
- elements.push(element);
- elementFilters.push(pattern.text);
- }
- }
- }
- }
-
- this.addSelectorsFunc(selectors, selectorFilters);
- this.hideElemsFunc(elements, elementFilters);
- },
-
- _stylesheetQueue: null,
-
- onLoad(event)
- {
- let stylesheet = event.target.sheet;
- if (stylesheet)
- {
- if (!this._stylesheetQueue &&
- Date.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL)
- {
- this._stylesheetQueue = [];
- this.window.setTimeout(() =>
- {
- let stylesheets = this._stylesheetQueue;
- this._stylesheetQueue = null;
- this.addSelectors(stylesheets);
- }, MIN_INVOCATION_INTERVAL - (Date.now() - this._lastInvocation));
- }
-
- if (this._stylesheetQueue)
- this._stylesheetQueue.push(stylesheet);
- else
- this.addSelectors([stylesheet]);
- }
- },
-
- apply()
- {
- this.getFiltersFunc(patterns =>
- {
- this.patterns = [];
- for (let pattern of patterns)
- {
- let selectors = this.parseSelector(pattern.selector);
- if (selectors != null && selectors.length > 0)
- this.patterns.push({selectors, text: pattern.text});
- }
-
- if (this.patterns.length > 0)
- {
- let {document} = this.window;
- this.addSelectors();
- document.addEventListener("load", this.onLoad.bind(this), true);
- }
- });
- }
-};
« no previous file with comments | « chrome/content/.eslintrc.json ('k') | chromium_process.js » ('j') | test_runner.js » ('J')

Powered by Google App Engine
This is Rietveld