1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/modules/SignInToWebsite.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,227 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ["SignInToWebsiteUX"]; 1.11 + 1.12 +const Cc = Components.classes; 1.13 +const Ci = Components.interfaces; 1.14 +const Cu = Components.utils; 1.15 + 1.16 +Cu.import("resource://gre/modules/Services.jsm"); 1.17 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", 1.20 + "resource://gre/modules/identity/Identity.jsm"); 1.21 + 1.22 +XPCOMUtils.defineLazyModuleGetter(this, "Logger", 1.23 + "resource://gre/modules/identity/LogUtils.jsm"); 1.24 + 1.25 +function log(...aMessageArgs) { 1.26 + Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs)); 1.27 +} 1.28 + 1.29 +this.SignInToWebsiteUX = { 1.30 + 1.31 + init: function SignInToWebsiteUX_init() { 1.32 + 1.33 + Services.obs.addObserver(this, "identity-request", false); 1.34 + Services.obs.addObserver(this, "identity-auth", false); 1.35 + Services.obs.addObserver(this, "identity-auth-complete", false); 1.36 + Services.obs.addObserver(this, "identity-login-state-changed", false); 1.37 + }, 1.38 + 1.39 + uninit: function SignInToWebsiteUX_uninit() { 1.40 + Services.obs.removeObserver(this, "identity-request"); 1.41 + Services.obs.removeObserver(this, "identity-auth"); 1.42 + Services.obs.removeObserver(this, "identity-auth-complete"); 1.43 + Services.obs.removeObserver(this, "identity-login-state-changed"); 1.44 + }, 1.45 + 1.46 + observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) { 1.47 + log("observe: received", aTopic, "with", aData, "for", aSubject); 1.48 + let options = null; 1.49 + if (aSubject) { 1.50 + options = aSubject.wrappedJSObject; 1.51 + } 1.52 + switch(aTopic) { 1.53 + case "identity-request": 1.54 + this.requestLogin(options); 1.55 + break; 1.56 + case "identity-auth": 1.57 + this._openAuthenticationUI(aData, options); 1.58 + break; 1.59 + case "identity-auth-complete": 1.60 + this._closeAuthenticationUI(aData); 1.61 + break; 1.62 + case "identity-login-state-changed": 1.63 + let emailAddress = aData; 1.64 + if (emailAddress) { 1.65 + this._removeRequestUI(options); 1.66 + this._showLoggedInUI(emailAddress, options); 1.67 + } else { 1.68 + this._removeLoggedInUI(options); 1.69 + } 1.70 + break; 1.71 + default: 1.72 + Logger.reportError("SignInToWebsiteUX", "Unknown observer notification:", aTopic); 1.73 + break; 1.74 + } 1.75 + }, 1.76 + 1.77 + /** 1.78 + * The website is requesting login so the user must choose an identity to use. 1.79 + */ 1.80 + requestLogin: function SignInToWebsiteUX_requestLogin(aOptions) { 1.81 + let windowID = aOptions.rpId; 1.82 + log("requestLogin", aOptions); 1.83 + let [chromeWin, browserEl] = this._getUIForWindowID(windowID); 1.84 + 1.85 + // message is not shown in the UI but is required 1.86 + let message = aOptions.origin; 1.87 + let mainAction = { 1.88 + label: chromeWin.gNavigatorBundle.getString("identity.next.label"), 1.89 + accessKey: chromeWin.gNavigatorBundle.getString("identity.next.accessKey"), 1.90 + callback: function() {}, // required 1.91 + }; 1.92 + let options = { 1.93 + identity: { 1.94 + origin: aOptions.origin, 1.95 + }, 1.96 + }; 1.97 + let secondaryActions = []; 1.98 + 1.99 + // add some extra properties to the notification to store some identity-related state 1.100 + for (let opt in aOptions) { 1.101 + options.identity[opt] = aOptions[opt]; 1.102 + } 1.103 + log("requestLogin: rpId: ", options.identity.rpId); 1.104 + 1.105 + chromeWin.PopupNotifications.show(browserEl, "identity-request", message, 1.106 + "identity-notification-icon", mainAction, 1.107 + [], options); 1.108 + }, 1.109 + 1.110 + /** 1.111 + * Get the list of possible identities to login to the given origin. 1.112 + */ 1.113 + getIdentitiesForSite: function SignInToWebsiteUX_getIdentitiesForSite(aOrigin) { 1.114 + return IdentityService.RP.getIdentitiesForSite(aOrigin); 1.115 + }, 1.116 + 1.117 + /** 1.118 + * User chose a new or existing identity from the doorhanger after a request() call 1.119 + */ 1.120 + selectIdentity: function SignInToWebsiteUX_selectIdentity(aRpId, aIdentity) { 1.121 + log("selectIdentity: rpId: ", aRpId, " identity: ", aIdentity); 1.122 + IdentityService.selectIdentity(aRpId, aIdentity); 1.123 + }, 1.124 + 1.125 + // Private 1.126 + 1.127 + /** 1.128 + * Return the chrome window and <browser> for the given outer window ID. 1.129 + */ 1.130 + _getUIForWindowID: function(aWindowID) { 1.131 + let content = Services.wm.getOuterWindowWithId(aWindowID); 1.132 + if (content) { 1.133 + let browser = content.QueryInterface(Ci.nsIInterfaceRequestor) 1.134 + .getInterface(Ci.nsIWebNavigation) 1.135 + .QueryInterface(Ci.nsIDocShell).chromeEventHandler; 1.136 + let chromeWin = browser.ownerDocument.defaultView; 1.137 + return [chromeWin, browser]; 1.138 + } 1.139 + 1.140 + Logger.reportError("SignInToWebsiteUX", "no content"); 1.141 + return [null, null]; 1.142 + }, 1.143 + 1.144 + /** 1.145 + * Open UI with a content frame displaying aAuthURI so that the user can authenticate with their 1.146 + * IDP. Then tell Identity.jsm the identifier for the window so that it knows that the DOM API 1.147 + * calls are for this authentication flow. 1.148 + */ 1.149 + _openAuthenticationUI: function _openAuthenticationUI(aAuthURI, aContext) { 1.150 + // Open a tab/window with aAuthURI with an identifier (aID) attached so that the DOM APIs know this is an auth. window. 1.151 + let chromeWin = Services.wm.getMostRecentWindow('navigator:browser'); 1.152 + let features = "chrome=false,width=640,height=480,centerscreen,location=yes,resizable=yes,scrollbars=yes,status=yes"; 1.153 + log("aAuthURI: ", aAuthURI); 1.154 + let authWin = Services.ww.openWindow(chromeWin, "about:blank", "", features, null); 1.155 + let windowID = authWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID; 1.156 + log("authWin outer id: ", windowID); 1.157 + 1.158 + let provId = aContext.provId; 1.159 + // Tell the ID service about the id before loading the url 1.160 + IdentityService.IDP.setAuthenticationFlow(windowID, provId); 1.161 + 1.162 + authWin.location = aAuthURI; 1.163 + }, 1.164 + 1.165 + _closeAuthenticationUI: function _closeAuthenticationUI(aAuthId) { 1.166 + log("_closeAuthenticationUI:", aAuthId); 1.167 + let [chromeWin, browserEl] = this._getUIForWindowID(aAuthId); 1.168 + if (chromeWin) 1.169 + chromeWin.close(); 1.170 + else 1.171 + Logger.reportError("SignInToWebsite", "Could not close window with ID", aAuthId); 1.172 + }, 1.173 + 1.174 + /** 1.175 + * Show a doorhanger indicating the currently logged-in user. 1.176 + */ 1.177 + _showLoggedInUI: function _showLoggedInUI(aIdentity, aContext) { 1.178 + let windowID = aContext.rpId; 1.179 + log("_showLoggedInUI for ", windowID); 1.180 + let [chromeWin, browserEl] = this._getUIForWindowID(windowID); 1.181 + 1.182 + let message = chromeWin.gNavigatorBundle.getFormattedString("identity.loggedIn.description", 1.183 + [aIdentity]); 1.184 + let mainAction = { 1.185 + label: chromeWin.gNavigatorBundle.getString("identity.loggedIn.signOut.label"), 1.186 + accessKey: chromeWin.gNavigatorBundle.getString("identity.loggedIn.signOut.accessKey"), 1.187 + callback: function() { 1.188 + log("sign out callback fired"); 1.189 + IdentityService.RP.logout(windowID); 1.190 + }, 1.191 + }; 1.192 + let secondaryActions = []; 1.193 + let options = { 1.194 + dismissed: true, 1.195 + }; 1.196 + let loggedInNot = chromeWin.PopupNotifications.show(browserEl, "identity-logged-in", message, 1.197 + "identity-notification-icon", mainAction, 1.198 + secondaryActions, options); 1.199 + loggedInNot.rpId = windowID; 1.200 + }, 1.201 + 1.202 + /** 1.203 + * Remove the doorhanger indicating the currently logged-in user. 1.204 + */ 1.205 + _removeLoggedInUI: function _removeLoggedInUI(aContext) { 1.206 + let windowID = aContext.rpId; 1.207 + log("_removeLoggedInUI for ", windowID); 1.208 + if (!windowID) 1.209 + throw "_removeLoggedInUI: Invalid RP ID"; 1.210 + let [chromeWin, browserEl] = this._getUIForWindowID(windowID); 1.211 + 1.212 + let loggedInNot = chromeWin.PopupNotifications.getNotification("identity-logged-in", browserEl); 1.213 + if (loggedInNot) 1.214 + chromeWin.PopupNotifications.remove(loggedInNot); 1.215 + }, 1.216 + 1.217 + /** 1.218 + * Remove the doorhanger indicating the currently logged-in user. 1.219 + */ 1.220 + _removeRequestUI: function _removeRequestUI(aContext) { 1.221 + let windowID = aContext.rpId; 1.222 + log("_removeRequestUI for ", windowID); 1.223 + let [chromeWin, browserEl] = this._getUIForWindowID(windowID); 1.224 + 1.225 + let requestNot = chromeWin.PopupNotifications.getNotification("identity-request", browserEl); 1.226 + if (requestNot) 1.227 + chromeWin.PopupNotifications.remove(requestNot); 1.228 + }, 1.229 + 1.230 +};