Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / login / screen_gaia_signin.js
blob94ca2b18c4ce1f64863f589ec4a3d212a20ce881
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 login.createScreen('GaiaSigninScreen', 'gaia-signin', function() {
10 // Gaia loading time after which error message must be displayed and
11 // lazy portal check should be fired.
12 /** @const */ var GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC = 7;
14 // Maximum Gaia loading time in seconds.
15 /** @const */ var MAX_GAIA_LOADING_TIME_SEC = 60;
17 /** @const */ var HELP_TOPIC_ENTERPRISE_REPORTING = 2535613;
19 return {
20 EXTERNAL_API: [
21 'loadAuthExtension',
22 'updateAuthExtension',
23 'doReload',
24 'onWebviewError',
25 'onFrameError',
26 'updateCancelButtonState'
29 /**
30 * Frame loading error code (0 - no error).
31 * @type {number}
32 * @private
34 error_: 0,
36 /**
37 * Saved gaia auth host load params.
38 * @type {?string}
39 * @private
41 gaiaAuthParams_: null,
43 /**
44 * Whether local version of Gaia page is used.
45 * @type {boolean}
46 * @private
48 isLocal_: false,
50 /**
51 * Whether new Gaia flow is active.
52 * @type {boolean}
54 isNewGaiaFlow: false,
56 /**
57 * Email of the user, which is logging in using offline mode.
58 * @type {string}
60 email: '',
62 /**
63 * Whether consumer management enrollment is in progress.
64 * @type {boolean}
65 * @private
67 isEnrollingConsumerManagement_: false,
69 /**
70 * Timer id of pending load.
71 * @type {number}
72 * @private
74 loadingTimer_: undefined,
76 /**
77 * Whether user can cancel Gaia screen.
78 * @type {boolean}
79 * @private
81 cancelAllowed_: undefined,
83 /**
84 * Whether we should show user pods on the login screen.
85 * @type {boolean}
86 * @private
88 isShowUsers_: undefined,
90 /**
91 * SAML password confirmation attempt count.
92 * @type {number}
94 samlPasswordConfirmAttempt_: 0,
96 /**
97 * Whether we should show webview based signin.
98 * @type {boolean}
99 * @private
101 isWebviewSignin: false,
103 /** @override */
104 decorate: function() {
105 this.isWebviewSignin = loadTimeData.getValue('isWebviewSignin');
106 if (this.isWebviewSignin) {
107 // Replace iframe with webview.
108 var webview = this.ownerDocument.createElement('webview');
109 webview.id = 'signin-frame';
110 webview.name = 'signin-frame';
111 webview.hidden = true;
112 $('signin-frame').parentNode.replaceChild(webview, $('signin-frame'));
113 this.gaiaAuthHost_ = new cr.login.GaiaAuthHost(webview);
114 } else {
115 this.gaiaAuthHost_ = new cr.login.GaiaAuthHost($('signin-frame'));
117 this.gaiaAuthHost_.addEventListener(
118 'ready', this.onAuthReady_.bind(this));
119 this.gaiaAuthHost_.addEventListener(
120 'dialogShown', this.onDialogShown_.bind(this));
121 this.gaiaAuthHost_.addEventListener(
122 'dialogHidden', this.onDialogHidden_.bind(this));
123 this.gaiaAuthHost_.addEventListener(
124 'backButton', this.onBackButton_.bind(this));
125 this.gaiaAuthHost_.confirmPasswordCallback =
126 this.onAuthConfirmPassword_.bind(this);
127 this.gaiaAuthHost_.noPasswordCallback =
128 this.onAuthNoPassword_.bind(this);
129 this.gaiaAuthHost_.insecureContentBlockedCallback =
130 this.onInsecureContentBlocked_.bind(this);
131 this.gaiaAuthHost_.missingGaiaInfoCallback =
132 this.missingGaiaInfo_.bind(this);
133 this.gaiaAuthHost_.samlApiUsedCallback =
134 this.samlApiUsed_.bind(this);
135 this.gaiaAuthHost_.addEventListener('authFlowChange',
136 this.onAuthFlowChange_.bind(this));
137 this.gaiaAuthHost_.addEventListener('authCompleted',
138 this.onAuthCompletedMessage_.bind(this));
139 this.gaiaAuthHost_.addEventListener('loadAbort',
140 this.onLoadAbortMessage_.bind(this));
142 $('enterprise-info-hint-link').addEventListener('click', function(e) {
143 chrome.send('launchHelpApp', [HELP_TOPIC_ENTERPRISE_REPORTING]);
144 e.preventDefault();
147 $('back-button-item').addEventListener('click', function(e) {
148 $('back-button-item').hidden = true;
149 $('signin-frame').back();
150 e.preventDefault();
151 }.bind(this));
152 $('close-button-item').addEventListener('click', function(e) {
153 this.cancel();
154 e.preventDefault();
155 }.bind(this));
157 this.updateLocalizedContent();
161 * Header text of the screen.
162 * @type {string}
164 get header() {
165 return loadTimeData.getString('signinScreenTitle');
169 * Returns true if local version of Gaia is used.
170 * @type {boolean}
172 get isLocal() {
173 return this.isLocal_;
177 * Sets whether local version of Gaia is used.
178 * @param {boolean} value Whether local version of Gaia is used.
180 set isLocal(value) {
181 this.isLocal_ = value;
182 chrome.send('updateOfflineLogin', [value]);
186 * Shows/hides loading UI.
187 * @param {boolean} show True to show loading UI.
188 * @private
190 showLoadingUI_: function(show) {
191 $('gaia-loading').hidden = !show;
192 $('signin-frame').hidden = show;
193 $('signin-right').hidden = show;
194 $('enterprise-info-container').hidden = show;
195 $('gaia-signin-divider').hidden = show;
196 this.classList.toggle('loading', show);
200 * Handler for Gaia loading suspiciously long timeout.
201 * @private
203 onLoadingSuspiciouslyLong_: function() {
204 if (this != Oobe.getInstance().currentScreen)
205 return;
206 chrome.send('showLoadingTimeoutError');
207 this.loadingTimer_ = window.setTimeout(
208 this.onLoadingTimeOut_.bind(this),
209 (MAX_GAIA_LOADING_TIME_SEC - GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC) *
210 1000);
214 * Handler for Gaia loading timeout.
215 * @private
217 onLoadingTimeOut_: function() {
218 this.loadingTimer_ = undefined;
219 chrome.send('showLoadingTimeoutError');
223 * Clears loading timer.
224 * @private
226 clearLoadingTimer_: function() {
227 if (this.loadingTimer_) {
228 window.clearTimeout(this.loadingTimer_);
229 this.loadingTimer_ = undefined;
234 * Sets up loading timer.
235 * @private
237 startLoadingTimer_: function() {
238 this.clearLoadingTimer_();
239 this.loadingTimer_ = window.setTimeout(
240 this.onLoadingSuspiciouslyLong_.bind(this),
241 GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC * 1000);
245 * Whether Gaia is loading.
246 * @type {boolean}
248 get loading() {
249 return !$('gaia-loading').hidden;
251 set loading(loading) {
252 if (loading == this.loading)
253 return;
255 this.showLoadingUI_(loading);
259 * Event handler that is invoked just before the frame is shown.
260 * @param {string} data Screen init payload. Url of auth extension start
261 * page.
263 onBeforeShow: function(data) {
264 chrome.send('loginUIStateChanged', ['gaia-signin', true]);
265 $('login-header-bar').signinUIState =
266 this.isEnrollingConsumerManagement_ ?
267 SIGNIN_UI_STATE.CONSUMER_MANAGEMENT_ENROLLMENT :
268 SIGNIN_UI_STATE.GAIA_SIGNIN;
270 // Ensure that GAIA signin (or loading UI) is actually visible.
271 window.requestAnimationFrame(function() {
272 chrome.send('loginVisible', ['gaia-loading']);
274 $('back-button-item').disabled = false;
275 $('back-button-item').hidden = true;
276 $('close-button-item').disabled = false;
277 this.classList.toggle('loading', this.loading);
279 // Button header is always visible when sign in is presented.
280 // Header is hidden once GAIA reports on successful sign in.
281 Oobe.getInstance().headerHidden = false;
284 onAfterShow: function(data) {
285 if (!this.loading && this.isWebviewSignin)
286 $('signin-frame').focus();
290 * Event handler that is invoked just before the screen is hidden.
292 onBeforeHide: function() {
293 chrome.send('loginUIStateChanged', ['gaia-signin', false]);
294 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN;
298 * Loads the authentication extension into the iframe.
299 * @param {Object} data Extension parameters bag.
300 * @private
302 loadAuthExtension: function(data) {
303 this.isLocal = data.isLocal;
304 this.email = '';
305 this.isNewGaiaFlow = data.useNewGaiaFlow;
307 // Reset SAML
308 this.classList.toggle('full-width', false);
309 this.samlPasswordConfirmAttempt_ = 0;
311 this.updateAuthExtension(data);
313 var params = {};
314 for (var i in cr.login.GaiaAuthHost.SUPPORTED_PARAMS) {
315 var name = cr.login.GaiaAuthHost.SUPPORTED_PARAMS[i];
316 if (data[name])
317 params[name] = data[name];
320 if (data.localizedStrings)
321 params.localizedStrings = data.localizedStrings;
323 if (this.isNewGaiaFlow) {
324 $('inner-container').classList.add('new-gaia-flow');
325 $('progress-dots').hidden = true;
326 if (data.enterpriseDomain)
327 params.enterpriseDomain = data.enterpriseDomain;
328 params.chromeType = data.chromeType;
329 params.isNewGaiaFlowChromeOS = true;
330 $('login-header-bar').showGuestButton = true;
333 if (data.gaiaEndpoint)
334 params.gaiaPath = data.gaiaEndpoint;
336 $('login-header-bar').newGaiaFlow = this.isNewGaiaFlow;
338 if (data.forceReload ||
339 JSON.stringify(this.gaiaAuthParams_) != JSON.stringify(params)) {
340 this.error_ = 0;
342 var authMode = cr.login.GaiaAuthHost.AuthMode.DEFAULT;
343 if (data.useOffline)
344 authMode = cr.login.GaiaAuthHost.AuthMode.OFFLINE;
346 this.gaiaAuthHost_.load(authMode,
347 params,
348 this.onAuthCompleted_.bind(this));
349 this.gaiaAuthParams_ = params;
351 this.loading = true;
352 this.startLoadingTimer_();
353 } else if (this.loading && this.error_) {
354 // An error has occurred, so trying to reload.
355 this.doReload();
360 * Updates the authentication extension with new parameters, if needed.
361 * @param {Object} data New extension parameters bag.
362 * @private
364 updateAuthExtension: function(data) {
365 var reasonLabel = $('gaia-signin-reason');
366 if (data.passwordChanged) {
367 reasonLabel.textContent =
368 loadTimeData.getString('signinScreenPasswordChanged');
369 reasonLabel.hidden = false;
370 } else {
371 reasonLabel.hidden = true;
374 if (this.isNewGaiaFlow) {
375 $('login-header-bar').showCreateSupervisedButton =
376 data.supervisedUsersCanCreate;
377 } else {
378 $('createAccount').hidden = !data.createAccount;
379 $('guestSignin').hidden = !data.guestSignin;
380 $('createSupervisedUserPane').hidden = !data.supervisedUsersEnabled;
382 $('createSupervisedUserLinkPlaceholder').hidden =
383 !data.supervisedUsersCanCreate;
384 $('createSupervisedUserNoManagerText').hidden =
385 data.supervisedUsersCanCreate;
386 $('createSupervisedUserNoManagerText').textContent =
387 data.supervisedUsersRestrictionReason;
390 var isEnrollingConsumerManagement = data.isEnrollingConsumerManagement;
391 $('consumerManagementEnrollment').hidden = !isEnrollingConsumerManagement;
393 this.isShowUsers_ = data.isShowUsers;
394 this.updateCancelButtonState();
396 this.isEnrollingConsumerManagement_ = isEnrollingConsumerManagement;
398 // Sign-in right panel is hidden if all of its items are hidden.
399 var noRightPanel = $('gaia-signin-reason').hidden &&
400 $('createAccount').hidden &&
401 $('guestSignin').hidden &&
402 $('createSupervisedUserPane').hidden &&
403 $('consumerManagementEnrollment').hidden;
404 this.classList.toggle('no-right-panel', noRightPanel);
405 this.classList.toggle('full-width', false);
406 if (Oobe.getInstance().currentScreen === this)
407 Oobe.getInstance().updateScreenSize(this);
411 * Updates [Cancel] button state. Allow cancellation of screen only when
412 * user pods can be displayed.
414 updateCancelButtonState: function() {
415 this.cancelAllowed_ = this.isShowUsers_ && $('pod-row').pods.length;
416 $('login-header-bar').allowCancel = this.cancelAllowed_;
417 if (this.isNewGaiaFlow)
418 $('close-button-item').hidden = !this.cancelAllowed_;
422 * Whether the current auth flow is SAML.
424 isSAML: function() {
425 return this.gaiaAuthHost_.authFlow ==
426 cr.login.GaiaAuthHost.AuthFlow.SAML;
430 * Invoked when the authFlow property is changed no the gaia host.
432 onAuthFlowChange_: function() {
433 var isSAML = this.isSAML();
435 if (isSAML) {
436 $('saml-notice-message').textContent = loadTimeData.getStringF(
437 'samlNotice',
438 this.gaiaAuthHost_.authDomain);
441 this.classList.toggle('no-right-panel', isSAML);
442 this.classList.toggle('full-width', isSAML);
443 $('saml-notice-container').hidden = !isSAML;
445 if (Oobe.getInstance().currentScreen === this) {
446 Oobe.getInstance().updateScreenSize(this);
447 $('login-header-bar').allowCancel = isSAML || this.cancelAllowed_;
448 if (this.isNewGaiaFlow)
449 $('close-button-item').hidden = !(isSAML || this.cancelAllowed_);
454 * Invoked when the auth host emits 'ready' event.
455 * @private
457 onAuthReady_: function() {
458 this.loading = false;
459 this.clearLoadingTimer_();
461 // Show deferred error bubble.
462 if (this.errorBubble_) {
463 this.showErrorBubble(this.errorBubble_[0], this.errorBubble_[1]);
464 this.errorBubble_ = undefined;
467 chrome.send('loginWebuiReady');
468 chrome.send('loginVisible', ['gaia-signin']);
470 // Warm up the user images screen.
471 Oobe.getInstance().preloadScreen({id: SCREEN_USER_IMAGE_PICKER});
475 * Invoked when the auth host emits 'dialogShown' event.
476 * @private
478 onDialogShown_: function() {
479 $('back-button-item').disabled = true;
480 $('close-button-item').disabled = true;
484 * Invoked when the auth host emits 'dialogHidden' event.
485 * @private
487 onDialogHidden_: function() {
488 $('back-button-item').disabled = false;
489 $('close-button-item').disabled = false;
493 * Invoked when the auth host emits 'backButton' event.
494 * @private
496 onBackButton_: function(e) {
497 $('back-button-item').hidden = !e.detail;
501 * Invoked when the user has successfully authenticated via SAML, the
502 * principals API was not used and the auth host needs the user to confirm
503 * the scraped password.
504 * @param {number} passwordCount The number of passwords that were scraped.
505 * @private
507 onAuthConfirmPassword_: function(passwordCount) {
508 this.loading = true;
509 Oobe.getInstance().headerHidden = false;
511 if (this.samlPasswordConfirmAttempt_ == 0)
512 chrome.send('scrapedPasswordCount', [passwordCount]);
514 if (this.samlPasswordConfirmAttempt_ < 2) {
515 login.ConfirmPasswordScreen.show(
516 this.samlPasswordConfirmAttempt_,
517 this.onConfirmPasswordCollected_.bind(this));
518 } else {
519 chrome.send('scrapedPasswordVerificationFailed');
520 this.showFatalAuthError(
521 loadTimeData.getString('fatalErrorMessageVerificationFailed'));
526 * Invoked when the confirm password screen is dismissed.
527 * @private
529 onConfirmPasswordCollected_: function(password) {
530 this.samlPasswordConfirmAttempt_++;
531 this.gaiaAuthHost_.verifyConfirmedPassword(password);
533 // Shows signin UI again without changing states.
534 Oobe.showScreen({id: SCREEN_GAIA_SIGNIN});
538 * Inovked when the user has successfully authenticated via SAML, the
539 * principals API was not used and no passwords could be scraped.
540 * @param {string} email The authenticated user's e-mail.
542 onAuthNoPassword_: function(email) {
543 this.showFatalAuthError(loadTimeData.getString(
544 'fatalErrorMessageNoPassword'));
545 chrome.send('scrapedPasswordCount', [0]);
549 * Invoked when the authentication flow had to be aborted because content
550 * served over an unencrypted connection was detected. Shows a fatal error.
551 * This method is only called on Chrome OS, where the entire authentication
552 * flow is required to be encrypted.
553 * @param {string} url The URL that was blocked.
555 onInsecureContentBlocked_: function(url) {
556 this.showFatalAuthError(loadTimeData.getStringF(
557 'fatalErrorMessageInsecureURL',
558 url));
562 * Shows the fatal auth error.
563 * @param {string} message The error message to show.
565 showFatalAuthError: function(message) {
566 login.FatalErrorScreen.show(message, Oobe.showSigninUI);
570 * Show fatal auth error when information is missing from GAIA.
572 missingGaiaInfo_: function() {
573 this.showFatalAuthError(
574 loadTimeData.getString('fatalErrorMessageNoAccountDetails'));
578 * Record that SAML API was used during sign-in.
580 samlApiUsed_: function() {
581 chrome.send('usingSAMLAPI');
585 * Invoked when auth is completed successfully.
586 * @param {!Object} credentials Credentials of the completed authentication.
587 * @private
589 onAuthCompleted_: function(credentials) {
590 if (credentials.useOffline) {
591 this.email = credentials.email;
592 chrome.send('authenticateUser',
593 [credentials.email,
594 credentials.password]);
595 } else if (credentials.authCode) {
596 if (credentials.hasOwnProperty('authCodeOnly') &&
597 credentials.authCodeOnly) {
598 chrome.send('completeAuthenticationAuthCodeOnly',
599 [credentials.authCode]);
600 } else {
601 chrome.send('completeAuthentication',
602 [credentials.gaiaId,
603 credentials.email,
604 credentials.password,
605 credentials.authCode,
606 credentials.usingSAML]);
608 } else {
609 chrome.send('completeLogin',
610 [credentials.gaiaId,
611 credentials.email,
612 credentials.password,
613 credentials.usingSAML]);
616 this.loading = true;
617 // Now that we're in logged in state header should be hidden.
618 Oobe.getInstance().headerHidden = true;
619 // Clear any error messages that were shown before login.
620 Oobe.clearErrors();
624 * Invoked when onAuthCompleted message received.
625 * @param {!Object} e Payload of the received HTML5 message.
626 * @private
628 onAuthCompletedMessage_: function(e) {
629 this.onAuthCompleted_(e.detail);
633 * Invoked when onLoadAbort message received.
634 * @param {!Object} e Payload of the received HTML5 message.
635 * @private
637 onLoadAbortMessage_: function(e) {
638 this.onWebviewError(e.detail);
642 * Clears input fields and switches to input mode.
643 * @param {boolean} takeFocus True to take focus.
644 * @param {boolean} forceOnline Whether online sign-in should be forced.
645 * If |forceOnline| is false previously used sign-in type will be used.
647 reset: function(takeFocus, forceOnline) {
648 // Reload and show the sign-in UI if needed.
649 if (takeFocus) {
650 if (!forceOnline && this.isLocal) {
651 // Show 'Cancel' button to allow user to return to the main screen
652 // (e.g. this makes sense when connection is back).
653 Oobe.getInstance().headerHidden = false;
654 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.GAIA_SIGNIN;
655 // Do nothing, since offline version is reloaded after an error comes.
656 } else {
657 Oobe.showSigninUI();
663 * Reloads extension frame.
665 doReload: function() {
666 this.error_ = 0;
667 this.gaiaAuthHost_.reload();
668 this.loading = true;
669 this.startLoadingTimer_();
673 * Updates localized content of the screen that is not updated via template.
675 updateLocalizedContent: function() {
676 $('createAccount').innerHTML = loadTimeData.getStringF(
677 'createAccount',
678 '<a id="createAccountLink" class="signin-link" href="#">',
679 '</a>');
680 $('guestSignin').innerHTML = loadTimeData.getStringF(
681 'guestSignin',
682 '<a id="guestSigninLink" class="signin-link" href="#">',
683 '</a>');
684 $('createSupervisedUserLinkPlaceholder').innerHTML =
685 loadTimeData.getStringF(
686 'createSupervisedUser',
687 '<a id="createSupervisedUserLink" class="signin-link" href="#">',
688 '</a>');
689 $('consumerManagementEnrollment').innerHTML = loadTimeData.getString(
690 'consumerManagementEnrollmentSigninMessage');
691 $('createAccountLink').addEventListener('click', function(e) {
692 chrome.send('createAccount');
693 e.preventDefault();
695 $('guestSigninLink').addEventListener('click', function(e) {
696 chrome.send('launchIncognito');
697 e.preventDefault();
699 $('createSupervisedUserLink').addEventListener('click', function(e) {
700 chrome.send('showSupervisedUserCreationScreen');
701 e.preventDefault();
706 * Shows sign-in error bubble.
707 * @param {number} loginAttempts Number of login attemps tried.
708 * @param {HTMLElement} content Content to show in bubble.
710 showErrorBubble: function(loginAttempts, error) {
711 if (this.isLocal) {
712 $('add-user-button').hidden = true;
713 $('cancel-add-user-button').hidden = false;
714 // Reload offline version of the sign-in extension, which will show
715 // error itself.
716 chrome.send('offlineLogin', [this.email]);
717 } else if (!this.loading) {
718 // We want to show bubble near "Email" field, but we can't calculate
719 // it's position because it is located inside iframe. So we only
720 // can hardcode some constants.
721 /** @const */ var ERROR_BUBBLE_OFFSET = 84;
722 /** @const */ var ERROR_BUBBLE_PADDING = 0;
723 $('bubble').showContentForElement($('login-box'),
724 cr.ui.Bubble.Attachment.LEFT,
725 error,
726 ERROR_BUBBLE_OFFSET,
727 ERROR_BUBBLE_PADDING);
728 } else {
729 // Defer the bubble until the frame has been loaded.
730 this.errorBubble_ = [loginAttempts, error];
735 * Called when user canceled signin.
737 cancel: function() {
738 if (!this.cancelAllowed_) {
739 // In OOBE signin screen, cancel is not allowed because there is
740 // no other screen to show. If user is in middle of a saml flow,
741 // reset signin screen to get out of the saml flow.
742 if (this.isSAML())
743 Oobe.resetSigninUI(true);
745 return;
748 $('pod-row').loadLastWallpaper();
749 Oobe.showScreen({id: SCREEN_ACCOUNT_PICKER});
750 Oobe.resetSigninUI(true);
754 * Handler for iframe's error notification coming from the outside.
755 * For more info see C++ class 'WebUILoginView' which calls this method.
756 * @param {number} error Error code.
757 * @param {string} url The URL that failed to load.
759 onFrameError: function(error, url) {
760 this.error_ = error;
761 chrome.send('frameLoadingCompleted', [this.error_]);
765 * Handler for webview error handling.
766 * @param {!Object} data Additional information about error event like:
767 * {string} error Error code such as "ERR_INTERNET_DISCONNECTED".
768 * {string} url The URL that failed to load.
770 onWebviewError: function(data) {
771 chrome.send('webviewLoadAborted', [data.error]);