| Left: | ||
| Right: |
| LEFT | RIGHT |
|---|---|
| 1 /* | 1 /* |
| 2 * This file is part of Adblock Plus <http://adblockplus.org/>, | 2 * This file is part of Adblock Plus <http://adblockplus.org/>, |
| 3 * Copyright (C) 2006-2014 Eyeo GmbH | 3 * Copyright (C) 2006-2014 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 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 21 | 21 |
| 22 Cu.import("resource://gre/modules/Services.jsm"); | 22 Cu.import("resource://gre/modules/Services.jsm"); |
| 23 | 23 |
| 24 let {Utils} = require("utils"); | 24 let {Utils} = require("utils"); |
| 25 let {IO} = require("io"); | 25 let {IO} = require("io"); |
| 26 let {Prefs} = require("prefs"); | 26 let {Prefs} = require("prefs"); |
| 27 let {ElemHideException} = require("filterClasses"); | 27 let {ElemHideException} = require("filterClasses"); |
| 28 let {FilterNotifier} = require("filterNotifier"); | 28 let {FilterNotifier} = require("filterNotifier"); |
| 29 let {AboutHandler} = require("elemHideHitRegistration"); | 29 let {AboutHandler} = require("elemHideHitRegistration"); |
| 30 let {TimeLine} = require("timeline"); | 30 let {TimeLine} = require("timeline"); |
| 31 let Policy = null; | |
| 31 | 32 |
| 32 /** | 33 /** |
| 33 * Lookup table, filters by their associated key | 34 * Lookup table, filters by their associated key |
| 34 * @type Object | 35 * @type Object |
| 35 */ | 36 */ |
| 36 let filterByKey = {__proto__: null}; | 37 let filterByKey = Object.create(null); |
| 37 | 38 |
| 38 /** | 39 /** |
| 39 * Lookup table, keys of the filters by filter text | 40 * Lookup table, keys of the filters by filter text |
| 40 * @type Object | 41 * @type Object |
| 41 */ | 42 */ |
| 42 let keyByFilter = {__proto__: null}; | 43 let keyByFilter = Object.create(null); |
| 43 | 44 |
| 44 /** | 45 /** |
| 45 * Lookup table, keys are known element hiding exceptions | 46 * Lookup table, keys are known element hiding exceptions |
| 46 * @type Object | 47 * @type Object |
| 47 */ | 48 */ |
| 48 let knownExceptions = {__proto__: null}; | 49 let knownExceptions = Object.create(null); |
| 49 | 50 |
| 50 /** | 51 /** |
| 51 * Lookup table, lists of element hiding exceptions by selector | 52 * Lookup table, lists of element hiding exceptions by selector |
| 52 * @type Object | 53 * @type Object |
| 53 */ | 54 */ |
| 54 let exceptions = {__proto__: null}; | 55 let exceptions = Object.create(null); |
| 55 | 56 |
| 56 /** | 57 /** |
| 57 * Currently applied stylesheet URL | 58 * Currently applied stylesheet URL |
| 58 * @type nsIURI | 59 * @type nsIURI |
| 59 */ | 60 */ |
| 60 let styleURL = null; | 61 let styleURL = null; |
| 61 | 62 |
| 62 /** | 63 /** |
| 64 * Global stylesheet that should be loaded into content windows. | |
| 65 * @type nsIStyleSheet | |
| 66 */ | |
| 67 let styleSheet = null; | |
| 68 | |
| 69 /** | |
| 70 * Use the new way of injecting styles per window that exists since Firefox 33. | |
| 71 * @type boolean | |
| 72 */ | |
| 73 let useNew = ('preloadSheet' in Utils.styleService); | |
| 74 | |
| 75 /** | |
| 63 * Element hiding component | 76 * Element hiding component |
| 64 * @class | 77 * @class |
| 65 */ | 78 */ |
| 66 let ElemHide = exports.ElemHide = | 79 let ElemHide = exports.ElemHide = |
| 67 { | 80 { |
| 81 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRefer ence]), | |
| 82 | |
| 68 /** | 83 /** |
| 69 * Indicates whether filters have been added or removed since the last apply() call. | 84 * Indicates whether filters have been added or removed since the last apply() call. |
| 70 * @type Boolean | 85 * @type Boolean |
| 71 */ | 86 */ |
| 72 isDirty: false, | 87 isDirty: false, |
| 73 | 88 |
| 74 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRefer ence]), | 89 /** |
| 90 * Inidicates whether the element hiding stylesheet is currently applied. | |
| 91 * @type Boolean | |
| 92 */ | |
| 93 applied: false, | |
| 75 | 94 |
| 76 /** | 95 /** |
| 77 * Called on module startup. | 96 * Called on module startup. |
| 78 */ | 97 */ |
| 79 init: function() | 98 init: function() |
| 80 { | 99 { |
| 81 TimeLine.enter("Entered ElemHide.init()"); | 100 TimeLine.enter("Entered ElemHide.init()"); |
| 101 | |
| 102 if (useNew) { | |
| 103 // Avoid dependency issue. | |
| 104 Policy = require("contentPolicy").Policy; | |
| 105 } | |
| 106 | |
| 82 Prefs.addListener(function(name) | 107 Prefs.addListener(function(name) |
| 83 { | 108 { |
| 84 if (name == "enabled") | 109 if (name == "enabled") |
| 85 ElemHide.apply(); | 110 ElemHide.apply(); |
| 86 }); | 111 }); |
|
Wladimir Palant
2014/05/20 19:36:12
This code is no longer necessary.
| |
| 87 onShutdown.add(function() | 112 |
| 88 { | 113 if (useNew) |
| 89 Services.obs.removeObserver(this, "content-document-global-created"); | 114 Services.obs.addObserver(this, "content-document-global-created", true); |
| 90 }.bind(this)); | 115 |
| 116 onShutdown.add(() => | |
| 117 { | |
| 118 ElemHide.unapply(); | |
| 119 if (useNew) | |
| 120 Services.obs.removeObserver(this, "content-document-global-created"); | |
| 121 }); | |
| 91 | 122 |
| 92 TimeLine.log("done adding prefs listener"); | 123 TimeLine.log("done adding prefs listener"); |
| 93 | 124 |
| 94 let styleFile = IO.resolveFilePath(Prefs.data_directory); | 125 let styleFile = IO.resolveFilePath(Prefs.data_directory); |
| 95 styleFile.append("elemhide.css"); | 126 styleFile.append("elemhide.css"); |
| 96 styleURL = Services.io.newFileURI(styleFile).QueryInterface(Ci.nsIFileURL); | 127 styleURL = Services.io.newFileURI(styleFile).QueryInterface(Ci.nsIFileURL); |
| 97 TimeLine.log("done determining stylesheet URL"); | 128 TimeLine.log("done determining stylesheet URL"); |
| 98 | 129 |
| 99 Services.obs.addObserver(this, "content-document-global-created", true); | |
| 100 | |
| 101 TimeLine.leave("ElemHide.init() done"); | 130 TimeLine.leave("ElemHide.init() done"); |
| 102 }, | |
| 103 | |
| 104 /** | |
| 105 * Removes all known filters | |
| 106 */ | |
| 107 clear: function() | |
| 108 { | |
| 109 filterByKey = {__proto__: null}; | |
| 110 keyByFilter = {__proto__: null}; | |
| 111 knownExceptions = {__proto__: null}; | |
| 112 exceptions = {__proto__: null}; | |
| 113 ElemHide.isDirty = false; | |
| 114 }, | 131 }, |
| 115 | 132 |
| 116 observe: function (subject, topic, data, additional) | 133 observe: function (subject, topic, data, additional) |
| 117 { | 134 { |
| 118 if (topic != "content-document-global-created") | 135 if (topic != "content-document-global-created") |
| 119 return; | 136 return; |
| 120 | 137 |
| 121 if (!Prefs.enabled) | 138 if (!Prefs.enabled) |
| 122 return; | 139 return; |
| 123 | 140 |
| 124 let uri = subject.document.documentURIObject; | 141 if (Policy.shouldNeverBlockWindow(subject)) |
| 125 if (!uri) | |
| 126 return; | 142 return; |
| 127 | 143 |
| 128 switch(uri.scheme) | 144 if (styleSheet) |
| 129 { | 145 { |
| 130 case "http": | 146 try |
| 131 case "https": | 147 { |
| 132 case "mailbox": | 148 let utils = subject.QueryInterface(Ci.nsIInterfaceRequestor) |
| 133 case "imap": | 149 .getInterface(Ci.nsIDOMWindowUtils); |
| 134 case "news": | 150 utils.addSheet(styleSheet, Ci.nsIStyleSheetService.USER_SHEET); |
| 135 case "snews": | 151 } |
| 136 break; | 152 catch (e) |
| 137 default: | 153 { |
| 138 // Avoid injecting in chrome documents | 154 Cu.reportError(e); |
| 139 return; | 155 } |
| 140 } | 156 } |
|
Wladimir Palant
2014/05/20 19:36:12
Please use Policy.isBlockableScheme() here (from c
| |
| 141 | 157 }, |
| 142 // XXX is there some better way to test if it's disabled on that domain that we should do before? | 158 |
| 143 if (!ElemHide.anySelectorForDomain(uri.host)) | 159 /** |
| 144 return; | 160 * Removes all known filters |
|
Wladimir Palant
2014/05/20 19:36:12
Wow, performance will go down the drain here. We c
| |
| 145 | 161 */ |
| 146 var windowUtils = subject.QueryInterface(Ci.nsIInterfaceRequestor).getInterf ace(Ci.nsIDOMWindowUtils); | 162 clear: function() |
| 147 windowUtils.loadSheet(styleURL, Ci.nsIStyleSheetService.USER_SHEET); | 163 { |
| 164 filterByKey = Object.create(null); | |
| 165 keyByFilter = Object.create(null); | |
| 166 knownExceptions = Object.create(null); | |
| 167 exceptions = Object.create(null); | |
| 168 ElemHide.isDirty = false; | |
| 169 ElemHide.unapply(); | |
| 148 }, | 170 }, |
| 149 | 171 |
| 150 /** | 172 /** |
| 151 * Add a new element hiding filter | 173 * Add a new element hiding filter |
| 152 * @param {ElemHideFilter} filter | 174 * @param {ElemHideFilter} filter |
| 153 */ | 175 */ |
| 154 add: function(filter) | 176 add: function(filter) |
| 155 { | 177 { |
| 156 if (filter instanceof ElemHideException) | 178 if (filter instanceof ElemHideException) |
| 157 { | 179 { |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 234 _applying: false, | 256 _applying: false, |
| 235 | 257 |
| 236 /** | 258 /** |
| 237 * Will be set to true if an apply() call arrives while apply() is already | 259 * Will be set to true if an apply() call arrives while apply() is already |
| 238 * running (delayed execution). | 260 * running (delayed execution). |
| 239 * @type Boolean | 261 * @type Boolean |
| 240 */ | 262 */ |
| 241 _needsApply: false, | 263 _needsApply: false, |
| 242 | 264 |
| 243 /** | 265 /** |
| 244 * Generates stylesheet URL. | 266 * Generates stylesheet URL and applies it globally |
| 245 */ | 267 */ |
| 246 // XXX Rename | |
| 247 apply: function() | 268 apply: function() |
|
Wladimir Palant
2014/05/20 19:36:12
saveStylesheet()?
| |
| 248 { | 269 { |
| 249 if (this._applying) | 270 if (this._applying) |
| 250 { | 271 { |
| 251 this._needsApply = true; | 272 this._needsApply = true; |
| 252 return; | 273 return; |
| 253 } | 274 } |
| 254 | 275 |
| 255 TimeLine.enter("Entered ElemHide.apply()"); | 276 TimeLine.enter("Entered ElemHide.apply()"); |
| 277 | |
| 278 if (!ElemHide.isDirty || !Prefs.enabled) | |
| 279 { | |
| 280 // Nothing changed, looks like we merely got enabled/disabled | |
| 281 if (Prefs.enabled && !ElemHide.applied) | |
| 282 { | |
| 283 try | |
| 284 { | |
| 285 if (!useNew) | |
| 286 Utils.styleService.loadAndRegisterSheet(styleURL, Ci.nsIStyleSheetSe rvice.USER_SHEET); | |
| 287 ElemHide.applied = true; | |
| 288 } | |
| 289 catch (e) | |
| 290 { | |
| 291 Cu.reportError(e); | |
| 292 } | |
| 293 TimeLine.log("Applying existing stylesheet finished"); | |
| 294 } | |
| 295 else if (!Prefs.enabled && ElemHide.applied) | |
| 296 { | |
| 297 ElemHide.unapply(); | |
| 298 TimeLine.log("ElemHide.unapply() finished"); | |
| 299 } | |
| 300 | |
| 301 TimeLine.leave("ElemHide.apply() done (no file changes)"); | |
| 302 return; | |
| 303 } | |
| 256 | 304 |
| 257 IO.writeToFile(styleURL.file, this._generateCSSContent(), function(e) | 305 IO.writeToFile(styleURL.file, this._generateCSSContent(), function(e) |
| 258 { | 306 { |
| 259 TimeLine.enter("ElemHide.apply() write callback"); | 307 TimeLine.enter("ElemHide.apply() write callback"); |
| 260 this._applying = false; | 308 this._applying = false; |
| 261 | 309 |
| 262 // _generateCSSContent is throwing NS_ERROR_NOT_AVAILABLE to indicate that | 310 // _generateCSSContent is throwing NS_ERROR_NOT_AVAILABLE to indicate that |
| 263 // there are no filters. If that exception is passed through XPCOM we will | 311 // there are no filters. If that exception is passed through XPCOM we will |
| 264 // see a proper exception here, otherwise a number. | 312 // see a proper exception here, otherwise a number. |
| 265 let noFilters = (e == Cr.NS_ERROR_NOT_AVAILABLE || (e && e.result == Cr.NS _ERROR_NOT_AVAILABLE)); | 313 let noFilters = (e == Cr.NS_ERROR_NOT_AVAILABLE || (e && e.result == Cr.NS _ERROR_NOT_AVAILABLE)); |
| 266 if (noFilters) | 314 if (noFilters) |
| 267 { | 315 { |
| 268 e = null; | 316 e = null; |
| 269 IO.removeFile(styleURL.file, function(e) {}); | 317 IO.removeFile(styleURL.file, function(e) {}); |
| 270 } | 318 } |
| 271 else if (e) | 319 else if (e) |
| 272 Cu.reportError(e); | 320 Cu.reportError(e); |
| 273 | 321 |
| 274 if (this._needsApply) | 322 if (this._needsApply) |
| 275 { | 323 { |
| 276 this._needsApply = false; | 324 this._needsApply = false; |
| 277 this.apply(); | 325 this.apply(); |
| 278 } | 326 } |
| 279 else if (!e) | 327 else if (!e) |
| 280 { | 328 { |
| 281 ElemHide.isDirty = false; | 329 ElemHide.isDirty = false; |
| 282 | 330 |
| 331 ElemHide.unapply(); | |
| 332 TimeLine.log("ElemHide.unapply() finished"); | |
| 333 | |
| 334 if (!noFilters) | |
| 335 { | |
| 336 try | |
| 337 { | |
| 338 if (!useNew) | |
| 339 Utils.styleService.loadAndRegisterSheet(styleURL, Ci.nsIStyleSheet Service.USER_SHEET); | |
| 340 else | |
| 341 styleSheet = Utils.styleService.preloadSheet(styleURL, Ci.nsIStyle SheetService.USER_SHEET); | |
| 342 ElemHide.applied = true; | |
| 343 } | |
| 344 catch (e) | |
| 345 { | |
| 346 Cu.reportError(e); | |
| 347 } | |
| 348 TimeLine.log("Applying stylesheet finished"); | |
| 349 } | |
| 350 | |
| 283 FilterNotifier.triggerListeners("elemhideupdate"); | 351 FilterNotifier.triggerListeners("elemhideupdate"); |
|
Wladimir Palant
2014/05/20 19:36:12
Apparently, this is dead code. This notification h
| |
| 284 } | 352 } |
| 285 TimeLine.leave("ElemHide.apply() write callback done"); | 353 TimeLine.leave("ElemHide.apply() write callback done"); |
| 286 }.bind(this), "ElemHideWrite"); | 354 }.bind(this), "ElemHideWrite"); |
| 287 | 355 |
| 288 this._applying = true; | 356 this._applying = true; |
| 289 | 357 |
| 290 TimeLine.leave("ElemHide.apply() done", "ElemHideWrite"); | 358 TimeLine.leave("ElemHide.apply() done", "ElemHideWrite"); |
| 291 }, | 359 }, |
| 292 | 360 |
| 293 _generateCSSContent: function() | 361 _generateCSSContent: function() |
| 294 { | 362 { |
| 295 // Grouping selectors by domains | 363 // Grouping selectors by domains |
| 296 TimeLine.log("start grouping selectors"); | 364 TimeLine.log("start grouping selectors"); |
| 297 let domains = {__proto__: null}; | 365 let domains = Object.create(null); |
| 298 let hasFilters = false; | 366 let hasFilters = false; |
| 299 for (let key in filterByKey) | 367 for (let key in filterByKey) |
| 300 { | 368 { |
| 301 let filter = filterByKey[key]; | 369 let filter = filterByKey[key]; |
| 302 let domain = filter.selectorDomain || ""; | 370 let domain = filter.selectorDomain || ""; |
| 303 | 371 |
| 304 let list; | 372 let list; |
| 305 if (domain in domains) | 373 if (domain in domains) |
| 306 list = domains[domain]; | 374 list = domains[domain]; |
| 307 else | 375 else |
| 308 { | 376 { |
| 309 list = {__proto__: null}; | 377 list = Object.create(null); |
| 310 domains[domain] = list; | 378 domains[domain] = list; |
| 311 } | 379 } |
| 312 list[filter.selector] = key; | 380 list[filter.selector] = key; |
| 313 hasFilters = true; | 381 hasFilters = true; |
| 314 } | 382 } |
| 315 TimeLine.log("done grouping selectors"); | 383 TimeLine.log("done grouping selectors"); |
| 316 | 384 |
| 317 if (!hasFilters) | 385 if (!hasFilters) |
| 318 throw Cr.NS_ERROR_NOT_AVAILABLE; | 386 throw Cr.NS_ERROR_NOT_AVAILABLE; |
| 319 | 387 |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 343 yield selector.replace(/[^\x01-\x7F]/g, escapeChar) + "{" + cssTemplate. replace("%ID%", list[selector]) + "}"; | 411 yield selector.replace(/[^\x01-\x7F]/g, escapeChar) + "{" + cssTemplate. replace("%ID%", list[selector]) + "}"; |
| 344 yield '}'; | 412 yield '}'; |
| 345 } | 413 } |
| 346 }, | 414 }, |
| 347 | 415 |
| 348 /** | 416 /** |
| 349 * Unapplies current stylesheet URL | 417 * Unapplies current stylesheet URL |
| 350 */ | 418 */ |
| 351 unapply: function() | 419 unapply: function() |
| 352 { | 420 { |
| 353 }, | 421 if (ElemHide.applied) |
|
Wladimir Palant
2014/05/20 19:36:12
This function should be removed.
| |
| 354 | 422 { |
| 355 /** | 423 try |
| 356 * Retrieves the currently applied stylesheet URL | 424 { |
| 357 * @type String | 425 if (!useNew) |
| 358 */ | 426 Utils.styleService.unregisterSheet(styleURL, Ci.nsIStyleSheetService.U SER_SHEET); |
| 359 get styleURL() styleURL.spec, | 427 } |
|
Wladimir Palant
2014/05/20 19:36:12
This getter was also introduced in https://hg.adbl
| |
| 428 catch (e) | |
| 429 { | |
| 430 Cu.reportError(e); | |
| 431 } | |
| 432 ElemHide.applied = false; | |
| 433 } | |
| 434 }, | |
| 360 | 435 |
| 361 /** | 436 /** |
| 362 * Retrieves an element hiding filter by the corresponding protocol key | 437 * Retrieves an element hiding filter by the corresponding protocol key |
| 363 */ | 438 */ |
| 364 getFilterByKey: function(/**String*/ key) /**Filter*/ | 439 getFilterByKey: function(/**String*/ key) /**Filter*/ |
| 365 { | 440 { |
| 366 return (key in filterByKey ? filterByKey[key] : null); | 441 return (key in filterByKey ? filterByKey[key] : null); |
| 367 }, | 442 }, |
| 368 | 443 |
| 369 /** | 444 /** |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 382 // workaround WebKit bug 132872, also see #419 | 457 // workaround WebKit bug 132872, also see #419 |
| 383 let domains = filter.domains; | 458 let domains = filter.domains; |
| 384 | 459 |
| 385 if (specificOnly && (!domains || domains[""])) | 460 if (specificOnly && (!domains || domains[""])) |
| 386 continue; | 461 continue; |
| 387 | 462 |
| 388 if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) | 463 if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) |
| 389 result.push(filter.selector); | 464 result.push(filter.selector); |
| 390 } | 465 } |
| 391 return result; | 466 return result; |
| 392 }, | |
| 393 | |
| 394 anySelectorForDomain: function(/**String*/ domain) | |
| 395 { | |
| 396 for (let key in filterByKey) | |
| 397 { | |
| 398 let filter = filterByKey[key]; | |
| 399 | |
| 400 if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) | |
| 401 return true; | |
| 402 } | |
| 403 | |
| 404 return false; | |
| 405 } | 467 } |
| 406 }; | 468 }; |
| LEFT | RIGHT |