Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | "use strict"; |
michael@0 | 6 | |
michael@0 | 7 | const {classes: Cc, interfaces: Ci, utils: Cu} = Components; |
michael@0 | 8 | |
michael@0 | 9 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 10 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 11 | |
michael@0 | 12 | const PREF_FXA_ENABLED = "identity.fxaccounts.enabled"; |
michael@0 | 13 | |
michael@0 | 14 | // This is the parent process corresponding to nsDOMIdentity. |
michael@0 | 15 | this.EXPORTED_SYMBOLS = ["DOMIdentity"]; |
michael@0 | 16 | |
michael@0 | 17 | XPCOMUtils.defineLazyModuleGetter(this, "objectCopy", |
michael@0 | 18 | "resource://gre/modules/identity/IdentityUtils.jsm"); |
michael@0 | 19 | |
michael@0 | 20 | XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", |
michael@0 | 21 | #ifdef MOZ_B2G_VERSION |
michael@0 | 22 | "resource://gre/modules/identity/MinimalIdentity.jsm"); |
michael@0 | 23 | #else |
michael@0 | 24 | "resource://gre/modules/identity/Identity.jsm"); |
michael@0 | 25 | #endif |
michael@0 | 26 | |
michael@0 | 27 | XPCOMUtils.defineLazyModuleGetter(this, "FirefoxAccounts", |
michael@0 | 28 | "resource://gre/modules/identity/FirefoxAccounts.jsm"); |
michael@0 | 29 | |
michael@0 | 30 | XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject", |
michael@0 | 31 | "resource://gre/modules/identity/IdentityUtils.jsm"); |
michael@0 | 32 | |
michael@0 | 33 | XPCOMUtils.defineLazyModuleGetter(this, |
michael@0 | 34 | "Logger", |
michael@0 | 35 | "resource://gre/modules/identity/LogUtils.jsm"); |
michael@0 | 36 | |
michael@0 | 37 | XPCOMUtils.defineLazyServiceGetter(this, "ppmm", |
michael@0 | 38 | "@mozilla.org/parentprocessmessagemanager;1", |
michael@0 | 39 | "nsIMessageListenerManager"); |
michael@0 | 40 | |
michael@0 | 41 | function log(...aMessageArgs) { |
michael@0 | 42 | Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs)); |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | function IDDOMMessage(aOptions) { |
michael@0 | 46 | objectCopy(aOptions, this); |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | function IDPProvisioningContext(aID, aOrigin, aTargetMM) { |
michael@0 | 50 | this._id = aID; |
michael@0 | 51 | this._origin = aOrigin; |
michael@0 | 52 | this._mm = aTargetMM; |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | IDPProvisioningContext.prototype = { |
michael@0 | 56 | get id() this._id, |
michael@0 | 57 | get origin() this._origin, |
michael@0 | 58 | |
michael@0 | 59 | doBeginProvisioningCallback: function IDPPC_doBeginProvCB(aID, aCertDuration) { |
michael@0 | 60 | let message = new IDDOMMessage({id: this.id}); |
michael@0 | 61 | message.identity = aID; |
michael@0 | 62 | message.certDuration = aCertDuration; |
michael@0 | 63 | this._mm.sendAsyncMessage("Identity:IDP:CallBeginProvisioningCallback", |
michael@0 | 64 | message); |
michael@0 | 65 | }, |
michael@0 | 66 | |
michael@0 | 67 | doGenKeyPairCallback: function IDPPC_doGenKeyPairCallback(aPublicKey) { |
michael@0 | 68 | log("doGenKeyPairCallback"); |
michael@0 | 69 | let message = new IDDOMMessage({id: this.id}); |
michael@0 | 70 | message.publicKey = aPublicKey; |
michael@0 | 71 | this._mm.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message); |
michael@0 | 72 | }, |
michael@0 | 73 | |
michael@0 | 74 | doError: function(msg) { |
michael@0 | 75 | log("Provisioning ERROR: " + msg); |
michael@0 | 76 | } |
michael@0 | 77 | }; |
michael@0 | 78 | |
michael@0 | 79 | function IDPAuthenticationContext(aID, aOrigin, aTargetMM) { |
michael@0 | 80 | this._id = aID; |
michael@0 | 81 | this._origin = aOrigin; |
michael@0 | 82 | this._mm = aTargetMM; |
michael@0 | 83 | } |
michael@0 | 84 | |
michael@0 | 85 | IDPAuthenticationContext.prototype = { |
michael@0 | 86 | get id() this._id, |
michael@0 | 87 | get origin() this._origin, |
michael@0 | 88 | |
michael@0 | 89 | doBeginAuthenticationCallback: function IDPAC_doBeginAuthCB(aIdentity) { |
michael@0 | 90 | let message = new IDDOMMessage({id: this.id}); |
michael@0 | 91 | message.identity = aIdentity; |
michael@0 | 92 | this._mm.sendAsyncMessage("Identity:IDP:CallBeginAuthenticationCallback", |
michael@0 | 93 | message); |
michael@0 | 94 | }, |
michael@0 | 95 | |
michael@0 | 96 | doError: function IDPAC_doError(msg) { |
michael@0 | 97 | log("Authentication ERROR: " + msg); |
michael@0 | 98 | } |
michael@0 | 99 | }; |
michael@0 | 100 | |
michael@0 | 101 | function RPWatchContext(aOptions, aTargetMM) { |
michael@0 | 102 | objectCopy(aOptions, this); |
michael@0 | 103 | |
michael@0 | 104 | // id and origin are required |
michael@0 | 105 | if (! (this.id && this.origin)) { |
michael@0 | 106 | throw new Error("id and origin are required for RP watch context"); |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | // default for no loggedInUser is undefined, not null |
michael@0 | 110 | this.loggedInUser = aOptions.loggedInUser; |
michael@0 | 111 | |
michael@0 | 112 | // Maybe internal. For hosted b2g identity shim. |
michael@0 | 113 | this._internal = aOptions._internal; |
michael@0 | 114 | |
michael@0 | 115 | this._mm = aTargetMM; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | RPWatchContext.prototype = { |
michael@0 | 119 | doLogin: function RPWatchContext_onlogin(aAssertion, aMaybeInternalParams) { |
michael@0 | 120 | log("doLogin: " + this.id); |
michael@0 | 121 | let message = new IDDOMMessage({id: this.id, assertion: aAssertion}); |
michael@0 | 122 | if (aMaybeInternalParams) { |
michael@0 | 123 | message._internalParams = aMaybeInternalParams; |
michael@0 | 124 | } |
michael@0 | 125 | this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogin", message); |
michael@0 | 126 | }, |
michael@0 | 127 | |
michael@0 | 128 | doLogout: function RPWatchContext_onlogout() { |
michael@0 | 129 | log("doLogout: " + this.id); |
michael@0 | 130 | let message = new IDDOMMessage({id: this.id}); |
michael@0 | 131 | this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogout", message); |
michael@0 | 132 | }, |
michael@0 | 133 | |
michael@0 | 134 | doReady: function RPWatchContext_onready() { |
michael@0 | 135 | log("doReady: " + this.id); |
michael@0 | 136 | let message = new IDDOMMessage({id: this.id}); |
michael@0 | 137 | this._mm.sendAsyncMessage("Identity:RP:Watch:OnReady", message); |
michael@0 | 138 | }, |
michael@0 | 139 | |
michael@0 | 140 | doCancel: function RPWatchContext_oncancel() { |
michael@0 | 141 | log("doCancel: " + this.id); |
michael@0 | 142 | let message = new IDDOMMessage({id: this.id}); |
michael@0 | 143 | this._mm.sendAsyncMessage("Identity:RP:Watch:OnCancel", message); |
michael@0 | 144 | }, |
michael@0 | 145 | |
michael@0 | 146 | doError: function RPWatchContext_onerror(aMessage) { |
michael@0 | 147 | log("doError: " + this.id + ": " + JSON.stringify(aMessage)); |
michael@0 | 148 | let message = new IDDOMMessage({id: this.id, message: aMessage}); |
michael@0 | 149 | this._mm.sendAsyncMessage("Identity:RP:Watch:OnError", message); |
michael@0 | 150 | } |
michael@0 | 151 | }; |
michael@0 | 152 | |
michael@0 | 153 | this.DOMIdentity = { |
michael@0 | 154 | /* |
michael@0 | 155 | * When relying parties (RPs) invoke the watch() method, they can request |
michael@0 | 156 | * to use Firefox Accounts as their auth service or BrowserID (the default). |
michael@0 | 157 | * For each RP, we create an RPWatchContext to store the parameters given to |
michael@0 | 158 | * watch(), and to provide hooks to invoke the onlogin(), onlogout(), etc. |
michael@0 | 159 | * callbacks held in the nsDOMIdentity state. |
michael@0 | 160 | * |
michael@0 | 161 | * The serviceContexts map associates the window ID of the RP with the |
michael@0 | 162 | * context object. The mmContexts map associates a message manager with a |
michael@0 | 163 | * window ID. We use the mmContexts map when child-process-shutdown is |
michael@0 | 164 | * observed, and all we have is a message manager to identify the window in |
michael@0 | 165 | * question. |
michael@0 | 166 | */ |
michael@0 | 167 | _serviceContexts: new Map(), |
michael@0 | 168 | _mmContexts: new Map(), |
michael@0 | 169 | |
michael@0 | 170 | /* |
michael@0 | 171 | * Mockable, for testing |
michael@0 | 172 | */ |
michael@0 | 173 | _mockIdentityService: null, |
michael@0 | 174 | get IdentityService() { |
michael@0 | 175 | if (this._mockIdentityService) { |
michael@0 | 176 | log("Using a mocked identity service"); |
michael@0 | 177 | return this._mockIdentityService; |
michael@0 | 178 | } |
michael@0 | 179 | return IdentityService; |
michael@0 | 180 | }, |
michael@0 | 181 | |
michael@0 | 182 | /* |
michael@0 | 183 | * Create a new RPWatchContext, and update the context maps. |
michael@0 | 184 | */ |
michael@0 | 185 | newContext: function(message, targetMM) { |
michael@0 | 186 | let context = new RPWatchContext(message, targetMM); |
michael@0 | 187 | this._serviceContexts.set(message.id, context); |
michael@0 | 188 | this._mmContexts.set(targetMM, message.id); |
michael@0 | 189 | return context; |
michael@0 | 190 | }, |
michael@0 | 191 | |
michael@0 | 192 | /* |
michael@0 | 193 | * Get the identity service used for an RP. |
michael@0 | 194 | * |
michael@0 | 195 | * @object message |
michael@0 | 196 | * A message received from an RP. Will include the id of the window |
michael@0 | 197 | * whence the message originated. |
michael@0 | 198 | * |
michael@0 | 199 | * Returns FirefoxAccounts or IdentityService |
michael@0 | 200 | */ |
michael@0 | 201 | getService: function(message) { |
michael@0 | 202 | if (!this._serviceContexts.has(message.id)) { |
michael@0 | 203 | throw new Error("getService called before newContext for " + message.id); |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | let context = this._serviceContexts.get(message.id); |
michael@0 | 207 | if (context.wantIssuer == "firefox-accounts") { |
michael@0 | 208 | if (Services.prefs.getPrefType(PREF_FXA_ENABLED) === Ci.nsIPrefBranch.PREF_BOOL |
michael@0 | 209 | && Services.prefs.getBoolPref(PREF_FXA_ENABLED)) { |
michael@0 | 210 | return FirefoxAccounts; |
michael@0 | 211 | } |
michael@0 | 212 | log("WARNING: Firefox Accounts is not enabled; Defaulting to BrowserID"); |
michael@0 | 213 | } |
michael@0 | 214 | return this.IdentityService; |
michael@0 | 215 | }, |
michael@0 | 216 | |
michael@0 | 217 | /* |
michael@0 | 218 | * Get the RPWatchContext object for a given message manager. |
michael@0 | 219 | */ |
michael@0 | 220 | getContextForMM: function(targetMM) { |
michael@0 | 221 | return this._serviceContexts.get(this._mmContexts.get(targetMM)); |
michael@0 | 222 | }, |
michael@0 | 223 | |
michael@0 | 224 | hasContextForMM: function(targetMM) { |
michael@0 | 225 | return this._mmContexts.has(targetMM); |
michael@0 | 226 | }, |
michael@0 | 227 | |
michael@0 | 228 | /* |
michael@0 | 229 | * Delete the RPWatchContext object for a given message manager. Removes the |
michael@0 | 230 | * mapping both from _serviceContexts and _mmContexts. |
michael@0 | 231 | */ |
michael@0 | 232 | deleteContextForMM: function(targetMM) { |
michael@0 | 233 | this._serviceContexts.delete(this._mmContexts.get(targetMM)); |
michael@0 | 234 | this._mmContexts.delete(targetMM); |
michael@0 | 235 | }, |
michael@0 | 236 | |
michael@0 | 237 | // nsIMessageListener |
michael@0 | 238 | receiveMessage: function DOMIdentity_receiveMessage(aMessage) { |
michael@0 | 239 | let msg = aMessage.json; |
michael@0 | 240 | |
michael@0 | 241 | // Target is the frame message manager that called us and is |
michael@0 | 242 | // used to send replies back to the proper window. |
michael@0 | 243 | let targetMM = aMessage.target; |
michael@0 | 244 | |
michael@0 | 245 | switch (aMessage.name) { |
michael@0 | 246 | // RP |
michael@0 | 247 | case "Identity:RP:Watch": |
michael@0 | 248 | this._watch(msg, targetMM); |
michael@0 | 249 | break; |
michael@0 | 250 | case "Identity:RP:Unwatch": |
michael@0 | 251 | this._unwatch(msg, targetMM); |
michael@0 | 252 | break; |
michael@0 | 253 | case "Identity:RP:Request": |
michael@0 | 254 | this._request(msg, targetMM); |
michael@0 | 255 | break; |
michael@0 | 256 | case "Identity:RP:Logout": |
michael@0 | 257 | this._logout(msg, targetMM); |
michael@0 | 258 | break; |
michael@0 | 259 | // IDP |
michael@0 | 260 | case "Identity:IDP:BeginProvisioning": |
michael@0 | 261 | this._beginProvisioning(msg, targetMM); |
michael@0 | 262 | break; |
michael@0 | 263 | case "Identity:IDP:GenKeyPair": |
michael@0 | 264 | this._genKeyPair(msg); |
michael@0 | 265 | break; |
michael@0 | 266 | case "Identity:IDP:RegisterCertificate": |
michael@0 | 267 | this._registerCertificate(msg); |
michael@0 | 268 | break; |
michael@0 | 269 | case "Identity:IDP:ProvisioningFailure": |
michael@0 | 270 | this._provisioningFailure(msg); |
michael@0 | 271 | break; |
michael@0 | 272 | case "Identity:IDP:BeginAuthentication": |
michael@0 | 273 | this._beginAuthentication(msg, targetMM); |
michael@0 | 274 | break; |
michael@0 | 275 | case "Identity:IDP:CompleteAuthentication": |
michael@0 | 276 | this._completeAuthentication(msg); |
michael@0 | 277 | break; |
michael@0 | 278 | case "Identity:IDP:AuthenticationFailure": |
michael@0 | 279 | this._authenticationFailure(msg); |
michael@0 | 280 | break; |
michael@0 | 281 | case "child-process-shutdown": |
michael@0 | 282 | // we receive child-process-shutdown if the appliction crashes, |
michael@0 | 283 | // including if it is crashed by the OS (killed for out-of-memory, |
michael@0 | 284 | // for example) |
michael@0 | 285 | this._childProcessShutdown(targetMM); |
michael@0 | 286 | break; |
michael@0 | 287 | } |
michael@0 | 288 | }, |
michael@0 | 289 | |
michael@0 | 290 | // nsIObserver |
michael@0 | 291 | observe: function DOMIdentity_observe(aSubject, aTopic, aData) { |
michael@0 | 292 | switch (aTopic) { |
michael@0 | 293 | case "xpcom-shutdown": |
michael@0 | 294 | this._unsubscribeListeners(); |
michael@0 | 295 | Services.obs.removeObserver(this, "xpcom-shutdown"); |
michael@0 | 296 | Services.ww.unregisterNotification(this); |
michael@0 | 297 | break; |
michael@0 | 298 | } |
michael@0 | 299 | }, |
michael@0 | 300 | |
michael@0 | 301 | messages: ["Identity:RP:Watch", "Identity:RP:Request", "Identity:RP:Logout", |
michael@0 | 302 | "Identity:IDP:BeginProvisioning", "Identity:IDP:ProvisioningFailure", |
michael@0 | 303 | "Identity:IDP:RegisterCertificate", "Identity:IDP:GenKeyPair", |
michael@0 | 304 | "Identity:IDP:BeginAuthentication", |
michael@0 | 305 | "Identity:IDP:CompleteAuthentication", |
michael@0 | 306 | "Identity:IDP:AuthenticationFailure", |
michael@0 | 307 | "Identity:RP:Unwatch", |
michael@0 | 308 | "child-process-shutdown"], |
michael@0 | 309 | |
michael@0 | 310 | // Private. |
michael@0 | 311 | _init: function DOMIdentity__init() { |
michael@0 | 312 | Services.ww.registerNotification(this); |
michael@0 | 313 | Services.obs.addObserver(this, "xpcom-shutdown", false); |
michael@0 | 314 | this._subscribeListeners(); |
michael@0 | 315 | }, |
michael@0 | 316 | |
michael@0 | 317 | _subscribeListeners: function DOMIdentity__subscribeListeners() { |
michael@0 | 318 | if (!ppmm) return; |
michael@0 | 319 | for (let message of this.messages) { |
michael@0 | 320 | ppmm.addMessageListener(message, this); |
michael@0 | 321 | } |
michael@0 | 322 | }, |
michael@0 | 323 | |
michael@0 | 324 | _unsubscribeListeners: function DOMIdentity__unsubscribeListeners() { |
michael@0 | 325 | for (let message of this.messages) { |
michael@0 | 326 | ppmm.removeMessageListener(message, this); |
michael@0 | 327 | } |
michael@0 | 328 | ppmm = null; |
michael@0 | 329 | }, |
michael@0 | 330 | |
michael@0 | 331 | _watch: function DOMIdentity__watch(message, targetMM) { |
michael@0 | 332 | log("DOMIdentity__watch: " + message.id); |
michael@0 | 333 | let context = this.newContext(message, targetMM); |
michael@0 | 334 | this.getService(message).RP.watch(context); |
michael@0 | 335 | }, |
michael@0 | 336 | |
michael@0 | 337 | _unwatch: function DOMIdentity_unwatch(message, targetMM) { |
michael@0 | 338 | log("DOMIDentity__unwatch: " + message.id); |
michael@0 | 339 | // If watch failed for some reason (e.g., exception thrown because RP did |
michael@0 | 340 | // not have the right callbacks, we don't want unwatch to throw, because it |
michael@0 | 341 | // will break the process of releasing the page's resources and leak |
michael@0 | 342 | // memory. |
michael@0 | 343 | try { |
michael@0 | 344 | this.getService(message).RP.unwatch(message.id, targetMM); |
michael@0 | 345 | } catch(ex) { |
michael@0 | 346 | log("ERROR: can't unwatch " + message.id + ": " + ex); |
michael@0 | 347 | } |
michael@0 | 348 | }, |
michael@0 | 349 | |
michael@0 | 350 | _request: function DOMIdentity__request(message) { |
michael@0 | 351 | this.getService(message).RP.request(message.id, message); |
michael@0 | 352 | }, |
michael@0 | 353 | |
michael@0 | 354 | _logout: function DOMIdentity__logout(message) { |
michael@0 | 355 | log("logout " + message + "\n"); |
michael@0 | 356 | this.getService(message).RP.logout(message.id, message.origin, message); |
michael@0 | 357 | }, |
michael@0 | 358 | |
michael@0 | 359 | _childProcessShutdown: function DOMIdentity__childProcessShutdown(targetMM) { |
michael@0 | 360 | if (!this.hasContextForMM(targetMM)) { |
michael@0 | 361 | return; |
michael@0 | 362 | } |
michael@0 | 363 | |
michael@0 | 364 | this.getContextForMM(targetMM).RP.childProcessShutdown(targetMM); |
michael@0 | 365 | this.deleteContextForMM(targetMM); |
michael@0 | 366 | |
michael@0 | 367 | let options = makeMessageObject({messageManager: targetMM, id: null, origin: null}); |
michael@0 | 368 | Services.obs.notifyObservers({wrappedJSObject: options}, "identity-child-process-shutdown", null); |
michael@0 | 369 | }, |
michael@0 | 370 | |
michael@0 | 371 | _beginProvisioning: function DOMIdentity__beginProvisioning(message, targetMM) { |
michael@0 | 372 | let context = new IDPProvisioningContext(message.id, message.origin, |
michael@0 | 373 | targetMM); |
michael@0 | 374 | this.getService(message).IDP.beginProvisioning(context); |
michael@0 | 375 | }, |
michael@0 | 376 | |
michael@0 | 377 | _genKeyPair: function DOMIdentity__genKeyPair(message) { |
michael@0 | 378 | this.getService(message).IDP.genKeyPair(message.id); |
michael@0 | 379 | }, |
michael@0 | 380 | |
michael@0 | 381 | _registerCertificate: function DOMIdentity__registerCertificate(message) { |
michael@0 | 382 | this.getService(message).IDP.registerCertificate(message.id, message.cert); |
michael@0 | 383 | }, |
michael@0 | 384 | |
michael@0 | 385 | _provisioningFailure: function DOMIdentity__provisioningFailure(message) { |
michael@0 | 386 | this.getService(message).IDP.raiseProvisioningFailure(message.id, message.reason); |
michael@0 | 387 | }, |
michael@0 | 388 | |
michael@0 | 389 | _beginAuthentication: function DOMIdentity__beginAuthentication(message, targetMM) { |
michael@0 | 390 | let context = new IDPAuthenticationContext(message.id, message.origin, |
michael@0 | 391 | targetMM); |
michael@0 | 392 | this.getService(message).IDP.beginAuthentication(context); |
michael@0 | 393 | }, |
michael@0 | 394 | |
michael@0 | 395 | _completeAuthentication: function DOMIdentity__completeAuthentication(message) { |
michael@0 | 396 | this.getService(message).IDP.completeAuthentication(message.id); |
michael@0 | 397 | }, |
michael@0 | 398 | |
michael@0 | 399 | _authenticationFailure: function DOMIdentity__authenticationFailure(message) { |
michael@0 | 400 | this.getService(message).IDP.cancelAuthentication(message.id); |
michael@0 | 401 | } |
michael@0 | 402 | }; |
michael@0 | 403 | |
michael@0 | 404 | // Object is initialized by nsIDService.js |