Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / login / screen_gaia_signin.js
blob4f9b0521c25999891a2c165b70a52992ab9a9ef2
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * @fileoverview Oobe signin screen implementation.
7  */
9 <include src="../../gaia_auth_host/gaia_auth_host.js"></include>
11 login.createScreen('GaiaSigninScreen', 'gaia-signin', function() {
12   // Gaia loading time after which error message must be displayed and
13   // lazy portal check should be fired.
14   /** @const */ var GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC = 7;
16   // Maximum Gaia loading time in seconds.
17   /** @const */ var MAX_GAIA_LOADING_TIME_SEC = 60;
19   /** @const */ var HELP_TOPIC_ENTERPRISE_REPORTING = 2535613;
21   return {
22     EXTERNAL_API: [
23       'loadAuthExtension',
24       'updateAuthExtension',
25       'setAuthenticatedUserEmail',
26       'doReload',
27       'onFrameError'
28     ],
30     /**
31      * Frame loading error code (0 - no error).
32      * @type {number}
33      * @private
34      */
35     error_: 0,
37     /**
38      * Saved gaia auth host load params.
39      * @type {?string}
40      * @private
41      */
42     gaiaAuthParams_: null,
44     /**
45      * Whether local version of Gaia page is used.
46      * @type {boolean}
47      * @private
48      */
49     isLocal_: false,
51     /**
52      * Email of the user, which is logging in using offline mode.
53      * @type {string}
54      */
55     email: '',
57     /**
58      * Timer id of pending load.
59      * @type {number}
60      * @private
61      */
62     loadingTimer_: undefined,
64     /**
65      * Whether user can cancel Gaia screen.
66      * @type {boolean}
67      * @private
68      */
69     cancelAllowed_: undefined,
71     /** @override */
72     decorate: function() {
73       this.gaiaAuthHost_ = new cr.login.GaiaAuthHost($('signin-frame'));
74       this.gaiaAuthHost_.addEventListener(
75           'ready', this.onAuthReady_.bind(this));
76       this.gaiaAuthHost_.retrieveAuthenticatedUserEmailCallback =
77           this.onRetrieveAuthenticatedUserEmail_.bind(this);
78       this.gaiaAuthHost_.confirmPasswordCallback =
79           this.onAuthConfirmPassword_.bind(this);
80       this.gaiaAuthHost_.noPasswordCallback =
81           this.onAuthNoPassword_.bind(this);
82       this.gaiaAuthHost_.addEventListener('authFlowChange',
83           this.onAuthFlowChange_.bind(this));
85       $('enterprise-info-hint-link').addEventListener('click', function(e) {
86         chrome.send('launchHelpApp', [HELP_TOPIC_ENTERPRISE_REPORTING]);
87         e.preventDefault();
88       });
91       this.updateLocalizedContent();
92     },
94     /**
95      * Header text of the screen.
96      * @type {string}
97      */
98     get header() {
99       return loadTimeData.getString('signinScreenTitle');
100     },
102     /**
103      * Returns true if local version of Gaia is used.
104      * @type {boolean}
105      */
106     get isLocal() {
107       return this.isLocal_;
108     },
110     /**
111      * Sets whether local version of Gaia is used.
112      * @param {boolean} value Whether local version of Gaia is used.
113      */
114     set isLocal(value) {
115       this.isLocal_ = value;
116       chrome.send('updateOfflineLogin', [value]);
117     },
119     /**
120      * Shows/hides loading UI.
121      * @param {boolean} show True to show loading UI.
122      * @private
123      */
124     showLoadingUI_: function(show) {
125       $('gaia-loading').hidden = !show;
126       this.gaiaAuthHost_.frame.hidden = show;
127       $('signin-right').hidden = show;
128       $('enterprise-info-container').hidden = show;
129       $('gaia-signin-divider').hidden = show;
130     },
132     /**
133      * Handler for Gaia loading suspiciously long timeout.
134      * @private
135      */
136     onLoadingSuspiciouslyLong_: function() {
137       if (this != Oobe.getInstance().currentScreen)
138         return;
139       chrome.send('showLoadingTimeoutError');
140       this.loadingTimer_ = window.setTimeout(
141           this.onLoadingTimeOut_.bind(this),
142           (MAX_GAIA_LOADING_TIME_SEC - GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC) *
143           1000);
144     },
146     /**
147      * Handler for Gaia loading timeout.
148      * @private
149      */
150     onLoadingTimeOut_: function() {
151       this.loadingTimer_ = undefined;
152       chrome.send('showLoadingTimeoutError');
153     },
155     /**
156      * Clears loading timer.
157      * @private
158      */
159     clearLoadingTimer_: function() {
160       if (this.loadingTimer_) {
161         window.clearTimeout(this.loadingTimer_);
162         this.loadingTimer_ = undefined;
163       }
164     },
166     /**
167      * Sets up loading timer.
168      * @private
169      */
170     startLoadingTimer_: function() {
171       this.clearLoadingTimer_();
172       this.loadingTimer_ = window.setTimeout(
173           this.onLoadingSuspiciouslyLong_.bind(this),
174           GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC * 1000);
175     },
177     /**
178      * Whether Gaia is loading.
179      * @type {boolean}
180      */
181     get loading() {
182       return !$('gaia-loading').hidden;
183     },
184     set loading(loading) {
185       if (loading == this.loading)
186         return;
188       this.showLoadingUI_(loading);
189     },
191     /**
192      * Event handler that is invoked just before the frame is shown.
193      * @param {string} data Screen init payload. Url of auth extension start
194      *                      page.
195      */
196     onBeforeShow: function(data) {
197       chrome.send('loginUIStateChanged', ['gaia-signin', true]);
198       $('login-header-bar').signinUIState = SIGNIN_UI_STATE.GAIA_SIGNIN;
200       // Ensure that GAIA signin (or loading UI) is actually visible.
201       window.webkitRequestAnimationFrame(function() {
202         chrome.send('loginVisible', ['gaia-loading']);
203       });
205       // Announce the name of the screen, if accessibility is on.
206       $('gaia-signin-aria-label').setAttribute(
207           'aria-label', loadTimeData.getString('signinScreenTitle'));
209       // Button header is always visible when sign in is presented.
210       // Header is hidden once GAIA reports on successful sign in.
211       Oobe.getInstance().headerHidden = false;
212     },
214     /**
215      * Event handler that is invoked just before the screen is hidden.
216      */
217     onBeforeHide: function() {
218       chrome.send('loginUIStateChanged', ['gaia-signin', false]);
219       $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN;
220     },
222     /**
223      * Loads the authentication extension into the iframe.
224      * @param {Object} data Extension parameters bag.
225      * @private
226      */
227     loadAuthExtension: function(data) {
228       this.isLocal = data.isLocal;
229       this.email = '';
230       this.classList.toggle('saml', false);
232       this.updateAuthExtension(data);
234       var params = {};
235       for (var i in cr.login.GaiaAuthHost.SUPPORTED_PARAMS) {
236         var name = cr.login.GaiaAuthHost.SUPPORTED_PARAMS[i];
237         if (data[name])
238           params[name] = data[name];
239       }
241       if (data.localizedStrings)
242         params.localizedStrings = data.localizedStrings;
244       if (data.forceReload ||
245           JSON.stringify(this.gaiaAuthParams_) != JSON.stringify(params)) {
246         this.error_ = 0;
247         this.gaiaAuthHost_.load(data.useOffline ?
248                                     cr.login.GaiaAuthHost.AuthMode.OFFLINE :
249                                     cr.login.GaiaAuthHost.AuthMode.DEFAULT,
250                                 params,
251                                 this.onAuthCompleted_.bind(this));
252         this.gaiaAuthParams_ = params;
254         this.loading = true;
255         this.startLoadingTimer_();
256       } else if (this.loading && this.error_) {
257         // An error has occurred, so trying to reload.
258         this.doReload();
259       }
260     },
262     /**
263      * Updates the authentication extension with new parameters, if needed.
264      * @param {Object} data New extension parameters bag.
265      * @private
266      */
267     updateAuthExtension: function(data) {
268       var reasonLabel = $('gaia-signin-reason');
269       if (data.passwordChanged) {
270         reasonLabel.textContent =
271             loadTimeData.getString('signinScreenPasswordChanged');
272         reasonLabel.hidden = false;
273       } else {
274         reasonLabel.hidden = true;
275       }
277       $('createAccount').hidden = !data.createAccount;
278       $('guestSignin').hidden = !data.guestSignin;
279       $('createManagedUserPane').hidden = !data.managedUsersEnabled;
281       $('createManagedUserLinkPlaceholder').hidden =
282           !data.managedUsersCanCreate;
283       $('createManagedUserNoManagerText').hidden = data.managedUsersCanCreate;
284       $('createManagedUserNoManagerText').textContent =
285           data.managedUsersRestrictionReason;
287       // Allow cancellation of screen only when user pods can be displayed.
288       this.cancelAllowed_ = data.isShowUsers && $('pod-row').pods.length;
289       $('login-header-bar').allowCancel = this.cancelAllowed_;
291       // Sign-in right panel is hidden if all of its items are hidden.
292       var noRightPanel = $('gaia-signin-reason').hidden &&
293                          $('createAccount').hidden &&
294                          $('guestSignin').hidden &&
295                          $('createManagedUserPane').hidden;
296       this.classList.toggle('no-right-panel', noRightPanel);
297       if (Oobe.getInstance().currentScreen === this)
298         Oobe.getInstance().updateScreenSize(this);
299     },
301     /**
302      * Sends the authenticated user's e-mail address to the auth extension.
303      * @param {number} attemptToken The opaque token provided to
304      *     onRetrieveAuthenticatedUserEmail_.
305      * @param {string} email The authenticated user's e-mail address.
306      */
307     setAuthenticatedUserEmail: function(attemptToken, email) {
308       this.gaiaAuthHost_.setAuthenticatedUserEmail(attemptToken, email);
309     },
311     /**
312      * Whether the current auth flow is SAML.
313      */
314     isSAML: function() {
315        return this.gaiaAuthHost_.authFlow ==
316            cr.login.GaiaAuthHost.AuthFlow.SAML;
317     },
319     /**
320      * Invoked when the authFlow property is changed no the gaia host.
321      * @param {Event} e Property change event.
322      */
323     onAuthFlowChange_: function(e) {
324       var isSAML = this.isSAML();
326       if (isSAML) {
327         $('saml-notice-message').textContent = loadTimeData.getStringF(
328             'samlNotice',
329             this.gaiaAuthHost_.authDomain);
330       }
332       this.classList.toggle('saml', isSAML);
333       $('saml-notice-container').hidden = !isSAML;
335       if (Oobe.getInstance().currentScreen === this) {
336         Oobe.getInstance().updateScreenSize(this);
337         $('login-header-bar').allowCancel = isSAML || this.cancelAllowed_;
338       }
339     },
341     /**
342      * Invoked when the auth host emits 'ready' event.
343      * @private
344      */
345     onAuthReady_: function() {
346       this.loading = false;
347       this.clearLoadingTimer_();
349       // Show deferred error bubble.
350       if (this.errorBubble_) {
351         this.showErrorBubble(this.errorBubble_[0], this.errorBubble_[1]);
352         this.errorBubble_ = undefined;
353       }
355       chrome.send('loginWebuiReady');
356       chrome.send('loginVisible', ['gaia-signin']);
358       // Warm up the user images screen.
359       Oobe.getInstance().preloadScreen({id: SCREEN_USER_IMAGE_PICKER});
360     },
362     /**
363      * Invoked when the auth host needs the authenticated user's e-mail to be
364      * retrieved.
365      * @param {number} attemptToken Opaque token to be passed to
366      *     setAuthenticatedUserEmail along with the e-mail address.
367      * @private
368      */
369     onRetrieveAuthenticatedUserEmail_: function(attemptToken) {
370       chrome.send('retrieveAuthenticatedUserEmail', [attemptToken]);
371     },
373     /**
374      * Invoked when the auth host needs the user to confirm password.
375      * @private
376      */
377     onAuthConfirmPassword_: function() {
378       this.loading = true;
379       Oobe.getInstance().headerHidden = false;
381       login.ConfirmPasswordScreen.show(
382           this.onConfirmPasswordCollected_.bind(this));
383     },
385     /**
386      * Invoked when the confirm password screen is dismissed.
387      * @private
388      */
389     onConfirmPasswordCollected_: function(password) {
390       this.gaiaAuthHost_.verifyConfirmedPassword(password);
392       // Shows signin UI again without changing states.
393       Oobe.showScreen({id: SCREEN_GAIA_SIGNIN});
394     },
396     /**
397      * Inovked when the auth flow completes but no password is available.
398      * @param {string} email The authenticated user email.
399      */
400     onAuthNoPassword_: function(email) {
401       login.MessageBoxScreen.show(
402           loadTimeData.getString('noPasswordWarningTitle'),
403           loadTimeData.getString('noPasswordWarningBody'),
404           loadTimeData.getString('noPasswordWarningOkButton'),
405           Oobe.showSigninUI);
406     },
408     /**
409      * Invoked when auth is completed successfully.
410      * @param {!Object} credentials Credentials of the completed authentication.
411      * @private
412      */
413     onAuthCompleted_: function(credentials) {
414       if (credentials.useOffline) {
415         this.email = credentials.email;
416         chrome.send('authenticateUser',
417                     [credentials.email, credentials.password]);
418       } else if (credentials.authCode) {
419         chrome.send('completeAuthentication',
420                     [credentials.email,
421                      credentials.password,
422                      credentials.authCode]);
423       } else {
424         chrome.send('completeLogin',
425                     [credentials.email, credentials.password]);
426       }
428       this.loading = true;
429       // Now that we're in logged in state header should be hidden.
430       Oobe.getInstance().headerHidden = true;
431       // Clear any error messages that were shown before login.
432       Oobe.clearErrors();
433     },
435     /**
436      * Clears input fields and switches to input mode.
437      * @param {boolean} takeFocus True to take focus.
438      * @param {boolean} forceOnline Whether online sign-in should be forced.
439      * If |forceOnline| is false previously used sign-in type will be used.
440      */
441     reset: function(takeFocus, forceOnline) {
442       // Reload and show the sign-in UI if needed.
443       if (takeFocus) {
444         if (!forceOnline && this.isLocal) {
445           // Show 'Cancel' button to allow user to return to the main screen
446           // (e.g. this makes sense when connection is back).
447           Oobe.getInstance().headerHidden = false;
448           $('login-header-bar').signinUIState = SIGNIN_UI_STATE.GAIA_SIGNIN;
449           // Do nothing, since offline version is reloaded after an error comes.
450         } else {
451           Oobe.showSigninUI();
452         }
453       }
454     },
456     /**
457      * Reloads extension frame.
458      */
459     doReload: function() {
460       this.error_ = 0;
461       this.gaiaAuthHost_.reload();
462       this.loading = true;
463       this.startLoadingTimer_();
464     },
466     /**
467      * Updates localized content of the screen that is not updated via template.
468      */
469     updateLocalizedContent: function() {
470       $('createAccount').innerHTML = loadTimeData.getStringF(
471           'createAccount',
472           '<a id="createAccountLink" class="signin-link" href="#">',
473           '</a>');
474       $('guestSignin').innerHTML = loadTimeData.getStringF(
475           'guestSignin',
476           '<a id="guestSigninLink" class="signin-link" href="#">',
477           '</a>');
478       $('createManagedUserLinkPlaceholder').innerHTML = loadTimeData.getStringF(
479             'createLocallyManagedUser',
480             '<a id="createManagedUserLink" class="signin-link" href="#">',
481             '</a>');
482       $('createAccountLink').addEventListener('click', function(e) {
483         chrome.send('createAccount');
484         e.preventDefault();
485       });
486       $('guestSigninLink').addEventListener('click', function(e) {
487         chrome.send('launchIncognito');
488         e.preventDefault();
489       });
490       $('createManagedUserLink').addEventListener('click', function(e) {
491         chrome.send('showLocallyManagedUserCreationScreen');
492         e.preventDefault();
493       });
494     },
496     /**
497      * Shows sign-in error bubble.
498      * @param {number} loginAttempts Number of login attemps tried.
499      * @param {HTMLElement} content Content to show in bubble.
500      */
501     showErrorBubble: function(loginAttempts, error) {
502       if (this.isLocal) {
503         $('add-user-button').hidden = true;
504         $('cancel-add-user-button').hidden = false;
505         // Reload offline version of the sign-in extension, which will show
506         // error itself.
507         chrome.send('offlineLogin', [this.email]);
508       } else if (!this.loading) {
509         // We want to show bubble near "Email" field, but we can't calculate
510         // it's position because it is located inside iframe. So we only
511         // can hardcode some constants.
512         /** @const */ var ERROR_BUBBLE_OFFSET = 84;
513         /** @const */ var ERROR_BUBBLE_PADDING = 0;
514         $('bubble').showContentForElement($('login-box'),
515                                           cr.ui.Bubble.Attachment.LEFT,
516                                           error,
517                                           ERROR_BUBBLE_OFFSET,
518                                           ERROR_BUBBLE_PADDING);
519       } else {
520         // Defer the bubble until the frame has been loaded.
521         this.errorBubble_ = [loginAttempts, error];
522       }
523     },
525     /**
526      * Called when user canceled signin.
527      */
528     cancel: function() {
529       if (!this.cancelAllowed_) {
530         // In OOBE signin screen, cancel is not allowed because there is
531         // no other screen to show. If user is in middle of a saml flow,
532         // reset signin screen to get out of the saml flow.
533         if (this.isSAML())
534           Oobe.resetSigninUI(true);
536         return;
537       }
539       $('pod-row').loadLastWallpaper();
540       Oobe.showScreen({id: SCREEN_ACCOUNT_PICKER});
541       Oobe.resetSigninUI(true);
542     },
544     /**
545      * Handler for iframe's error notification coming from the outside.
546      * For more info see C++ class 'SnifferObserver' which calls this method.
547      * @param {number} error Error code.
548      */
549     onFrameError: function(error) {
550       this.error_ = error;
551       chrome.send('frameLoadingCompleted', [this.error_]);
552     },
553   };