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.
6 * @fileoverview Oobe signin screen implementation.
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;
24 'updateAuthExtension',
25 'setAuthenticatedUserEmail',
31 * Frame loading error code (0 - no error).
38 * Saved gaia auth host load params.
42 gaiaAuthParams_: null,
45 * Whether local version of Gaia page is used.
52 * Email of the user, which is logging in using offline mode.
58 * Timer id of pending load.
62 loadingTimer_: undefined,
65 * Whether user can cancel Gaia screen.
69 cancelAllowed_: undefined,
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]);
91 this.updateLocalizedContent();
95 * Header text of the screen.
99 return loadTimeData.getString('signinScreenTitle');
103 * Returns true if local version of Gaia is used.
107 return this.isLocal_;
111 * Sets whether local version of Gaia is used.
112 * @param {boolean} value Whether local version of Gaia is used.
115 this.isLocal_ = value;
116 chrome.send('updateOfflineLogin', [value]);
120 * Shows/hides loading UI.
121 * @param {boolean} show True to show loading UI.
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;
133 * Handler for Gaia loading suspiciously long timeout.
136 onLoadingSuspiciouslyLong_: function() {
137 if (this != Oobe.getInstance().currentScreen)
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) *
147 * Handler for Gaia loading timeout.
150 onLoadingTimeOut_: function() {
151 this.loadingTimer_ = undefined;
152 chrome.send('showLoadingTimeoutError');
156 * Clears loading timer.
159 clearLoadingTimer_: function() {
160 if (this.loadingTimer_) {
161 window.clearTimeout(this.loadingTimer_);
162 this.loadingTimer_ = undefined;
167 * Sets up loading timer.
170 startLoadingTimer_: function() {
171 this.clearLoadingTimer_();
172 this.loadingTimer_ = window.setTimeout(
173 this.onLoadingSuspiciouslyLong_.bind(this),
174 GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC * 1000);
178 * Whether Gaia is loading.
182 return !$('gaia-loading').hidden;
184 set loading(loading) {
185 if (loading == this.loading)
188 this.showLoadingUI_(loading);
192 * Event handler that is invoked just before the frame is shown.
193 * @param {string} data Screen init payload. Url of auth extension start
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']);
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;
215 * Event handler that is invoked just before the screen is hidden.
217 onBeforeHide: function() {
218 chrome.send('loginUIStateChanged', ['gaia-signin', false]);
219 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN;
223 * Loads the authentication extension into the iframe.
224 * @param {Object} data Extension parameters bag.
227 loadAuthExtension: function(data) {
228 this.isLocal = data.isLocal;
230 this.classList.toggle('saml', false);
232 this.updateAuthExtension(data);
235 for (var i in cr.login.GaiaAuthHost.SUPPORTED_PARAMS) {
236 var name = cr.login.GaiaAuthHost.SUPPORTED_PARAMS[i];
238 params[name] = data[name];
241 if (data.localizedStrings)
242 params.localizedStrings = data.localizedStrings;
244 if (data.forceReload ||
245 JSON.stringify(this.gaiaAuthParams_) != JSON.stringify(params)) {
247 this.gaiaAuthHost_.load(data.useOffline ?
248 cr.login.GaiaAuthHost.AuthMode.OFFLINE :
249 cr.login.GaiaAuthHost.AuthMode.DEFAULT,
251 this.onAuthCompleted_.bind(this));
252 this.gaiaAuthParams_ = params;
255 this.startLoadingTimer_();
256 } else if (this.loading && this.error_) {
257 // An error has occurred, so trying to reload.
263 * Updates the authentication extension with new parameters, if needed.
264 * @param {Object} data New extension parameters bag.
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;
274 reasonLabel.hidden = true;
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);
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.
307 setAuthenticatedUserEmail: function(attemptToken, email) {
308 this.gaiaAuthHost_.setAuthenticatedUserEmail(attemptToken, email);
312 * Whether the current auth flow is SAML.
315 return this.gaiaAuthHost_.authFlow ==
316 cr.login.GaiaAuthHost.AuthFlow.SAML;
320 * Invoked when the authFlow property is changed no the gaia host.
321 * @param {Event} e Property change event.
323 onAuthFlowChange_: function(e) {
324 var isSAML = this.isSAML();
327 $('saml-notice-message').textContent = loadTimeData.getStringF(
329 this.gaiaAuthHost_.authDomain);
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_;
342 * Invoked when the auth host emits 'ready' event.
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;
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});
363 * Invoked when the auth host needs the authenticated user's e-mail to be
365 * @param {number} attemptToken Opaque token to be passed to
366 * setAuthenticatedUserEmail along with the e-mail address.
369 onRetrieveAuthenticatedUserEmail_: function(attemptToken) {
370 chrome.send('retrieveAuthenticatedUserEmail', [attemptToken]);
374 * Invoked when the auth host needs the user to confirm password.
377 onAuthConfirmPassword_: function() {
379 Oobe.getInstance().headerHidden = false;
381 login.ConfirmPasswordScreen.show(
382 this.onConfirmPasswordCollected_.bind(this));
386 * Invoked when the confirm password screen is dismissed.
389 onConfirmPasswordCollected_: function(password) {
390 this.gaiaAuthHost_.verifyConfirmedPassword(password);
392 // Shows signin UI again without changing states.
393 Oobe.showScreen({id: SCREEN_GAIA_SIGNIN});
397 * Inovked when the auth flow completes but no password is available.
398 * @param {string} email The authenticated user email.
400 onAuthNoPassword_: function(email) {
401 login.MessageBoxScreen.show(
402 loadTimeData.getString('noPasswordWarningTitle'),
403 loadTimeData.getString('noPasswordWarningBody'),
404 loadTimeData.getString('noPasswordWarningOkButton'),
409 * Invoked when auth is completed successfully.
410 * @param {!Object} credentials Credentials of the completed authentication.
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',
421 credentials.password,
422 credentials.authCode]);
424 chrome.send('completeLogin',
425 [credentials.email, credentials.password]);
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.
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.
441 reset: function(takeFocus, forceOnline) {
442 // Reload and show the sign-in UI if needed.
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.
457 * Reloads extension frame.
459 doReload: function() {
461 this.gaiaAuthHost_.reload();
463 this.startLoadingTimer_();
467 * Updates localized content of the screen that is not updated via template.
469 updateLocalizedContent: function() {
470 $('createAccount').innerHTML = loadTimeData.getStringF(
472 '<a id="createAccountLink" class="signin-link" href="#">',
474 $('guestSignin').innerHTML = loadTimeData.getStringF(
476 '<a id="guestSigninLink" class="signin-link" href="#">',
478 $('createManagedUserLinkPlaceholder').innerHTML = loadTimeData.getStringF(
479 'createLocallyManagedUser',
480 '<a id="createManagedUserLink" class="signin-link" href="#">',
482 $('createAccountLink').addEventListener('click', function(e) {
483 chrome.send('createAccount');
486 $('guestSigninLink').addEventListener('click', function(e) {
487 chrome.send('launchIncognito');
490 $('createManagedUserLink').addEventListener('click', function(e) {
491 chrome.send('showLocallyManagedUserCreationScreen');
497 * Shows sign-in error bubble.
498 * @param {number} loginAttempts Number of login attemps tried.
499 * @param {HTMLElement} content Content to show in bubble.
501 showErrorBubble: function(loginAttempts, error) {
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
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,
518 ERROR_BUBBLE_PADDING);
520 // Defer the bubble until the frame has been loaded.
521 this.errorBubble_ = [loginAttempts, error];
526 * Called when user canceled signin.
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.
534 Oobe.resetSigninUI(true);
539 $('pod-row').loadLastWallpaper();
540 Oobe.showScreen({id: SCREEN_ACCOUNT_PICKER});
541 Oobe.resetSigninUI(true);
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.
549 onFrameError: function(error) {
551 chrome.send('frameLoadingCompleted', [this.error_]);