Left: | ||
Right: |
OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 }; |
OLD | NEW |