Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / login / screen_gaia_signin.js
blob9650c9058a41026a3b3b130577ed6f48b5d7a11a
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 // GAIA animation guard timer. Started when GAIA page is loaded
15 // (Authenticator 'ready' event) and is intended to guard against edge cases
16 // when 'showView' message is not generated/received.
17 /** @const */ var GAIA_ANIMATION_GUARD_MILLISEC = 300;
19 // Maximum Gaia loading time in seconds.
20 /** @const */ var MAX_GAIA_LOADING_TIME_SEC = 60;
22 /** @const */ var HELP_TOPIC_ENTERPRISE_REPORTING = 2535613;
24 // The help topic regarding user not being in the whitelist.
25 /** @const */ var HELP_CANT_ACCESS_ACCOUNT = 188036;
27 // Amount of time the user has to be idle for before showing the online login
28 // page.
29 /** @const */ var IDLE_TIME_UNTIL_EXIT_OFFLINE_IN_MILLISECONDS = 180 * 1000;
31 // Approximate amount of time between checks to see if we should go to the
32 // online login page when we're in the offline login page and the device is
33 // online.
34 /** @const */ var IDLE_TIME_CHECK_FREQUENCY = 5 * 1000;
36 return {
37 EXTERNAL_API: [
38 'loadAuthExtension',
39 'updateAuthExtension',
40 'doReload',
41 'monitorOfflineIdle',
42 'onWebviewError',
43 'onFrameError',
44 'updateCancelButtonState',
45 'showWhitelistCheckFailedError',
48 /**
49 * Frame loading error code (0 - no error).
50 * @type {number}
51 * @private
53 error_: 0,
55 /**
56 * Saved gaia auth host load params.
57 * @type {?string}
58 * @private
60 gaiaAuthParams_: null,
62 /**
63 * Whether local version of Gaia page is used.
64 * @type {boolean}
65 * @private
67 isLocal_: false,
69 /**
70 * Whether new Gaia flow is active.
71 * @type {boolean}
73 isNewGaiaFlow: false,
75 /**
76 * Email of the user, which is logging in using offline mode.
77 * @type {string}
79 email: '',
81 /**
82 * Whether consumer management enrollment is in progress.
83 * @type {boolean}
84 * @private
86 isEnrollingConsumerManagement_: false,
88 /**
89 * Timer id of pending load.
90 * @type {number}
91 * @private
93 loadingTimer_: undefined,
95 /**
96 * Timer id of a guard timer that is fired in case 'showView' message
97 * is not received from GAIA.
98 * @type {number}
99 * @private
101 loadAnimationGuardTimer_: undefined,
104 * Whether we've processed 'showView' message - either from GAIA or from
105 * guard timer.
106 * @type {boolean}
107 * @private
109 showViewProcessed_: undefined,
112 * Whether user can cancel Gaia screen.
113 * @type {boolean}
114 * @private
116 cancelAllowed_: undefined,
119 * Whether we should show user pods on the login screen.
120 * @type {boolean}
121 * @private
123 isShowUsers_: undefined,
126 * SAML password confirmation attempt count.
127 * @type {number}
129 samlPasswordConfirmAttempt_: 0,
132 * Do we currently have a setTimeout task running that tries to bring us
133 * back to the online login page after the user has idled for awhile? If so,
134 * then this id will be non-negative.
136 tryToGoToOnlineLoginPageCallbackId_: -1,
139 * The most recent period of time that the user has interacted. This is
140 * only updated when the offline page is active and the device is online.
142 mostRecentUserActivity_: Date.now(),
145 * Whether we should show webview based signin.
146 * @type {boolean}
147 * @private
149 isWebviewSignin: false,
151 /** @override */
152 decorate: function() {
153 this.isWebviewSignin = loadTimeData.getValue('isWebviewSignin');
154 if (this.isWebviewSignin) {
155 // Replace iframe with webview.
156 var webview = this.ownerDocument.createElement('webview');
157 webview.id = 'signin-frame';
158 webview.name = 'signin-frame';
159 webview.hidden = true;
160 $('signin-frame').parentNode.replaceChild(webview, $('signin-frame'));
161 this.gaiaAuthHost_ = new cr.login.GaiaAuthHost(webview);
163 $('offline-gaia').addEventListener('authCompleted',
164 this.onAuthCompletedMessage_.bind(this));
165 } else {
166 this.gaiaAuthHost_ = new cr.login.GaiaAuthHost($('signin-frame'));
168 this.gaiaAuthHost_.addEventListener(
169 'ready', this.onAuthReady_.bind(this));
170 this.gaiaAuthHost_.addEventListener(
171 'dialogShown', this.onDialogShown_.bind(this));
172 this.gaiaAuthHost_.addEventListener(
173 'dialogHidden', this.onDialogHidden_.bind(this));
174 this.gaiaAuthHost_.addEventListener(
175 'backButton', this.onBackButton_.bind(this));
176 this.gaiaAuthHost_.addEventListener(
177 'showView', this.onShowView_.bind(this));
178 this.gaiaAuthHost_.confirmPasswordCallback =
179 this.onAuthConfirmPassword_.bind(this);
180 this.gaiaAuthHost_.noPasswordCallback =
181 this.onAuthNoPassword_.bind(this);
182 this.gaiaAuthHost_.insecureContentBlockedCallback =
183 this.onInsecureContentBlocked_.bind(this);
184 this.gaiaAuthHost_.missingGaiaInfoCallback =
185 this.missingGaiaInfo_.bind(this);
186 this.gaiaAuthHost_.samlApiUsedCallback =
187 this.samlApiUsed_.bind(this);
188 this.gaiaAuthHost_.addEventListener('authDomainChange',
189 this.onAuthDomainChange_.bind(this));
190 this.gaiaAuthHost_.addEventListener('authFlowChange',
191 this.onAuthFlowChange_.bind(this));
192 this.gaiaAuthHost_.addEventListener('authCompleted',
193 this.onAuthCompletedMessage_.bind(this));
194 this.gaiaAuthHost_.addEventListener('loadAbort',
195 this.onLoadAbortMessage_.bind(this));
196 this.gaiaAuthHost_.addEventListener(
197 'identifierEntered', this.onIdentifierEnteredMessage_.bind(this));
199 $('enterprise-info-hint-link').addEventListener('click', function(e) {
200 chrome.send('launchHelpApp', [HELP_TOPIC_ENTERPRISE_REPORTING]);
201 e.preventDefault();
204 $('back-button-item').addEventListener('click', function(e) {
205 $('back-button-item').hidden = true;
206 $('signin-frame').back();
207 e.preventDefault();
210 $('close-button-item').addEventListener('click', function(e) {
211 this.cancel();
212 e.preventDefault();
213 }.bind(this));
215 $('gaia-whitelist-error').addEventListener('buttonclick', function() {
216 this.showWhitelistCheckFailedError(false);
217 }.bind(this));
219 $('gaia-whitelist-error').addEventListener('linkclick', function() {
220 chrome.send('launchHelpApp', [HELP_CANT_ACCESS_ACCOUNT]);
223 this.updateLocalizedContent();
227 * Header text of the screen.
228 * @type {string}
230 get header() {
231 return loadTimeData.getString('signinScreenTitle');
235 * Returns true if local version of Gaia is used.
236 * @type {boolean}
238 get isLocal() {
239 return this.isLocal_;
243 * Sets whether local version of Gaia is used.
244 * @param {boolean} value Whether local version of Gaia is used.
246 set isLocal(value) {
247 this.isLocal_ = value;
248 if (this.isNewGaiaFlow) {
249 $('signin-frame').hidden = this.isLocal_;
250 $('offline-gaia').hidden = !this.isLocal_;
252 chrome.send('updateOfflineLogin', [value]);
256 * This enables or disables trying to go back to the online login page
257 * after the user is idle for a few minutes, assuming that we're currently
258 * in the offline one. This is only applicable when the offline page is
259 * currently active. It is intended that when the device goes online, this
260 * gets called with true; when it goes offline, this gets called with
261 * false.
263 monitorOfflineIdle: function(shouldMonitor) {
264 var ACTIVITY_EVENTS = ['click', 'mousemove', 'keypress'];
265 var self = this;
267 // updateActivityTime_ is used as a callback for addEventListener, so we
268 // need the exact reference for removeEventListener. Because the callback
269 // needs to access the |this| as scoped inside of this function, we create
270 // a closure that uses the appropriate |this|.
272 // Unfourtantely, we cannot define this function inside of the JSON object
273 // as then we have to no way to create to capture the correct |this|
274 // reference. We define it here instead.
275 if (!self.updateActivityTime_) {
276 self.updateActivityTime_ = function() {
277 self.mostRecentUserActivity_ = Date.now();
281 // Begin monitoring.
282 if (shouldMonitor) {
283 // If we're not using the offline login page or we're already
284 // monitoring, then we don't need to do anything.
285 if (self.isLocal === false ||
286 self.tryToGoToOnlineLoginPageCallbackId_ !== -1)
287 return;
289 self.mostRecentUserActivity_ = Date.now();
290 ACTIVITY_EVENTS.forEach(function(event) {
291 document.addEventListener(event, self.updateActivityTime_);
294 self.tryToGoToOnlineLoginPageCallbackId_ = setInterval(function() {
295 // If we're not in the offline page or the signin page, then we want
296 // to terminate monitoring.
297 if (self.isLocal === false ||
298 Oobe.getInstance().currentScreen.id != 'gaia-signin') {
299 self.monitorOfflineIdle(false);
300 return;
303 var idleDuration = Date.now() - self.mostRecentUserActivity_;
304 if (idleDuration > IDLE_TIME_UNTIL_EXIT_OFFLINE_IN_MILLISECONDS) {
305 self.isLocal = false;
306 self.monitorOfflineIdle(false);
308 }, IDLE_TIME_CHECK_FREQUENCY);
311 // Stop monitoring.
312 else {
313 // We're not monitoring, so we don't need to do anything.
314 if (self.tryToGoToOnlineLoginPageCallbackId_ === -1)
315 return;
317 ACTIVITY_EVENTS.forEach(function(event) {
318 document.removeEventListener(event, self.updateActivityTime_);
320 clearInterval(self.tryToGoToOnlineLoginPageCallbackId_);
321 self.tryToGoToOnlineLoginPageCallbackId_ = -1;
326 * Shows/hides loading UI.
327 * @param {boolean} show True to show loading UI.
328 * @private
330 showLoadingUI_: function(show) {
331 $('gaia-loading').hidden = !show;
332 if (this.isNewGaiaFlow && this.isLocal) {
333 $('offline-gaia').hidden = show;
334 } else {
335 $('signin-frame').hidden = show;
337 $('signin-right').hidden = show;
338 $('enterprise-info-container').hidden = show;
339 $('gaia-signin-divider').hidden = show;
340 this.classList.toggle('loading', show);
341 $('signin-frame').classList.remove('show');
342 if (!show)
343 this.classList.remove('auth-completed');
347 * Handler for Gaia loading suspiciously long timeout.
348 * @private
350 onLoadingSuspiciouslyLong_: function() {
351 if (this != Oobe.getInstance().currentScreen)
352 return;
353 chrome.send('showLoadingTimeoutError');
354 this.loadingTimer_ = setTimeout(
355 this.onLoadingTimeOut_.bind(this),
356 (MAX_GAIA_LOADING_TIME_SEC - GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC) *
357 1000);
361 * Handler for Gaia loading timeout.
362 * @private
364 onLoadingTimeOut_: function() {
365 this.loadingTimer_ = undefined;
366 chrome.send('showLoadingTimeoutError');
370 * Clears loading timer.
371 * @private
373 clearLoadingTimer_: function() {
374 if (this.loadingTimer_) {
375 clearTimeout(this.loadingTimer_);
376 this.loadingTimer_ = undefined;
381 * Sets up loading timer.
382 * @private
384 startLoadingTimer_: function() {
385 this.clearLoadingTimer_();
386 this.loadingTimer_ = setTimeout(
387 this.onLoadingSuspiciouslyLong_.bind(this),
388 GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC * 1000);
392 * Handler for GAIA animation guard timer.
393 * @private
395 onLoadAnimationGuardTimer_: function() {
396 this.loadAnimationGuardTimer_ = undefined;
397 this.onShowView_();
401 * Clears GAIA animation guard timer.
402 * @private
404 clearLoadAnimationGuardTimer_: function() {
405 if (this.loadAnimationGuardTimer_) {
406 clearTimeout(this.loadAnimationGuardTimer_);
407 this.loadAnimationGuardTimer_ = undefined;
412 * Sets up GAIA animation guard timer.
413 * @private
415 startLoadAnimationGuardTimer_: function() {
416 this.clearLoadAnimationGuardTimer_();
417 this.loadAnimationGuardTimer_ = setTimeout(
418 this.onLoadAnimationGuardTimer_.bind(this),
419 GAIA_ANIMATION_GUARD_MILLISEC);
423 * Whether Gaia is loading.
424 * @type {boolean}
426 get loading() {
427 return !$('gaia-loading').hidden;
429 set loading(loading) {
430 if (loading == this.loading)
431 return;
433 this.showLoadingUI_(loading);
437 * Event handler that is invoked just before the frame is shown.
438 * @param {string} data Screen init payload. Url of auth extension start
439 * page.
441 onBeforeShow: function(data) {
442 chrome.send('loginUIStateChanged', ['gaia-signin', true]);
443 $('login-header-bar').signinUIState =
444 this.isEnrollingConsumerManagement_ ?
445 SIGNIN_UI_STATE.CONSUMER_MANAGEMENT_ENROLLMENT :
446 SIGNIN_UI_STATE.GAIA_SIGNIN;
448 // Ensure that GAIA signin (or loading UI) is actually visible.
449 window.requestAnimationFrame(function() {
450 chrome.send('loginVisible', ['gaia-loading']);
452 $('back-button-item').disabled = false;
453 $('back-button-item').hidden = true;
454 $('close-button-item').disabled = false;
455 this.classList.toggle('loading', this.loading);
457 // Button header is always visible when sign in is presented.
458 // Header is hidden once GAIA reports on successful sign in.
459 Oobe.getInstance().headerHidden = false;
462 onAfterShow: function(data) {
463 if (!this.loading && this.isWebviewSignin) {
464 if (this.isLocal)
465 $('offline-gaia').focus();
466 else
467 $('signin-frame').focus();
472 * Event handler that is invoked just before the screen is hidden.
474 onBeforeHide: function() {
475 chrome.send('loginUIStateChanged', ['gaia-signin', false]);
476 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN;
480 * Loads the authentication extension into the iframe.
481 * @param {Object} data Extension parameters bag.
482 * @private
484 loadAuthExtension: function(data) {
485 this.isNewGaiaFlow = data.useNewGaiaFlow;
486 this.isLocal = data.isLocal;
487 this.email = '';
489 // Reset SAML
490 this.classList.toggle('full-width', false);
491 this.samlPasswordConfirmAttempt_ = 0;
493 this.updateAuthExtension(data);
495 var params = {};
496 for (var i in cr.login.GaiaAuthHost.SUPPORTED_PARAMS) {
497 var name = cr.login.GaiaAuthHost.SUPPORTED_PARAMS[i];
498 if (data[name])
499 params[name] = data[name];
502 if (data.localizedStrings)
503 params.localizedStrings = data.localizedStrings;
505 if (this.isNewGaiaFlow) {
506 $('inner-container').classList.add('new-gaia-flow');
507 params.chromeType = data.chromeType;
508 params.isNewGaiaFlowChromeOS = true;
511 if (data.gaiaEndpoint)
512 params.gaiaPath = data.gaiaEndpoint;
514 $('login-header-bar').newGaiaFlow = this.isNewGaiaFlow;
516 // Screen size could have been changed because of 'new-gaia-flow' or
517 // 'full-width' classes.
518 if (Oobe.getInstance().currentScreen === this)
519 Oobe.getInstance().updateScreenSize(this);
521 if (data.forceReload ||
522 JSON.stringify(this.gaiaAuthParams_) != JSON.stringify(params)) {
523 this.error_ = 0;
525 var authMode = cr.login.GaiaAuthHost.AuthMode.DEFAULT;
526 if (data.useOffline)
527 authMode = cr.login.GaiaAuthHost.AuthMode.OFFLINE;
529 this.gaiaAuthParams_ = params;
530 this.loading = true;
531 this.startLoadingTimer_();
533 if (this.isLocal && this.isNewGaiaFlow) {
534 this.loadOffline(params);
535 this.onAuthReady_();
536 } else {
537 this.gaiaAuthHost_.load(authMode,
538 params,
539 this.onAuthCompleted_.bind(this));
541 } else if (this.loading && this.error_) {
542 // An error has occurred, so trying to reload.
543 this.doReload();
548 * Updates the authentication extension with new parameters, if needed.
549 * @param {Object} data New extension parameters bag.
550 * @private
552 updateAuthExtension: function(data) {
553 var reasonLabel = $('gaia-signin-reason');
554 if (data.passwordChanged) {
555 reasonLabel.textContent =
556 loadTimeData.getString('signinScreenPasswordChanged');
557 reasonLabel.hidden = false;
558 } else {
559 reasonLabel.hidden = true;
562 if (this.isNewGaiaFlow) {
563 $('login-header-bar').showCreateSupervisedButton =
564 data.supervisedUsersEnabled && data.supervisedUsersCanCreate;
565 $('login-header-bar').showGuestButton = data.guestSignin;
566 } else {
567 $('createAccount').hidden = !data.createAccount;
568 $('guestSignin').hidden = !data.guestSignin;
569 $('createSupervisedUserPane').hidden = !data.supervisedUsersEnabled;
571 $('createSupervisedUserLinkPlaceholder').hidden =
572 !data.supervisedUsersCanCreate;
573 $('createSupervisedUserNoManagerText').hidden =
574 data.supervisedUsersCanCreate;
575 $('createSupervisedUserNoManagerText').textContent =
576 data.supervisedUsersRestrictionReason;
579 var isEnrollingConsumerManagement = data.isEnrollingConsumerManagement;
580 $('consumerManagementEnrollment').hidden = !isEnrollingConsumerManagement;
582 this.isShowUsers_ = data.isShowUsers;
583 this.updateCancelButtonState();
585 this.isEnrollingConsumerManagement_ = isEnrollingConsumerManagement;
587 // Sign-in right panel is hidden if all of its items are hidden.
588 var noRightPanel = $('gaia-signin-reason').hidden &&
589 $('createAccount').hidden &&
590 $('guestSignin').hidden &&
591 $('createSupervisedUserPane').hidden &&
592 $('consumerManagementEnrollment').hidden;
593 this.classList.toggle('no-right-panel', noRightPanel);
594 this.classList.toggle('full-width', false);
595 if (Oobe.getInstance().currentScreen === this)
596 Oobe.getInstance().updateScreenSize(this);
600 * Updates [Cancel] button state. Allow cancellation of screen only when
601 * user pods can be displayed.
603 updateCancelButtonState: function() {
604 this.cancelAllowed_ = this.isLocal ||
605 (this.isShowUsers_ && $('pod-row').pods.length);
606 $('login-header-bar').allowCancel = this.cancelAllowed_;
607 if (this.isNewGaiaFlow)
608 $('close-button-item').hidden = !this.cancelAllowed_;
612 * Whether the current auth flow is SAML.
614 isSAML: function() {
615 return this.gaiaAuthHost_.authFlow ==
616 cr.login.GaiaAuthHost.AuthFlow.SAML;
620 * Invoked when the authDomain property is changed on the GAIA host.
622 onAuthDomainChange_: function() {
623 $('saml-notice-message').textContent = loadTimeData.getStringF(
624 'samlNotice',
625 this.gaiaAuthHost_.authDomain);
629 * Invoked when the authFlow property is changed on the GAIA host.
630 * @param {Event} e Property change event.
632 onAuthFlowChange_: function(e) {
633 var isSAML = this.isSAML();
635 this.classList.toggle('no-right-panel', isSAML);
636 this.classList.toggle('full-width', isSAML);
637 $('saml-notice-container').hidden = !isSAML;
639 if (Oobe.getInstance().currentScreen === this) {
640 Oobe.getInstance().updateScreenSize(this);
641 $('login-header-bar').allowCancel = isSAML || this.cancelAllowed_;
642 if (this.isNewGaiaFlow)
643 $('close-button-item').hidden = !(isSAML || this.cancelAllowed_);
648 * Invoked when the auth host emits 'ready' event.
649 * @private
651 onAuthReady_: function() {
652 showViewProcessed_ = false;
653 if (this.isNewGaiaFlow)
654 this.startLoadAnimationGuardTimer_();
656 this.clearLoadingTimer_();
657 this.loading = false;
659 if (!this.isNewGaiaFlow)
660 this.onLoginUIVisible_();
662 // Warm up the user images screen.
663 Oobe.getInstance().preloadScreen({id: SCREEN_USER_IMAGE_PICKER});
667 * Invoked when the auth host emits 'dialogShown' event.
668 * @private
670 onDialogShown_: function() {
671 $('back-button-item').disabled = true;
672 $('close-button-item').disabled = true;
676 * Invoked when the auth host emits 'dialogHidden' event.
677 * @private
679 onDialogHidden_: function() {
680 $('back-button-item').disabled = false;
681 $('close-button-item').disabled = false;
685 * Invoked when the auth host emits 'backButton' event.
686 * @private
688 onBackButton_: function(e) {
689 $('back-button-item').hidden = !e.detail;
690 $('login-header-bar').updateUI_();
691 $('signin-frame').focus();
695 * Invoked when the auth host emits 'showView' event or when corresponding
696 * guard time fires.
697 * @private
699 onShowView_: function(e) {
700 if (showViewProcessed_)
701 return;
703 showViewProcessed_ = true;
704 this.clearLoadAnimationGuardTimer_();
705 $('signin-frame').classList.add('show');
706 this.onLoginUIVisible_();
710 * Called when UI is shown.
711 * @private
713 onLoginUIVisible_: function() {
714 // Show deferred error bubble.
715 if (this.errorBubble_) {
716 this.showErrorBubble(this.errorBubble_[0], this.errorBubble_[1]);
717 this.errorBubble_ = undefined;
720 chrome.send('loginWebuiReady');
721 chrome.send('loginVisible', ['gaia-signin']);
725 * Invoked when the user has successfully authenticated via SAML, the
726 * principals API was not used and the auth host needs the user to confirm
727 * the scraped password.
728 * @param {string} email The authenticated user's e-mail.
729 * @param {number} passwordCount The number of passwords that were scraped.
730 * @private
732 onAuthConfirmPassword_: function(email, passwordCount) {
733 this.loading = true;
734 Oobe.getInstance().headerHidden = false;
736 if (this.samlPasswordConfirmAttempt_ == 0)
737 chrome.send('scrapedPasswordCount', [passwordCount]);
739 if (this.samlPasswordConfirmAttempt_ < 2) {
740 login.ConfirmPasswordScreen.show(
741 email,
742 this.samlPasswordConfirmAttempt_,
743 this.onConfirmPasswordCollected_.bind(this));
744 } else {
745 chrome.send('scrapedPasswordVerificationFailed');
746 this.showFatalAuthError(
747 loadTimeData.getString('fatalErrorMessageVerificationFailed'),
748 loadTimeData.getString('fatalErrorTryAgainButton'));
750 if (this.isNewGaiaFlow) {
751 this.classList.toggle('no-right-panel', false);
752 this.classList.toggle('full-width', false);
757 * Invoked when the confirm password screen is dismissed.
758 * @private
760 onConfirmPasswordCollected_: function(password) {
761 this.samlPasswordConfirmAttempt_++;
762 this.gaiaAuthHost_.verifyConfirmedPassword(password);
764 // Shows signin UI again without changing states.
765 Oobe.showScreen({id: SCREEN_GAIA_SIGNIN});
769 * Inovked when the user has successfully authenticated via SAML, the
770 * principals API was not used and no passwords could be scraped.
771 * @param {string} email The authenticated user's e-mail.
773 onAuthNoPassword_: function(email) {
774 this.showFatalAuthError(
775 loadTimeData.getString('fatalErrorMessageNoPassword'),
776 loadTimeData.getString('fatalErrorTryAgainButton'));
777 chrome.send('scrapedPasswordCount', [0]);
781 * Invoked when the authentication flow had to be aborted because content
782 * served over an unencrypted connection was detected. Shows a fatal error.
783 * This method is only called on Chrome OS, where the entire authentication
784 * flow is required to be encrypted.
785 * @param {string} url The URL that was blocked.
787 onInsecureContentBlocked_: function(url) {
788 this.showFatalAuthError(
789 loadTimeData.getStringF('fatalErrorMessageInsecureURL', url),
790 loadTimeData.getString('fatalErrorDoneButton'));
794 * Shows the fatal auth error.
795 * @param {string} message The error message to show.
796 * @param {string} buttonLabel The label to display on dismiss button.
798 showFatalAuthError: function(message, buttonLabel) {
799 login.FatalErrorScreen.show(message, buttonLabel, Oobe.showSigninUI);
803 * Show fatal auth error when information is missing from GAIA.
805 missingGaiaInfo_: function() {
806 this.showFatalAuthError(
807 loadTimeData.getString('fatalErrorMessageNoAccountDetails'),
808 loadTimeData.getString('fatalErrorTryAgainButton'));
812 * Record that SAML API was used during sign-in.
814 samlApiUsed_: function() {
815 chrome.send('usingSAMLAPI');
819 * Invoked when auth is completed successfully.
820 * @param {!Object} credentials Credentials of the completed authentication.
821 * @private
823 onAuthCompleted_: function(credentials) {
824 if (credentials.useOffline) {
825 this.email = credentials.email;
826 chrome.send('authenticateUser',
827 [credentials.email,
828 credentials.password]);
829 } else if (credentials.authCode) {
830 if (credentials.hasOwnProperty('authCodeOnly') &&
831 credentials.authCodeOnly) {
832 chrome.send('completeAuthenticationAuthCodeOnly',
833 [credentials.authCode]);
834 } else {
835 chrome.send('completeAuthentication', [
836 credentials.gaiaId,
837 credentials.email,
838 credentials.password,
839 credentials.authCode,
840 credentials.usingSAML,
841 credentials.gapsCookie
844 } else {
845 chrome.send('completeLogin',
846 [credentials.gaiaId,
847 credentials.email,
848 credentials.password,
849 credentials.usingSAML]);
852 this.loading = true;
853 this.classList.add('auth-completed');
854 // Now that we're in logged in state header should be hidden.
855 Oobe.getInstance().headerHidden = true;
856 // Clear any error messages that were shown before login.
857 Oobe.clearErrors();
861 * Invoked when onAuthCompleted message received.
862 * @param {!Object} e Payload of the received HTML5 message.
863 * @private
865 onAuthCompletedMessage_: function(e) {
866 this.onAuthCompleted_(e.detail);
870 * Invoked when onLoadAbort message received.
871 * @param {!Object} e Payload of the received HTML5 message.
872 * @private
874 onLoadAbortMessage_: function(e) {
875 this.onWebviewError(e.detail);
879 * Invoked when identifierEntered message received.
880 * @param {!Object} e Payload of the received HTML5 message.
881 * @private
883 onIdentifierEnteredMessage_: function(e) {
884 this.onIdentifierEntered(e.detail);
888 * Clears input fields and switches to input mode.
889 * @param {boolean} takeFocus True to take focus.
890 * @param {boolean} forceOnline Whether online sign-in should be forced.
891 * If |forceOnline| is false previously used sign-in type will be used.
893 reset: function(takeFocus, forceOnline) {
894 // Reload and show the sign-in UI if needed.
895 if (takeFocus) {
896 if (!forceOnline && this.isLocal) {
897 // Show 'Cancel' button to allow user to return to the main screen
898 // (e.g. this makes sense when connection is back).
899 Oobe.getInstance().headerHidden = false;
900 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.GAIA_SIGNIN;
901 // Do nothing, since offline version is reloaded after an error comes.
902 } else {
903 Oobe.showSigninUI();
909 * Reloads extension frame.
911 doReload: function() {
912 if (this.isLocal)
913 return;
914 this.error_ = 0;
915 this.gaiaAuthHost_.reload();
916 this.loading = true;
917 this.startLoadingTimer_();
921 * Updates localized content of the screen that is not updated via template.
923 updateLocalizedContent: function() {
924 $('createAccount').innerHTML = loadTimeData.getStringF(
925 'createAccount',
926 '<a id="createAccountLink" class="signin-link" href="#">',
927 '</a>');
928 $('guestSignin').innerHTML = loadTimeData.getStringF(
929 'guestSignin',
930 '<a id="guestSigninLink" class="signin-link" href="#">',
931 '</a>');
932 $('createSupervisedUserLinkPlaceholder').innerHTML =
933 loadTimeData.getStringF(
934 'createSupervisedUser',
935 '<a id="createSupervisedUserLink" class="signin-link" href="#">',
936 '</a>');
937 $('consumerManagementEnrollment').innerHTML = loadTimeData.getString(
938 'consumerManagementEnrollmentSigninMessage');
939 $('createAccountLink').addEventListener('click', function(e) {
940 chrome.send('createAccount');
941 e.preventDefault();
943 $('guestSigninLink').addEventListener('click', function(e) {
944 chrome.send('launchIncognito');
945 e.preventDefault();
947 $('createSupervisedUserLink').addEventListener('click', function(e) {
948 chrome.send('showSupervisedUserCreationScreen');
949 e.preventDefault();
954 * Shows sign-in error bubble.
955 * @param {number} loginAttempts Number of login attemps tried.
956 * @param {HTMLElement} content Content to show in bubble.
958 showErrorBubble: function(loginAttempts, error) {
959 if (this.isLocal) {
960 $('add-user-button').hidden = true;
961 $('cancel-add-user-button').hidden = false;
962 // Reload offline version of the sign-in extension, which will show
963 // error itself.
964 chrome.send('offlineLogin', [this.email]);
965 } else if (!this.loading) {
966 // We want to show bubble near "Email" field, but we can't calculate
967 // it's position because it is located inside iframe. So we only
968 // can hardcode some constants.
969 /** @const */ var ERROR_BUBBLE_OFFSET = 84;
970 /** @const */ var ERROR_BUBBLE_PADDING = 0;
971 $('bubble').showContentForElement($('login-box'),
972 cr.ui.Bubble.Attachment.LEFT,
973 error,
974 ERROR_BUBBLE_OFFSET,
975 ERROR_BUBBLE_PADDING);
976 } else {
977 // Defer the bubble until the frame has been loaded.
978 this.errorBubble_ = [loginAttempts, error];
983 * Called when user canceled signin.
985 cancel: function() {
986 if (!this.cancelAllowed_) {
987 // In OOBE signin screen, cancel is not allowed because there is
988 // no other screen to show. If user is in middle of a saml flow,
989 // reset signin screen to get out of the saml flow.
990 if (this.isSAML())
991 Oobe.resetSigninUI(true);
993 return;
996 $('offline-gaia').switchToEmailCard();
998 this.classList.remove('whitelist-error');
999 Oobe.showUserPods();
1003 * Handler for iframe's error notification coming from the outside.
1004 * For more info see C++ class 'WebUILoginView' which calls this method.
1005 * @param {number} error Error code.
1006 * @param {string} url The URL that failed to load.
1008 onFrameError: function(error, url) {
1009 this.error_ = error;
1010 chrome.send('frameLoadingCompleted', [this.error_]);
1014 * Handler for webview error handling.
1015 * @param {!Object} data Additional information about error event like:
1016 * {string} error Error code such as "ERR_INTERNET_DISCONNECTED".
1017 * {string} url The URL that failed to load.
1019 onWebviewError: function(data) {
1020 chrome.send('webviewLoadAborted', [data.error]);
1024 * Handler for identifierEntered event.
1025 * @param {!Object} data The identifier entered by user:
1026 * {string} accountIdentifier User identifier.
1028 onIdentifierEntered: function(data) {
1029 chrome.send('identifierEntered', [data.accountIdentifier]);
1033 * Sets welcome and enterpriseinfo strings for offline gaia.
1034 * Also sets callback and sends message whether we already have email and
1035 * should switch to the password screen with error.
1037 loadOffline: function(params) {
1038 var offlineLogin = $('offline-gaia');
1039 var strings = params.localizedStrings;
1040 if ('stringEnterpriseInfo' in strings)
1041 offlineLogin.enterpriseInfo = strings['stringEnterpriseInfo'];
1042 if ('emailDomain' in params)
1043 offlineLogin.emailDomain = '@' + params['emailDomain'];
1044 offlineLogin.setEmail(params.email);
1048 * Show/Hide error when user is not in whitelist. When UI is hidden
1049 * GAIA is reloaded.
1050 * @param {boolean} show Show/hide error UI.
1051 * @param {!Object} opt_data Optional additional information.
1053 showWhitelistCheckFailedError: function(show, opt_data) {
1054 if (show) {
1055 var isManaged = opt_data && opt_data.enterpriseManaged;
1056 $('gaia-whitelist-error').textContent =
1057 loadTimeData.getValue(isManaged ? 'whitelistErrorEnterprise' :
1058 'whitelistErrorConsumer');
1061 this.classList.toggle('whitelist-error', show);
1062 this.loading = !show;
1064 if (!show)
1065 Oobe.showSigninUI();