Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 | /* |
michael@0 | 6 | * SignInToWebsite.jsm - UX Controller and means for accessing identity |
michael@0 | 7 | * cookies on behalf of relying parties. |
michael@0 | 8 | * |
michael@0 | 9 | * Currently, the b2g security architecture isolates web applications |
michael@0 | 10 | * so that each window has access only to a local cookie jar: |
michael@0 | 11 | * |
michael@0 | 12 | * To prevent Web apps from interfering with one another, each one is |
michael@0 | 13 | * hosted on a separate domain, and therefore may only access the |
michael@0 | 14 | * resources associated with its domain. These resources include |
michael@0 | 15 | * things such as IndexedDB databases, cookies, offline storage, |
michael@0 | 16 | * and so forth. |
michael@0 | 17 | * |
michael@0 | 18 | * -- https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Security/Security_model |
michael@0 | 19 | * |
michael@0 | 20 | * As a result, an authentication system like Persona cannot share its |
michael@0 | 21 | * cookie jar with multiple relying parties, and so would require a |
michael@0 | 22 | * fresh login request in every window. This would not be a good |
michael@0 | 23 | * experience. |
michael@0 | 24 | * |
michael@0 | 25 | * |
michael@0 | 26 | * In order for navigator.id.request() to maintain state in a single |
michael@0 | 27 | * cookie jar, we cause all Persona interactions to take place in a |
michael@0 | 28 | * content context that is launched by the system application, with the |
michael@0 | 29 | * result that Persona has a single cookie jar that all Relying |
michael@0 | 30 | * Parties can use. Since of course those Relying Parties cannot |
michael@0 | 31 | * reach into the system cookie jar, the Controller in this module |
michael@0 | 32 | * provides a way to get messages and data to and fro between the |
michael@0 | 33 | * Relying Party in its window context, and the Persona internal api |
michael@0 | 34 | * in its context. |
michael@0 | 35 | * |
michael@0 | 36 | * On the Relying Party's side, say a web page invokes |
michael@0 | 37 | * navigator.id.watch(), to register callbacks, and then |
michael@0 | 38 | * navigator.id.request() to request an assertion. The navigator.id |
michael@0 | 39 | * calls are provided by nsDOMIdentity. nsDOMIdentity messages down |
michael@0 | 40 | * to the privileged DOMIdentity code (using cpmm and ppmm message |
michael@0 | 41 | * managers). DOMIdentity stores the state of Relying Party flows |
michael@0 | 42 | * using an Identity service (MinimalIdentity.jsm), and emits messages |
michael@0 | 43 | * requesting Persona functions (doWatch, doReady, doLogout). |
michael@0 | 44 | * |
michael@0 | 45 | * The Identity service sends these observer messages to the |
michael@0 | 46 | * Controller in this module, which in turn triggers content to open a |
michael@0 | 47 | * window to host the Persona js. If user interaction is required, |
michael@0 | 48 | * content will open the trusty UI. If user interaction is not required, |
michael@0 | 49 | * and we only need to get to Persona functions, content will open a |
michael@0 | 50 | * hidden iframe. In either case, a window is opened into which the |
michael@0 | 51 | * controller causes the script identity.js to be injected. This |
michael@0 | 52 | * script provides the glue between the in-page javascript and the |
michael@0 | 53 | * pipe back down to the Controller, translating navigator.internal |
michael@0 | 54 | * function callbacks into messages sent back to the Controller. |
michael@0 | 55 | * |
michael@0 | 56 | * As a result, a navigator.internal function in the hosted popup or |
michael@0 | 57 | * iframe can call back to the injected identity.js (doReady, doLogin, |
michael@0 | 58 | * or doLogout). identity.js callbacks send messages back through the |
michael@0 | 59 | * pipe to the Controller. The controller invokes the corresponding |
michael@0 | 60 | * function on the Identity Service (doReady, doLogin, or doLogout). |
michael@0 | 61 | * The IdentityService calls the corresponding callback for the |
michael@0 | 62 | * correct Relying Party, which causes DOMIdentity to send a message |
michael@0 | 63 | * up to the Relying Party through nsDOMIdentity |
michael@0 | 64 | * (Identity:RP:Watch:OnLogin etc.), and finally, nsDOMIdentity |
michael@0 | 65 | * receives these messages and calls the original callback that the |
michael@0 | 66 | * Relying Party registered (navigator.id.watch(), |
michael@0 | 67 | * navigator.id.request(), or navigator.id.logout()). |
michael@0 | 68 | */ |
michael@0 | 69 | |
michael@0 | 70 | "use strict"; |
michael@0 | 71 | |
michael@0 | 72 | this.EXPORTED_SYMBOLS = ["SignInToWebsiteController"]; |
michael@0 | 73 | |
michael@0 | 74 | const Ci = Components.interfaces; |
michael@0 | 75 | const Cu = Components.utils; |
michael@0 | 76 | |
michael@0 | 77 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 78 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 79 | |
michael@0 | 80 | XPCOMUtils.defineLazyModuleGetter(this, "getRandomId", |
michael@0 | 81 | "resource://gre/modules/identity/IdentityUtils.jsm"); |
michael@0 | 82 | |
michael@0 | 83 | XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", |
michael@0 | 84 | "resource://gre/modules/identity/MinimalIdentity.jsm"); |
michael@0 | 85 | |
michael@0 | 86 | XPCOMUtils.defineLazyModuleGetter(this, "Logger", |
michael@0 | 87 | "resource://gre/modules/identity/LogUtils.jsm"); |
michael@0 | 88 | |
michael@0 | 89 | XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", |
michael@0 | 90 | "resource://gre/modules/SystemAppProxy.jsm"); |
michael@0 | 91 | |
michael@0 | 92 | // The default persona uri; can be overwritten with toolkit.identity.uri pref. |
michael@0 | 93 | // Do this if you want to repoint to a different service for testing. |
michael@0 | 94 | // There's no point in setting up an observer to monitor the pref, as b2g prefs |
michael@0 | 95 | // can only be overwritten when the profie is recreated. So just get the value |
michael@0 | 96 | // on start-up. |
michael@0 | 97 | let kPersonaUri = "https://firefoxos.persona.org"; |
michael@0 | 98 | try { |
michael@0 | 99 | kPersonaUri = Services.prefs.getCharPref("toolkit.identity.uri"); |
michael@0 | 100 | } catch(noSuchPref) { |
michael@0 | 101 | // stick with the default value |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | // JS shim that contains the callback functions that |
michael@0 | 105 | // live within the identity UI provisioning frame. |
michael@0 | 106 | const kIdentityShimFile = "chrome://b2g/content/identity.js"; |
michael@0 | 107 | |
michael@0 | 108 | // Type of MozChromeEvents to handle id dialogs. |
michael@0 | 109 | const kOpenIdentityDialog = "id-dialog-open"; |
michael@0 | 110 | const kDoneIdentityDialog = "id-dialog-done"; |
michael@0 | 111 | const kCloseIdentityDialog = "id-dialog-close-iframe"; |
michael@0 | 112 | |
michael@0 | 113 | // Observer messages to communicate to shim |
michael@0 | 114 | const kIdentityDelegateWatch = "identity-delegate-watch"; |
michael@0 | 115 | const kIdentityDelegateRequest = "identity-delegate-request"; |
michael@0 | 116 | const kIdentityDelegateLogout = "identity-delegate-logout"; |
michael@0 | 117 | const kIdentityDelegateFinished = "identity-delegate-finished"; |
michael@0 | 118 | const kIdentityDelegateReady = "identity-delegate-ready"; |
michael@0 | 119 | |
michael@0 | 120 | const kIdentityControllerDoMethod = "identity-controller-doMethod"; |
michael@0 | 121 | |
michael@0 | 122 | function log(...aMessageArgs) { |
michael@0 | 123 | Logger.log.apply(Logger, ["SignInToWebsiteController"].concat(aMessageArgs)); |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | log("persona uri =", kPersonaUri); |
michael@0 | 127 | |
michael@0 | 128 | function sendChromeEvent(details) { |
michael@0 | 129 | details.uri = kPersonaUri; |
michael@0 | 130 | SystemAppProxy.dispatchEvent(details); |
michael@0 | 131 | } |
michael@0 | 132 | |
michael@0 | 133 | function Pipe() { |
michael@0 | 134 | this._watchers = []; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | Pipe.prototype = { |
michael@0 | 138 | init: function pipe_init() { |
michael@0 | 139 | Services.obs.addObserver(this, "identity-child-process-shutdown", false); |
michael@0 | 140 | Services.obs.addObserver(this, "identity-controller-unwatch", false); |
michael@0 | 141 | }, |
michael@0 | 142 | |
michael@0 | 143 | uninit: function pipe_uninit() { |
michael@0 | 144 | Services.obs.removeObserver(this, "identity-child-process-shutdown"); |
michael@0 | 145 | Services.obs.removeObserver(this, "identity-controller-unwatch"); |
michael@0 | 146 | }, |
michael@0 | 147 | |
michael@0 | 148 | observe: function Pipe_observe(aSubject, aTopic, aData) { |
michael@0 | 149 | let options = {}; |
michael@0 | 150 | if (aSubject) { |
michael@0 | 151 | options = aSubject.wrappedJSObject; |
michael@0 | 152 | } |
michael@0 | 153 | switch (aTopic) { |
michael@0 | 154 | case "identity-child-process-shutdown": |
michael@0 | 155 | log("pipe removing watchers by message manager"); |
michael@0 | 156 | this._removeWatchers(null, options.messageManager); |
michael@0 | 157 | break; |
michael@0 | 158 | |
michael@0 | 159 | case "identity-controller-unwatch": |
michael@0 | 160 | log("unwatching", options.id); |
michael@0 | 161 | this._removeWatchers(options.id, options.messageManager); |
michael@0 | 162 | break; |
michael@0 | 163 | } |
michael@0 | 164 | }, |
michael@0 | 165 | |
michael@0 | 166 | _addWatcher: function Pipe__addWatcher(aId, aMm) { |
michael@0 | 167 | log("Adding watcher with id", aId); |
michael@0 | 168 | for (let i = 0; i < this._watchers.length; ++i) { |
michael@0 | 169 | let watcher = this._watchers[i]; |
michael@0 | 170 | if (this._watcher.id === aId) { |
michael@0 | 171 | watcher.count++; |
michael@0 | 172 | return; |
michael@0 | 173 | } |
michael@0 | 174 | } |
michael@0 | 175 | this._watchers.push({id: aId, count: 1, mm: aMm}); |
michael@0 | 176 | }, |
michael@0 | 177 | |
michael@0 | 178 | _removeWatchers: function Pipe__removeWatcher(aId, aMm) { |
michael@0 | 179 | let checkId = aId !== null; |
michael@0 | 180 | let index = -1; |
michael@0 | 181 | for (let i = 0; i < this._watchers.length; ++i) { |
michael@0 | 182 | let watcher = this._watchers[i]; |
michael@0 | 183 | if (watcher.mm === aMm && |
michael@0 | 184 | (!checkId || (checkId && watcher.id === aId))) { |
michael@0 | 185 | index = i; |
michael@0 | 186 | break; |
michael@0 | 187 | } |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | if (index !== -1) { |
michael@0 | 191 | if (checkId) { |
michael@0 | 192 | if (--(this._watchers[index].count) === 0) { |
michael@0 | 193 | this._watchers.splice(index, 1); |
michael@0 | 194 | } |
michael@0 | 195 | } else { |
michael@0 | 196 | this._watchers.splice(index, 1); |
michael@0 | 197 | } |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | if (this._watchers.length === 0) { |
michael@0 | 201 | log("No more watchers; clean up persona host iframe"); |
michael@0 | 202 | let detail = { |
michael@0 | 203 | type: kCloseIdentityDialog |
michael@0 | 204 | }; |
michael@0 | 205 | log('telling content to close the dialog'); |
michael@0 | 206 | // tell content to close the dialog |
michael@0 | 207 | sendChromeEvent(detail); |
michael@0 | 208 | } |
michael@0 | 209 | }, |
michael@0 | 210 | |
michael@0 | 211 | communicate: function(aRpOptions, aContentOptions, aMessageCallback) { |
michael@0 | 212 | let rpID = aRpOptions.id; |
michael@0 | 213 | let rpMM = aRpOptions.mm; |
michael@0 | 214 | if (rpMM) { |
michael@0 | 215 | this._addWatcher(rpID, rpMM); |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | log("RP options:", aRpOptions, "\n content options:", aContentOptions); |
michael@0 | 219 | |
michael@0 | 220 | // This content variable is injected into the scope of |
michael@0 | 221 | // kIdentityShimFile, where it is used to access the BrowserID object |
michael@0 | 222 | // and its internal API. |
michael@0 | 223 | let mm = null; |
michael@0 | 224 | let uuid = getRandomId(); |
michael@0 | 225 | let self = this; |
michael@0 | 226 | |
michael@0 | 227 | function removeMessageListeners() { |
michael@0 | 228 | if (mm) { |
michael@0 | 229 | mm.removeMessageListener(kIdentityDelegateFinished, identityDelegateFinished); |
michael@0 | 230 | mm.removeMessageListener(kIdentityControllerDoMethod, aMessageCallback); |
michael@0 | 231 | } |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | function identityDelegateFinished() { |
michael@0 | 235 | removeMessageListeners(); |
michael@0 | 236 | |
michael@0 | 237 | let detail = { |
michael@0 | 238 | type: kDoneIdentityDialog, |
michael@0 | 239 | showUI: aContentOptions.showUI || false, |
michael@0 | 240 | id: kDoneIdentityDialog + "-" + uuid, |
michael@0 | 241 | requestId: aRpOptions.id |
michael@0 | 242 | }; |
michael@0 | 243 | log('received delegate finished; telling content to close the dialog'); |
michael@0 | 244 | sendChromeEvent(detail); |
michael@0 | 245 | self._removeWatchers(rpID, rpMM); |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | SystemAppProxy.addEventListener("mozContentEvent", function getAssertion(evt) { |
michael@0 | 249 | let msg = evt.detail; |
michael@0 | 250 | if (!msg.id.match(uuid)) { |
michael@0 | 251 | return; |
michael@0 | 252 | } |
michael@0 | 253 | |
michael@0 | 254 | switch (msg.id) { |
michael@0 | 255 | case kOpenIdentityDialog + '-' + uuid: |
michael@0 | 256 | if (msg.type === 'cancel') { |
michael@0 | 257 | // The user closed the dialog. Clean up and call cancel. |
michael@0 | 258 | SystemAppProxy.removeEventListener("mozContentEvent", getAssertion); |
michael@0 | 259 | removeMessageListeners(); |
michael@0 | 260 | aMessageCallback({json: {method: "cancel"}}); |
michael@0 | 261 | } else { |
michael@0 | 262 | // The window has opened. Inject the identity shim file containing |
michael@0 | 263 | // the callbacks in the content script. This could be either the |
michael@0 | 264 | // visible popup that the user interacts with, or it could be an |
michael@0 | 265 | // invisible frame. |
michael@0 | 266 | let frame = evt.detail.frame; |
michael@0 | 267 | let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; |
michael@0 | 268 | mm = frameLoader.messageManager; |
michael@0 | 269 | try { |
michael@0 | 270 | mm.loadFrameScript(kIdentityShimFile, true, true); |
michael@0 | 271 | log("Loaded shim", kIdentityShimFile); |
michael@0 | 272 | } catch (e) { |
michael@0 | 273 | log("Error loading", kIdentityShimFile, "as a frame script:", e); |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | // There are two messages that the delegate can send back: a "do |
michael@0 | 277 | // method" event, and a "finished" event. We pass the do-method |
michael@0 | 278 | // events straight to the caller for interpretation and handling. |
michael@0 | 279 | // If we receive a "finished" event, then the delegate is done, so |
michael@0 | 280 | // we shut down the pipe and clean up. |
michael@0 | 281 | mm.addMessageListener(kIdentityControllerDoMethod, aMessageCallback); |
michael@0 | 282 | mm.addMessageListener(kIdentityDelegateFinished, identityDelegateFinished); |
michael@0 | 283 | |
michael@0 | 284 | mm.sendAsyncMessage(aContentOptions.message, aRpOptions); |
michael@0 | 285 | } |
michael@0 | 286 | break; |
michael@0 | 287 | |
michael@0 | 288 | case kDoneIdentityDialog + '-' + uuid: |
michael@0 | 289 | // Received our assertion. The message manager callbacks will handle |
michael@0 | 290 | // communicating back to the IDService. All we have to do is remove |
michael@0 | 291 | // this listener. |
michael@0 | 292 | SystemAppProxy.removeEventListener("mozContentEvent", getAssertion); |
michael@0 | 293 | break; |
michael@0 | 294 | |
michael@0 | 295 | default: |
michael@0 | 296 | log("ERROR - Unexpected message: id=" + msg.id + ", type=" + msg.type + ", errorMsg=" + msg.errorMsg); |
michael@0 | 297 | break; |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | }); |
michael@0 | 301 | |
michael@0 | 302 | // Tell content to open the identity iframe or trusty popup. The parameter |
michael@0 | 303 | // showUI signals whether user interaction is needed. If it is, content will |
michael@0 | 304 | // open a dialog; if not, a hidden iframe. In each case, BrowserID is |
michael@0 | 305 | // available in the context. |
michael@0 | 306 | let detail = { |
michael@0 | 307 | type: kOpenIdentityDialog, |
michael@0 | 308 | showUI: aContentOptions.showUI || false, |
michael@0 | 309 | id: kOpenIdentityDialog + "-" + uuid, |
michael@0 | 310 | requestId: aRpOptions.id |
michael@0 | 311 | }; |
michael@0 | 312 | |
michael@0 | 313 | sendChromeEvent(detail); |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | }; |
michael@0 | 317 | |
michael@0 | 318 | /* |
michael@0 | 319 | * The controller sits between the IdentityService used by DOMIdentity |
michael@0 | 320 | * and a content process launches an (invisible) iframe or (visible) |
michael@0 | 321 | * trusty UI. Using an injected js script (identity.js), the |
michael@0 | 322 | * controller enables the content window to access the persona identity |
michael@0 | 323 | * storage in the system cookie jar and send events back via the |
michael@0 | 324 | * controller into IdentityService and DOM, and ultimately up to the |
michael@0 | 325 | * Relying Party, which is open in a different window context. |
michael@0 | 326 | */ |
michael@0 | 327 | this.SignInToWebsiteController = { |
michael@0 | 328 | |
michael@0 | 329 | /* |
michael@0 | 330 | * Initialize the controller. To use a different content communication pipe, |
michael@0 | 331 | * such as when mocking it in tests, pass aOptions.pipe. |
michael@0 | 332 | */ |
michael@0 | 333 | init: function SignInToWebsiteController_init(aOptions) { |
michael@0 | 334 | aOptions = aOptions || {}; |
michael@0 | 335 | this.pipe = aOptions.pipe || new Pipe(); |
michael@0 | 336 | Services.obs.addObserver(this, "identity-controller-watch", false); |
michael@0 | 337 | Services.obs.addObserver(this, "identity-controller-request", false); |
michael@0 | 338 | Services.obs.addObserver(this, "identity-controller-logout", false); |
michael@0 | 339 | }, |
michael@0 | 340 | |
michael@0 | 341 | uninit: function SignInToWebsiteController_uninit() { |
michael@0 | 342 | Services.obs.removeObserver(this, "identity-controller-watch"); |
michael@0 | 343 | Services.obs.removeObserver(this, "identity-controller-request"); |
michael@0 | 344 | Services.obs.removeObserver(this, "identity-controller-logout"); |
michael@0 | 345 | }, |
michael@0 | 346 | |
michael@0 | 347 | observe: function SignInToWebsiteController_observe(aSubject, aTopic, aData) { |
michael@0 | 348 | log("observe: received", aTopic, "with", aData, "for", aSubject); |
michael@0 | 349 | let options = null; |
michael@0 | 350 | if (aSubject) { |
michael@0 | 351 | options = aSubject.wrappedJSObject; |
michael@0 | 352 | } |
michael@0 | 353 | switch (aTopic) { |
michael@0 | 354 | case "identity-controller-watch": |
michael@0 | 355 | this.doWatch(options); |
michael@0 | 356 | break; |
michael@0 | 357 | case "identity-controller-request": |
michael@0 | 358 | this.doRequest(options); |
michael@0 | 359 | break; |
michael@0 | 360 | case "identity-controller-logout": |
michael@0 | 361 | this.doLogout(options); |
michael@0 | 362 | break; |
michael@0 | 363 | default: |
michael@0 | 364 | Logger.reportError("SignInToWebsiteController", "Unknown observer notification:", aTopic); |
michael@0 | 365 | break; |
michael@0 | 366 | } |
michael@0 | 367 | }, |
michael@0 | 368 | |
michael@0 | 369 | /* |
michael@0 | 370 | * options: method required - name of method to invoke |
michael@0 | 371 | * assertion optional |
michael@0 | 372 | */ |
michael@0 | 373 | _makeDoMethodCallback: function SignInToWebsiteController__makeDoMethodCallback(aRpId) { |
michael@0 | 374 | return function SignInToWebsiteController_methodCallback(aOptions) { |
michael@0 | 375 | let message = aOptions.json; |
michael@0 | 376 | if (typeof message === 'string') { |
michael@0 | 377 | message = JSON.parse(message); |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | switch (message.method) { |
michael@0 | 381 | case "ready": |
michael@0 | 382 | IdentityService.doReady(aRpId); |
michael@0 | 383 | break; |
michael@0 | 384 | |
michael@0 | 385 | case "login": |
michael@0 | 386 | if (message._internalParams) { |
michael@0 | 387 | IdentityService.doLogin(aRpId, message.assertion, message._internalParams); |
michael@0 | 388 | } else { |
michael@0 | 389 | IdentityService.doLogin(aRpId, message.assertion); |
michael@0 | 390 | } |
michael@0 | 391 | break; |
michael@0 | 392 | |
michael@0 | 393 | case "logout": |
michael@0 | 394 | IdentityService.doLogout(aRpId); |
michael@0 | 395 | break; |
michael@0 | 396 | |
michael@0 | 397 | case "cancel": |
michael@0 | 398 | IdentityService.doCancel(aRpId); |
michael@0 | 399 | break; |
michael@0 | 400 | |
michael@0 | 401 | default: |
michael@0 | 402 | log("WARNING: wonky method call:", message.method); |
michael@0 | 403 | break; |
michael@0 | 404 | } |
michael@0 | 405 | }; |
michael@0 | 406 | }, |
michael@0 | 407 | |
michael@0 | 408 | doWatch: function SignInToWebsiteController_doWatch(aRpOptions) { |
michael@0 | 409 | // dom prevents watch from being called twice |
michael@0 | 410 | let contentOptions = { |
michael@0 | 411 | message: kIdentityDelegateWatch, |
michael@0 | 412 | showUI: false |
michael@0 | 413 | }; |
michael@0 | 414 | this.pipe.communicate(aRpOptions, contentOptions, |
michael@0 | 415 | this._makeDoMethodCallback(aRpOptions.id)); |
michael@0 | 416 | }, |
michael@0 | 417 | |
michael@0 | 418 | /** |
michael@0 | 419 | * The website is requesting login so the user must choose an identity to use. |
michael@0 | 420 | */ |
michael@0 | 421 | doRequest: function SignInToWebsiteController_doRequest(aRpOptions) { |
michael@0 | 422 | log("doRequest", aRpOptions); |
michael@0 | 423 | let contentOptions = { |
michael@0 | 424 | message: kIdentityDelegateRequest, |
michael@0 | 425 | showUI: true |
michael@0 | 426 | }; |
michael@0 | 427 | this.pipe.communicate(aRpOptions, contentOptions, |
michael@0 | 428 | this._makeDoMethodCallback(aRpOptions.id)); |
michael@0 | 429 | }, |
michael@0 | 430 | |
michael@0 | 431 | /* |
michael@0 | 432 | * |
michael@0 | 433 | */ |
michael@0 | 434 | doLogout: function SignInToWebsiteController_doLogout(aRpOptions) { |
michael@0 | 435 | log("doLogout", aRpOptions); |
michael@0 | 436 | let contentOptions = { |
michael@0 | 437 | message: kIdentityDelegateLogout, |
michael@0 | 438 | showUI: false |
michael@0 | 439 | }; |
michael@0 | 440 | this.pipe.communicate(aRpOptions, contentOptions, |
michael@0 | 441 | this._makeDoMethodCallback(aRpOptions.id)); |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | }; |