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

Side by Side Diff: chrome/content/elemHideEmulation.js

Issue 29485567: Issue 5395 - Make sure element hiding emulation doesn't degrade website performance too much (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Patch Set: Added JSDoc comment Created July 11, 2017, 8:17 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/browser/elemHideEmulation.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2017 eyeo GmbH 3 * Copyright (C) 2006-2017 eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 /* globals filterToRegExp */ 18 /* globals filterToRegExp */
19 19
20 "use strict"; 20 "use strict";
21 21
22 const MIN_INVOCATION_INTERVAL = 3000;
22 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i; 23 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i;
23 24
24 let reportError = () => {}; 25 let reportError = () => {};
25 26
26 function splitSelector(selector) 27 function splitSelector(selector)
27 { 28 {
28 if (selector.indexOf(",") == -1) 29 if (selector.indexOf(",") == -1)
29 return [selector]; 30 return [selector];
30 31
31 let selectors = []; 32 let selectors = [];
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
192 const incompletePrefixRegexp = /[\s>+~]$/; 193 const incompletePrefixRegexp = /[\s>+~]$/;
193 194
194 function HasSelector(selectors) 195 function HasSelector(selectors)
195 { 196 {
196 this._innerSelectors = selectors; 197 this._innerSelectors = selectors;
197 } 198 }
198 199
199 HasSelector.prototype = { 200 HasSelector.prototype = {
200 requiresHiding: true, 201 requiresHiding: true,
201 202
203 get dependsOnStyles()
204 {
205 return this._innerSelectors.some(selector => selector.dependsOnStyles);
206 },
207
202 *getSelectors(prefix, subtree, styles) 208 *getSelectors(prefix, subtree, styles)
203 { 209 {
204 for (let element of this.getElements(prefix, subtree, styles)) 210 for (let element of this.getElements(prefix, subtree, styles))
205 yield [makeSelector(element, ""), element]; 211 yield [makeSelector(element, ""), element];
206 }, 212 },
207 213
208 /** 214 /**
209 * Generator function returning selected elements. 215 * Generator function returning selected elements.
210 * @param {string} prefix the prefix for the selector. 216 * @param {string} prefix the prefix for the selector.
211 * @param {Node} subtree the subtree we work on. 217 * @param {Node} subtree the subtree we work on.
(...skipping 28 matching lines...) Expand all
240 .replace("\\x7B ", "{").replace("\\x7D ", "}"); 246 .replace("\\x7B ", "{").replace("\\x7D ", "}");
241 } 247 }
242 else 248 else
243 regexpString = filterToRegExp(propertyExpression); 249 regexpString = filterToRegExp(propertyExpression);
244 250
245 this._regexp = new RegExp(regexpString, "i"); 251 this._regexp = new RegExp(regexpString, "i");
246 } 252 }
247 253
248 PropsSelector.prototype = { 254 PropsSelector.prototype = {
249 preferHideWithSelector: true, 255 preferHideWithSelector: true,
256 dependsOnStyles: true,
250 257
251 *findPropsSelectors(styles, prefix, regexp) 258 *findPropsSelectors(styles, prefix, regexp)
252 { 259 {
253 for (let style of styles) 260 for (let style of styles)
254 if (regexp.test(style.style)) 261 if (regexp.test(style.style))
255 for (let subSelector of style.subSelectors) 262 for (let subSelector of style.subSelectors)
256 yield prefix + subSelector; 263 yield prefix + subSelector;
257 }, 264 },
258 265
259 *getSelectors(prefix, subtree, styles) 266 *getSelectors(prefix, subtree, styles)
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
335 342
336 let suffix = this.parseSelector(selector.substr(content.end + 1)); 343 let suffix = this.parseSelector(selector.substr(content.end + 1));
337 if (suffix == null) 344 if (suffix == null)
338 return null; 345 return null;
339 346
340 selectors.push(...suffix); 347 selectors.push(...suffix);
341 348
342 return selectors; 349 return selectors;
343 }, 350 },
344 351
352 _lastInvocation: 0,
353
354 /**
355 * Processes the current document and applies all rules to it.
356 * @param {CSSStyleSheet[]} [stylesheets]
357 * The list of new stylesheets that have been added to the document and
358 * made reprocessing necessary. This parameter shouldn't be passed in for
359 * the initial processing, all of document's stylesheets will be considered
360 * then and all rules, including the ones not dependent on styles.
361 */
345 addSelectors(stylesheets) 362 addSelectors(stylesheets)
346 { 363 {
364 this._lastInvocation = Date.now();
365
347 let selectors = []; 366 let selectors = [];
348 let selectorFilters = []; 367 let selectorFilters = [];
349 368
350 let elements = []; 369 let elements = [];
351 let elementFilters = []; 370 let elementFilters = [];
352 371
353 let cssStyles = []; 372 let cssStyles = [];
354 373
374 let stylesheetOnlyChange = !!stylesheets;
375 if (!stylesheets)
376 stylesheets = this.window.document.styleSheets;
377
355 for (let stylesheet of stylesheets) 378 for (let stylesheet of stylesheets)
356 { 379 {
357 // Explicitly ignore third-party stylesheets to ensure consistent behavior 380 // Explicitly ignore third-party stylesheets to ensure consistent behavior
358 // between Firefox and Chrome. 381 // between Firefox and Chrome.
359 if (!this.isSameOrigin(stylesheet)) 382 if (!this.isSameOrigin(stylesheet))
360 continue; 383 continue;
361 384
362 let rules = stylesheet.cssRules; 385 let rules = stylesheet.cssRules;
363 if (!rules) 386 if (!rules)
364 continue; 387 continue;
365 388
366 for (let rule of rules) 389 for (let rule of rules)
367 { 390 {
368 if (rule.type != rule.STYLE_RULE) 391 if (rule.type != rule.STYLE_RULE)
369 continue; 392 continue;
370 393
371 cssStyles.push(stringifyStyle(rule)); 394 cssStyles.push(stringifyStyle(rule));
372 } 395 }
373 } 396 }
374 397
375 let {document} = this.window; 398 let {document} = this.window;
376 for (let pattern of this.patterns) 399 for (let pattern of this.patterns)
377 { 400 {
401 if (stylesheetOnlyChange &&
402 !pattern.selectors.some(selector => selector.dependsOnStyles))
403 {
404 continue;
405 }
406
378 for (let selector of evaluate(pattern.selectors, 407 for (let selector of evaluate(pattern.selectors,
379 0, "", document, cssStyles)) 408 0, "", document, cssStyles))
380 { 409 {
381 if (pattern.selectors.some(s => s.preferHideWithSelector) && 410 if (pattern.selectors.some(s => s.preferHideWithSelector) &&
382 !pattern.selectors.some(s => s.requiresHiding)) 411 !pattern.selectors.some(s => s.requiresHiding))
383 { 412 {
384 selectors.push(selector); 413 selectors.push(selector);
385 selectorFilters.push(pattern.text); 414 selectorFilters.push(pattern.text);
386 } 415 }
387 else 416 else
388 { 417 {
389 for (let element of document.querySelectorAll(selector)) 418 for (let element of document.querySelectorAll(selector))
390 { 419 {
391 elements.push(element); 420 elements.push(element);
392 elementFilters.push(pattern.text); 421 elementFilters.push(pattern.text);
393 } 422 }
394 } 423 }
395 } 424 }
396 } 425 }
397 426
398 this.addSelectorsFunc(selectors, selectorFilters); 427 this.addSelectorsFunc(selectors, selectorFilters);
399 this.hideElemsFunc(elements, elementFilters); 428 this.hideElemsFunc(elements, elementFilters);
400 }, 429 },
401 430
431 _stylesheetQueue: null,
432
402 onLoad(event) 433 onLoad(event)
403 { 434 {
404 let stylesheet = event.target.sheet; 435 let stylesheet = event.target.sheet;
405 if (stylesheet) 436 if (stylesheet)
406 this.addSelectors([stylesheet]); 437 {
438 if (!this._stylesheetQueue &&
439 Date.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL)
440 {
441 this._stylesheetQueue = [];
442 this.window.setTimeout(() =>
443 {
444 let stylesheets = this._stylesheetQueue;
445 this._stylesheetQueue = null;
446 this.addSelectors(stylesheets);
447 }, MIN_INVOCATION_INTERVAL - (Date.now() - this._lastInvocation));
448 }
449
450 if (this._stylesheetQueue)
451 this._stylesheetQueue.push(stylesheet);
452 else
453 this.addSelectors([stylesheet]);
454 }
407 }, 455 },
408 456
409 apply() 457 apply()
410 { 458 {
411 this.getFiltersFunc(patterns => 459 this.getFiltersFunc(patterns =>
412 { 460 {
413 this.patterns = []; 461 this.patterns = [];
414 for (let pattern of patterns) 462 for (let pattern of patterns)
415 { 463 {
416 let selectors = this.parseSelector(pattern.selector); 464 let selectors = this.parseSelector(pattern.selector);
417 if (selectors != null && selectors.length > 0) 465 if (selectors != null && selectors.length > 0)
418 this.patterns.push({selectors, text: pattern.text}); 466 this.patterns.push({selectors, text: pattern.text});
419 } 467 }
420 468
421 if (this.patterns.length > 0) 469 if (this.patterns.length > 0)
422 { 470 {
423 let {document} = this.window; 471 let {document} = this.window;
424 this.addSelectors(document.styleSheets); 472 this.addSelectors();
425 document.addEventListener("load", this.onLoad.bind(this), true); 473 document.addEventListener("load", this.onLoad.bind(this), true);
426 } 474 }
427 }); 475 });
428 } 476 }
429 }; 477 };
OLDNEW
« no previous file with comments | « no previous file | test/browser/elemHideEmulation.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld