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: Created July 10, 2017, 1:46 p.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
345 addSelectors(stylesheets) 354 addSelectors(stylesheets)
kzar 2017/07/10 14:50:17 Nit: IMO this function could do with a comment to
Wladimir Palant 2017/07/11 08:18:11 Done.
kzar 2017/07/11 11:05:14 Nice, thanks.
346 { 355 {
356 this._lastInvocation = Date.now();
357
347 let selectors = []; 358 let selectors = [];
348 let selectorFilters = []; 359 let selectorFilters = [];
349 360
350 let elements = []; 361 let elements = [];
351 let elementFilters = []; 362 let elementFilters = [];
352 363
353 let cssStyles = []; 364 let cssStyles = [];
354 365
366 let stylesheetOnlyChange = !!stylesheets;
367 if (!stylesheets)
368 stylesheets = this.window.document.styleSheets;
369
355 for (let stylesheet of stylesheets) 370 for (let stylesheet of stylesheets)
356 { 371 {
357 // Explicitly ignore third-party stylesheets to ensure consistent behavior 372 // Explicitly ignore third-party stylesheets to ensure consistent behavior
358 // between Firefox and Chrome. 373 // between Firefox and Chrome.
359 if (!this.isSameOrigin(stylesheet)) 374 if (!this.isSameOrigin(stylesheet))
360 continue; 375 continue;
361 376
362 let rules = stylesheet.cssRules; 377 let rules = stylesheet.cssRules;
363 if (!rules) 378 if (!rules)
364 continue; 379 continue;
365 380
366 for (let rule of rules) 381 for (let rule of rules)
367 { 382 {
368 if (rule.type != rule.STYLE_RULE) 383 if (rule.type != rule.STYLE_RULE)
369 continue; 384 continue;
370 385
371 cssStyles.push(stringifyStyle(rule)); 386 cssStyles.push(stringifyStyle(rule));
372 } 387 }
373 } 388 }
374 389
375 let {document} = this.window; 390 let {document} = this.window;
376 for (let pattern of this.patterns) 391 for (let pattern of this.patterns)
377 { 392 {
393 if (stylesheetOnlyChange &&
394 !pattern.selectors.some(selector => selector.dependsOnStyles))
395 {
396 continue;
397 }
398
378 for (let selector of evaluate(pattern.selectors, 399 for (let selector of evaluate(pattern.selectors,
379 0, "", document, cssStyles)) 400 0, "", document, cssStyles))
380 { 401 {
381 if (pattern.selectors.some(s => s.preferHideWithSelector) && 402 if (pattern.selectors.some(s => s.preferHideWithSelector) &&
382 !pattern.selectors.some(s => s.requiresHiding)) 403 !pattern.selectors.some(s => s.requiresHiding))
383 { 404 {
384 selectors.push(selector); 405 selectors.push(selector);
385 selectorFilters.push(pattern.text); 406 selectorFilters.push(pattern.text);
386 } 407 }
387 else 408 else
388 { 409 {
389 for (let element of document.querySelectorAll(selector)) 410 for (let element of document.querySelectorAll(selector))
390 { 411 {
391 elements.push(element); 412 elements.push(element);
392 elementFilters.push(pattern.text); 413 elementFilters.push(pattern.text);
393 } 414 }
394 } 415 }
395 } 416 }
396 } 417 }
397 418
398 this.addSelectorsFunc(selectors, selectorFilters); 419 this.addSelectorsFunc(selectors, selectorFilters);
399 this.hideElemsFunc(elements, elementFilters); 420 this.hideElemsFunc(elements, elementFilters);
400 }, 421 },
401 422
423 _stylesheetQueue: null,
424
402 onLoad(event) 425 onLoad(event)
403 { 426 {
404 let stylesheet = event.target.sheet; 427 let stylesheet = event.target.sheet;
405 if (stylesheet) 428 if (stylesheet)
406 this.addSelectors([stylesheet]); 429 {
430 if (!this._stylesheetQueue &&
431 Date.now() - this._lastInvocation < MIN_INVOCATION_INTERVAL)
432 {
433 this._stylesheetQueue = [];
434 this.window.setTimeout(() =>
435 {
436 let stylesheets = this._stylesheetQueue;
437 this._stylesheetQueue = null;
438 this.addSelectors(stylesheets);
Felix Dahlke 2017/07/10 14:37:19 Nit: I'm probably rusty, but couldn't the above th
Wladimir Palant 2017/07/10 14:38:52 The trouble here is reentrance - if addSelector()
Felix Dahlke 2017/07/10 14:52:14 Makes sense.
439 }, MIN_INVOCATION_INTERVAL - (Date.now() - this._lastInvocation));
440 }
441
442 if (this._stylesheetQueue)
443 this._stylesheetQueue.push(stylesheet);
444 else
445 this.addSelectors([stylesheet]);
446 }
407 }, 447 },
408 448
409 apply() 449 apply()
410 { 450 {
411 this.getFiltersFunc(patterns => 451 this.getFiltersFunc(patterns =>
412 { 452 {
413 this.patterns = []; 453 this.patterns = [];
414 for (let pattern of patterns) 454 for (let pattern of patterns)
415 { 455 {
416 let selectors = this.parseSelector(pattern.selector); 456 let selectors = this.parseSelector(pattern.selector);
417 if (selectors != null && selectors.length > 0) 457 if (selectors != null && selectors.length > 0)
418 this.patterns.push({selectors, text: pattern.text}); 458 this.patterns.push({selectors, text: pattern.text});
419 } 459 }
420 460
421 if (this.patterns.length > 0) 461 if (this.patterns.length > 0)
422 { 462 {
423 let {document} = this.window; 463 let {document} = this.window;
424 this.addSelectors(document.styleSheets); 464 this.addSelectors();
425 document.addEventListener("load", this.onLoad.bind(this), true); 465 document.addEventListener("load", this.onLoad.bind(this), true);
426 } 466 }
427 }); 467 });
428 } 468 }
429 }; 469 };
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