|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = ["SignInToWebsiteUX"]; |
|
8 |
|
9 const Cc = Components.classes; |
|
10 const Ci = Components.interfaces; |
|
11 const Cu = Components.utils; |
|
12 |
|
13 Cu.import("resource://gre/modules/Services.jsm"); |
|
14 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
15 |
|
16 XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", |
|
17 "resource://gre/modules/identity/Identity.jsm"); |
|
18 |
|
19 XPCOMUtils.defineLazyModuleGetter(this, "Logger", |
|
20 "resource://gre/modules/identity/LogUtils.jsm"); |
|
21 |
|
22 function log(...aMessageArgs) { |
|
23 Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs)); |
|
24 } |
|
25 |
|
26 this.SignInToWebsiteUX = { |
|
27 |
|
28 init: function SignInToWebsiteUX_init() { |
|
29 |
|
30 Services.obs.addObserver(this, "identity-request", false); |
|
31 Services.obs.addObserver(this, "identity-auth", false); |
|
32 Services.obs.addObserver(this, "identity-auth-complete", false); |
|
33 Services.obs.addObserver(this, "identity-login-state-changed", false); |
|
34 }, |
|
35 |
|
36 uninit: function SignInToWebsiteUX_uninit() { |
|
37 Services.obs.removeObserver(this, "identity-request"); |
|
38 Services.obs.removeObserver(this, "identity-auth"); |
|
39 Services.obs.removeObserver(this, "identity-auth-complete"); |
|
40 Services.obs.removeObserver(this, "identity-login-state-changed"); |
|
41 }, |
|
42 |
|
43 observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) { |
|
44 log("observe: received", aTopic, "with", aData, "for", aSubject); |
|
45 let options = null; |
|
46 if (aSubject) { |
|
47 options = aSubject.wrappedJSObject; |
|
48 } |
|
49 switch(aTopic) { |
|
50 case "identity-request": |
|
51 this.requestLogin(options); |
|
52 break; |
|
53 case "identity-auth": |
|
54 this._openAuthenticationUI(aData, options); |
|
55 break; |
|
56 case "identity-auth-complete": |
|
57 this._closeAuthenticationUI(aData); |
|
58 break; |
|
59 case "identity-login-state-changed": |
|
60 let emailAddress = aData; |
|
61 if (emailAddress) { |
|
62 this._removeRequestUI(options); |
|
63 this._showLoggedInUI(emailAddress, options); |
|
64 } else { |
|
65 this._removeLoggedInUI(options); |
|
66 } |
|
67 break; |
|
68 default: |
|
69 Logger.reportError("SignInToWebsiteUX", "Unknown observer notification:", aTopic); |
|
70 break; |
|
71 } |
|
72 }, |
|
73 |
|
74 /** |
|
75 * The website is requesting login so the user must choose an identity to use. |
|
76 */ |
|
77 requestLogin: function SignInToWebsiteUX_requestLogin(aOptions) { |
|
78 let windowID = aOptions.rpId; |
|
79 log("requestLogin", aOptions); |
|
80 let [chromeWin, browserEl] = this._getUIForWindowID(windowID); |
|
81 |
|
82 // message is not shown in the UI but is required |
|
83 let message = aOptions.origin; |
|
84 let mainAction = { |
|
85 label: chromeWin.gNavigatorBundle.getString("identity.next.label"), |
|
86 accessKey: chromeWin.gNavigatorBundle.getString("identity.next.accessKey"), |
|
87 callback: function() {}, // required |
|
88 }; |
|
89 let options = { |
|
90 identity: { |
|
91 origin: aOptions.origin, |
|
92 }, |
|
93 }; |
|
94 let secondaryActions = []; |
|
95 |
|
96 // add some extra properties to the notification to store some identity-related state |
|
97 for (let opt in aOptions) { |
|
98 options.identity[opt] = aOptions[opt]; |
|
99 } |
|
100 log("requestLogin: rpId: ", options.identity.rpId); |
|
101 |
|
102 chromeWin.PopupNotifications.show(browserEl, "identity-request", message, |
|
103 "identity-notification-icon", mainAction, |
|
104 [], options); |
|
105 }, |
|
106 |
|
107 /** |
|
108 * Get the list of possible identities to login to the given origin. |
|
109 */ |
|
110 getIdentitiesForSite: function SignInToWebsiteUX_getIdentitiesForSite(aOrigin) { |
|
111 return IdentityService.RP.getIdentitiesForSite(aOrigin); |
|
112 }, |
|
113 |
|
114 /** |
|
115 * User chose a new or existing identity from the doorhanger after a request() call |
|
116 */ |
|
117 selectIdentity: function SignInToWebsiteUX_selectIdentity(aRpId, aIdentity) { |
|
118 log("selectIdentity: rpId: ", aRpId, " identity: ", aIdentity); |
|
119 IdentityService.selectIdentity(aRpId, aIdentity); |
|
120 }, |
|
121 |
|
122 // Private |
|
123 |
|
124 /** |
|
125 * Return the chrome window and <browser> for the given outer window ID. |
|
126 */ |
|
127 _getUIForWindowID: function(aWindowID) { |
|
128 let content = Services.wm.getOuterWindowWithId(aWindowID); |
|
129 if (content) { |
|
130 let browser = content.QueryInterface(Ci.nsIInterfaceRequestor) |
|
131 .getInterface(Ci.nsIWebNavigation) |
|
132 .QueryInterface(Ci.nsIDocShell).chromeEventHandler; |
|
133 let chromeWin = browser.ownerDocument.defaultView; |
|
134 return [chromeWin, browser]; |
|
135 } |
|
136 |
|
137 Logger.reportError("SignInToWebsiteUX", "no content"); |
|
138 return [null, null]; |
|
139 }, |
|
140 |
|
141 /** |
|
142 * Open UI with a content frame displaying aAuthURI so that the user can authenticate with their |
|
143 * IDP. Then tell Identity.jsm the identifier for the window so that it knows that the DOM API |
|
144 * calls are for this authentication flow. |
|
145 */ |
|
146 _openAuthenticationUI: function _openAuthenticationUI(aAuthURI, aContext) { |
|
147 // Open a tab/window with aAuthURI with an identifier (aID) attached so that the DOM APIs know this is an auth. window. |
|
148 let chromeWin = Services.wm.getMostRecentWindow('navigator:browser'); |
|
149 let features = "chrome=false,width=640,height=480,centerscreen,location=yes,resizable=yes,scrollbars=yes,status=yes"; |
|
150 log("aAuthURI: ", aAuthURI); |
|
151 let authWin = Services.ww.openWindow(chromeWin, "about:blank", "", features, null); |
|
152 let windowID = authWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID; |
|
153 log("authWin outer id: ", windowID); |
|
154 |
|
155 let provId = aContext.provId; |
|
156 // Tell the ID service about the id before loading the url |
|
157 IdentityService.IDP.setAuthenticationFlow(windowID, provId); |
|
158 |
|
159 authWin.location = aAuthURI; |
|
160 }, |
|
161 |
|
162 _closeAuthenticationUI: function _closeAuthenticationUI(aAuthId) { |
|
163 log("_closeAuthenticationUI:", aAuthId); |
|
164 let [chromeWin, browserEl] = this._getUIForWindowID(aAuthId); |
|
165 if (chromeWin) |
|
166 chromeWin.close(); |
|
167 else |
|
168 Logger.reportError("SignInToWebsite", "Could not close window with ID", aAuthId); |
|
169 }, |
|
170 |
|
171 /** |
|
172 * Show a doorhanger indicating the currently logged-in user. |
|
173 */ |
|
174 _showLoggedInUI: function _showLoggedInUI(aIdentity, aContext) { |
|
175 let windowID = aContext.rpId; |
|
176 log("_showLoggedInUI for ", windowID); |
|
177 let [chromeWin, browserEl] = this._getUIForWindowID(windowID); |
|
178 |
|
179 let message = chromeWin.gNavigatorBundle.getFormattedString("identity.loggedIn.description", |
|
180 [aIdentity]); |
|
181 let mainAction = { |
|
182 label: chromeWin.gNavigatorBundle.getString("identity.loggedIn.signOut.label"), |
|
183 accessKey: chromeWin.gNavigatorBundle.getString("identity.loggedIn.signOut.accessKey"), |
|
184 callback: function() { |
|
185 log("sign out callback fired"); |
|
186 IdentityService.RP.logout(windowID); |
|
187 }, |
|
188 }; |
|
189 let secondaryActions = []; |
|
190 let options = { |
|
191 dismissed: true, |
|
192 }; |
|
193 let loggedInNot = chromeWin.PopupNotifications.show(browserEl, "identity-logged-in", message, |
|
194 "identity-notification-icon", mainAction, |
|
195 secondaryActions, options); |
|
196 loggedInNot.rpId = windowID; |
|
197 }, |
|
198 |
|
199 /** |
|
200 * Remove the doorhanger indicating the currently logged-in user. |
|
201 */ |
|
202 _removeLoggedInUI: function _removeLoggedInUI(aContext) { |
|
203 let windowID = aContext.rpId; |
|
204 log("_removeLoggedInUI for ", windowID); |
|
205 if (!windowID) |
|
206 throw "_removeLoggedInUI: Invalid RP ID"; |
|
207 let [chromeWin, browserEl] = this._getUIForWindowID(windowID); |
|
208 |
|
209 let loggedInNot = chromeWin.PopupNotifications.getNotification("identity-logged-in", browserEl); |
|
210 if (loggedInNot) |
|
211 chromeWin.PopupNotifications.remove(loggedInNot); |
|
212 }, |
|
213 |
|
214 /** |
|
215 * Remove the doorhanger indicating the currently logged-in user. |
|
216 */ |
|
217 _removeRequestUI: function _removeRequestUI(aContext) { |
|
218 let windowID = aContext.rpId; |
|
219 log("_removeRequestUI for ", windowID); |
|
220 let [chromeWin, browserEl] = this._getUIForWindowID(windowID); |
|
221 |
|
222 let requestNot = chromeWin.PopupNotifications.getNotification("identity-request", browserEl); |
|
223 if (requestNot) |
|
224 chromeWin.PopupNotifications.remove(requestNot); |
|
225 }, |
|
226 |
|
227 }; |