Index: chrome/content/elemHideEmulation.js |
=================================================================== |
--- a/chrome/content/elemHideEmulation.js |
+++ b/chrome/content/elemHideEmulation.js |
@@ -14,16 +14,17 @@ |
* 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; |
let reportError = () => {}; |
function splitSelector(selector) |
{ |
if (selector.indexOf(",") == -1) |
return [selector]; |
@@ -194,16 +195,21 @@ 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. |
@@ -242,16 +248,17 @@ function PropsSelector(propertyExpressio |
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) |
yield prefix + subSelector; |
}, |
@@ -337,26 +344,42 @@ ElemHideEmulation.prototype = { |
if (suffix == null) |
return null; |
selectors.push(...suffix); |
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; |
+ |
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; |
@@ -370,16 +393,22 @@ ElemHideEmulation.prototype = { |
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); |
@@ -394,21 +423,40 @@ ElemHideEmulation.prototype = { |
} |
} |
} |
this.addSelectorsFunc(selectors, selectorFilters); |
this.hideElemsFunc(elements, elementFilters); |
}, |
+ _stylesheetQueue: null, |
+ |
onLoad(event) |
{ |
let stylesheet = event.target.sheet; |
if (stylesheet) |
- this.addSelectors([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) |
@@ -416,14 +464,14 @@ ElemHideEmulation.prototype = { |
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.styleSheets); |
+ this.addSelectors(); |
document.addEventListener("load", this.onLoad.bind(this), true); |
} |
}); |
} |
}; |