diff -r 000000000000 -r 6474c204b198 browser/modules/SignInToWebsite.jsm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/browser/modules/SignInToWebsite.jsm Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,227 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["SignInToWebsiteUX"]; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", + "resource://gre/modules/identity/Identity.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Logger", + "resource://gre/modules/identity/LogUtils.jsm"); + +function log(...aMessageArgs) { + Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs)); +} + +this.SignInToWebsiteUX = { + + init: function SignInToWebsiteUX_init() { + + Services.obs.addObserver(this, "identity-request", false); + Services.obs.addObserver(this, "identity-auth", false); + Services.obs.addObserver(this, "identity-auth-complete", false); + Services.obs.addObserver(this, "identity-login-state-changed", false); + }, + + uninit: function SignInToWebsiteUX_uninit() { + Services.obs.removeObserver(this, "identity-request"); + Services.obs.removeObserver(this, "identity-auth"); + Services.obs.removeObserver(this, "identity-auth-complete"); + Services.obs.removeObserver(this, "identity-login-state-changed"); + }, + + observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) { + log("observe: received", aTopic, "with", aData, "for", aSubject); + let options = null; + if (aSubject) { + options = aSubject.wrappedJSObject; + } + switch(aTopic) { + case "identity-request": + this.requestLogin(options); + break; + case "identity-auth": + this._openAuthenticationUI(aData, options); + break; + case "identity-auth-complete": + this._closeAuthenticationUI(aData); + break; + case "identity-login-state-changed": + let emailAddress = aData; + if (emailAddress) { + this._removeRequestUI(options); + this._showLoggedInUI(emailAddress, options); + } else { + this._removeLoggedInUI(options); + } + break; + default: + Logger.reportError("SignInToWebsiteUX", "Unknown observer notification:", aTopic); + break; + } + }, + + /** + * The website is requesting login so the user must choose an identity to use. + */ + requestLogin: function SignInToWebsiteUX_requestLogin(aOptions) { + let windowID = aOptions.rpId; + log("requestLogin", aOptions); + let [chromeWin, browserEl] = this._getUIForWindowID(windowID); + + // message is not shown in the UI but is required + let message = aOptions.origin; + let mainAction = { + label: chromeWin.gNavigatorBundle.getString("identity.next.label"), + accessKey: chromeWin.gNavigatorBundle.getString("identity.next.accessKey"), + callback: function() {}, // required + }; + let options = { + identity: { + origin: aOptions.origin, + }, + }; + let secondaryActions = []; + + // add some extra properties to the notification to store some identity-related state + for (let opt in aOptions) { + options.identity[opt] = aOptions[opt]; + } + log("requestLogin: rpId: ", options.identity.rpId); + + chromeWin.PopupNotifications.show(browserEl, "identity-request", message, + "identity-notification-icon", mainAction, + [], options); + }, + + /** + * Get the list of possible identities to login to the given origin. + */ + getIdentitiesForSite: function SignInToWebsiteUX_getIdentitiesForSite(aOrigin) { + return IdentityService.RP.getIdentitiesForSite(aOrigin); + }, + + /** + * User chose a new or existing identity from the doorhanger after a request() call + */ + selectIdentity: function SignInToWebsiteUX_selectIdentity(aRpId, aIdentity) { + log("selectIdentity: rpId: ", aRpId, " identity: ", aIdentity); + IdentityService.selectIdentity(aRpId, aIdentity); + }, + + // Private + + /** + * Return the chrome window and for the given outer window ID. + */ + _getUIForWindowID: function(aWindowID) { + let content = Services.wm.getOuterWindowWithId(aWindowID); + if (content) { + let browser = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell).chromeEventHandler; + let chromeWin = browser.ownerDocument.defaultView; + return [chromeWin, browser]; + } + + Logger.reportError("SignInToWebsiteUX", "no content"); + return [null, null]; + }, + + /** + * Open UI with a content frame displaying aAuthURI so that the user can authenticate with their + * IDP. Then tell Identity.jsm the identifier for the window so that it knows that the DOM API + * calls are for this authentication flow. + */ + _openAuthenticationUI: function _openAuthenticationUI(aAuthURI, aContext) { + // Open a tab/window with aAuthURI with an identifier (aID) attached so that the DOM APIs know this is an auth. window. + let chromeWin = Services.wm.getMostRecentWindow('navigator:browser'); + let features = "chrome=false,width=640,height=480,centerscreen,location=yes,resizable=yes,scrollbars=yes,status=yes"; + log("aAuthURI: ", aAuthURI); + let authWin = Services.ww.openWindow(chromeWin, "about:blank", "", features, null); + let windowID = authWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID; + log("authWin outer id: ", windowID); + + let provId = aContext.provId; + // Tell the ID service about the id before loading the url + IdentityService.IDP.setAuthenticationFlow(windowID, provId); + + authWin.location = aAuthURI; + }, + + _closeAuthenticationUI: function _closeAuthenticationUI(aAuthId) { + log("_closeAuthenticationUI:", aAuthId); + let [chromeWin, browserEl] = this._getUIForWindowID(aAuthId); + if (chromeWin) + chromeWin.close(); + else + Logger.reportError("SignInToWebsite", "Could not close window with ID", aAuthId); + }, + + /** + * Show a doorhanger indicating the currently logged-in user. + */ + _showLoggedInUI: function _showLoggedInUI(aIdentity, aContext) { + let windowID = aContext.rpId; + log("_showLoggedInUI for ", windowID); + let [chromeWin, browserEl] = this._getUIForWindowID(windowID); + + let message = chromeWin.gNavigatorBundle.getFormattedString("identity.loggedIn.description", + [aIdentity]); + let mainAction = { + label: chromeWin.gNavigatorBundle.getString("identity.loggedIn.signOut.label"), + accessKey: chromeWin.gNavigatorBundle.getString("identity.loggedIn.signOut.accessKey"), + callback: function() { + log("sign out callback fired"); + IdentityService.RP.logout(windowID); + }, + }; + let secondaryActions = []; + let options = { + dismissed: true, + }; + let loggedInNot = chromeWin.PopupNotifications.show(browserEl, "identity-logged-in", message, + "identity-notification-icon", mainAction, + secondaryActions, options); + loggedInNot.rpId = windowID; + }, + + /** + * Remove the doorhanger indicating the currently logged-in user. + */ + _removeLoggedInUI: function _removeLoggedInUI(aContext) { + let windowID = aContext.rpId; + log("_removeLoggedInUI for ", windowID); + if (!windowID) + throw "_removeLoggedInUI: Invalid RP ID"; + let [chromeWin, browserEl] = this._getUIForWindowID(windowID); + + let loggedInNot = chromeWin.PopupNotifications.getNotification("identity-logged-in", browserEl); + if (loggedInNot) + chromeWin.PopupNotifications.remove(loggedInNot); + }, + + /** + * Remove the doorhanger indicating the currently logged-in user. + */ + _removeRequestUI: function _removeRequestUI(aContext) { + let windowID = aContext.rpId; + log("_removeRequestUI for ", windowID); + let [chromeWin, browserEl] = this._getUIForWindowID(windowID); + + let requestNot = chromeWin.PopupNotifications.getNotification("identity-request", browserEl); + if (requestNot) + chromeWin.PopupNotifications.remove(requestNot); + }, + +};