Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / chromeos / login / screen_gaia_signin.js
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.
4
5 /**
6  * @fileoverview Oobe signin screen implementation.
7  */
8
9 <include src="../../gaia_auth_host/gaia_auth_host.js">
10
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;
15
16   // Maximum Gaia loading time in seconds.
17   /** @const */ var MAX_GAIA_LOADING_TIME_SEC = 60;
18
19   /** @const */ var HELP_TOPIC_ENTERPRISE_REPORTING = 2535613;
20
21   return {
22     EXTERNAL_API: [
23       'loadAuthExtension',
24       'updateAuthExtension',
25       'doReload',
26       'onFrameError',
27       'updateCancelButtonState'
28     ],
29
30     /**
31      * Frame loading error code (0 - no error).
32      * @type {number}
33      * @private
34      */
35     error_: 0,
36
37     /**
38      * Saved gaia auth host load params.
39      * @type {?string}
40      * @private
41      */
42     gaiaAuthParams_: null,
43
44     /**
45      * Whether local version of Gaia page is used.
46      * @type {boolean}
47      * @private
48      */
49     isLocal_: false,
50
51     /**
52      * Email of the user, which is logging in using offline mode.
53      * @type {string}
54      */
55     email: '',
56
57     /**
58      * Whether consumer management enrollment is in progress.
59      * @type {boolean}
60      * @private
61      */
62     isEnrollingConsumerManagement_: false,
63
64     /**
65      * Timer id of pending load.
66      * @type {number}
67      * @private
68      */
69     loadingTimer_: undefined,
70
71     /**
72      * Whether user can cancel Gaia screen.
73      * @type {boolean}
74      * @private
75      */
76     cancelAllowed_: undefined,
77
78     /**
79      * Whether we should show user pods on the login screen.
80      * @type {boolean}
81      * @private
82      */
83     isShowUsers_: undefined,
84
85     /**
86      * SAML password confirmation attempt count.
87      * @type {number}
88      */
89     samlPasswordConfirmAttempt_: 0,
90
91     /** @override */
92     decorate: function() {
93       this.gaiaAuthHost_ = new cr.login.GaiaAuthHost($('signin-frame'));
94       this.gaiaAuthHost_.addEventListener(
95           'ready', this.onAuthReady_.bind(this));
96       this.gaiaAuthHost_.confirmPasswordCallback =
97           this.onAuthConfirmPassword_.bind(this);
98       this.gaiaAuthHost_.noPasswordCallback =
99           this.onAuthNoPassword_.bind(this);
100       this.gaiaAuthHost_.insecureContentBlockedCallback =
101           this.onInsecureContentBlocked_.bind(this);
102       this.gaiaAuthHost_.missingGaiaInfoCallback =
103           this.missingGaiaInfo_.bind(this);
104       this.gaiaAuthHost_.samlApiUsedCallback =
105           this.samlApiUsed_.bind(this);
106       this.gaiaAuthHost_.addEventListener('authFlowChange',
107           this.onAuthFlowChange_.bind(this));
108
109       $('enterprise-info-hint-link').addEventListener('click', function(e) {
110         chrome.send('launchHelpApp', [HELP_TOPIC_ENTERPRISE_REPORTING]);
111         e.preventDefault();
112       });
113
114
115       this.updateLocalizedContent();
116     },
117
118     /**
119      * Header text of the screen.
120      * @type {string}
121      */
122     get header() {
123       return loadTimeData.getString('signinScreenTitle');
124     },
125
126     /**
127      * Returns true if local version of Gaia is used.
128      * @type {boolean}
129      */
130     get isLocal() {
131       return this.isLocal_;
132     },
133
134     /**
135      * Sets whether local version of Gaia is used.
136      * @param {boolean} value Whether local version of Gaia is used.
137      */
138     set isLocal(value) {
139       this.isLocal_ = value;
140       chrome.send('updateOfflineLogin', [value]);
141     },
142
143     /**
144      * Shows/hides loading UI.
145      * @param {boolean} show True to show loading UI.
146      * @private
147      */
148     showLoadingUI_: function(show) {
149       $('gaia-loading').hidden = !show;
150       this.gaiaAuthHost_.frame.hidden = show;
151       $('signin-right').hidden = show;
152       $('enterprise-info-container').hidden = show;
153       $('gaia-signin-divider').hidden = show;
154     },
155
156     /**
157      * Handler for Gaia loading suspiciously long timeout.
158      * @private
159      */
160     onLoadingSuspiciouslyLong_: function() {
161       if (this != Oobe.getInstance().currentScreen)
162         return;
163       chrome.send('showLoadingTimeoutError');
164       this.loadingTimer_ = window.setTimeout(
165           this.onLoadingTimeOut_.bind(this),
166           (MAX_GAIA_LOADING_TIME_SEC - GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC) *
167           1000);
168     },
169
170     /**
171      * Handler for Gaia loading timeout.
172      * @private
173      */
174     onLoadingTimeOut_: function() {
175       this.loadingTimer_ = undefined;
176       chrome.send('showLoadingTimeoutError');
177     },
178
179     /**
180      * Clears loading timer.
181      * @private
182      */
183     clearLoadingTimer_: function() {
184       if (this.loadingTimer_) {
185         window.clearTimeout(this.loadingTimer_);
186         this.loadingTimer_ = undefined;
187       }
188     },
189
190     /**
191      * Sets up loading timer.
192      * @private
193      */
194     startLoadingTimer_: function() {
195       this.clearLoadingTimer_();
196       this.loadingTimer_ = window.setTimeout(
197           this.onLoadingSuspiciouslyLong_.bind(this),
198           GAIA_LOADING_PORTAL_SUSSPECT_TIME_SEC * 1000);
199     },
200
201     /**
202      * Whether Gaia is loading.
203      * @type {boolean}
204      */
205     get loading() {
206       return !$('gaia-loading').hidden;
207     },
208     set loading(loading) {
209       if (loading == this.loading)
210         return;
211
212       this.showLoadingUI_(loading);
213     },
214
215     /**
216      * Event handler that is invoked just before the frame is shown.
217      * @param {string} data Screen init payload. Url of auth extension start
218      *                      page.
219      */
220     onBeforeShow: function(data) {
221       chrome.send('loginUIStateChanged', ['gaia-signin', true]);
222       $('login-header-bar').signinUIState =
223           this.isEnrollingConsumerManagement_ ?
224               SIGNIN_UI_STATE.CONSUMER_MANAGEMENT_ENROLLMENT :
225               SIGNIN_UI_STATE.GAIA_SIGNIN;
226
227       // Ensure that GAIA signin (or loading UI) is actually visible.
228       window.requestAnimationFrame(function() {
229         chrome.send('loginVisible', ['gaia-loading']);
230       });
231
232       // Button header is always visible when sign in is presented.
233       // Header is hidden once GAIA reports on successful sign in.
234       Oobe.getInstance().headerHidden = false;
235     },
236
237     /**
238      * Event handler that is invoked just before the screen is hidden.
239      */
240     onBeforeHide: function() {
241       chrome.send('loginUIStateChanged', ['gaia-signin', false]);
242       $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN;
243     },
244
245     /**
246      * Loads the authentication extension into the iframe.
247      * @param {Object} data Extension parameters bag.
248      * @private
249      */
250     loadAuthExtension: function(data) {
251       this.isLocal = data.isLocal;
252       this.email = '';
253
254       // Reset SAML
255       this.classList.toggle('saml', false);
256       this.samlPasswordConfirmAttempt_ = 0;
257
258       this.updateAuthExtension(data);
259
260       var params = {};
261       for (var i in cr.login.GaiaAuthHost.SUPPORTED_PARAMS) {
262         var name = cr.login.GaiaAuthHost.SUPPORTED_PARAMS[i];
263         if (data[name])
264           params[name] = data[name];
265       }
266
267       if (data.localizedStrings)
268         params.localizedStrings = data.localizedStrings;
269
270       if (data.useEmbedded)
271         params.gaiaPath = 'EmbeddedSignIn';
272
273       if (data.forceReload ||
274           JSON.stringify(this.gaiaAuthParams_) != JSON.stringify(params)) {
275         this.error_ = 0;
276
277         var authMode = cr.login.GaiaAuthHost.AuthMode.DEFAULT;
278         if (data.useOffline)
279           authMode = cr.login.GaiaAuthHost.AuthMode.OFFLINE;
280         else if (data.useEmbedded)
281           authMode = cr.login.GaiaAuthHost.AuthMode.DESKTOP;
282
283         this.gaiaAuthHost_.load(authMode,
284                                 params,
285                                 this.onAuthCompleted_.bind(this));
286         this.gaiaAuthParams_ = params;
287
288         this.loading = true;
289         this.startLoadingTimer_();
290       } else if (this.loading && this.error_) {
291         // An error has occurred, so trying to reload.
292         this.doReload();
293       }
294     },
295
296     /**
297      * Updates the authentication extension with new parameters, if needed.
298      * @param {Object} data New extension parameters bag.
299      * @private
300      */
301     updateAuthExtension: function(data) {
302       var reasonLabel = $('gaia-signin-reason');
303       if (data.passwordChanged) {
304         reasonLabel.textContent =
305             loadTimeData.getString('signinScreenPasswordChanged');
306         reasonLabel.hidden = false;
307       } else {
308         reasonLabel.hidden = true;
309       }
310
311       $('createAccount').hidden = !data.createAccount;
312       $('guestSignin').hidden = !data.guestSignin;
313       $('createSupervisedUserPane').hidden = !data.supervisedUsersEnabled;
314
315       $('createSupervisedUserLinkPlaceholder').hidden =
316           !data.supervisedUsersCanCreate;
317       $('createSupervisedUserNoManagerText').hidden =
318           data.supervisedUsersCanCreate;
319       $('createSupervisedUserNoManagerText').textContent =
320           data.supervisedUsersRestrictionReason;
321
322       var isEnrollingConsumerManagement = data.isEnrollingConsumerManagement;
323       $('consumerManagementEnrollment').hidden = !isEnrollingConsumerManagement;
324
325       this.isShowUsers_ = data.isShowUsers;
326       this.updateCancelButtonState();
327
328       this.isEnrollingConsumerManagement_ = isEnrollingConsumerManagement;
329
330       // Sign-in right panel is hidden if all of its items are hidden.
331       var noRightPanel = $('gaia-signin-reason').hidden &&
332                          $('createAccount').hidden &&
333                          $('guestSignin').hidden &&
334                          $('createSupervisedUserPane').hidden &&
335                          $('consumerManagementEnrollment').hidden;
336       this.classList.toggle('no-right-panel', noRightPanel);
337       if (Oobe.getInstance().currentScreen === this)
338         Oobe.getInstance().updateScreenSize(this);
339     },
340
341     /**
342      * Updates [Cancel] button state. Allow cancellation of screen only when
343      * user pods can be displayed.
344      */
345     updateCancelButtonState: function() {
346       this.cancelAllowed_ = this.isShowUsers_ && $('pod-row').pods.length;
347       $('login-header-bar').allowCancel = this.cancelAllowed_;
348     },
349
350     /**
351      * Whether the current auth flow is SAML.
352      */
353     isSAML: function() {
354        return this.gaiaAuthHost_.authFlow ==
355            cr.login.GaiaAuthHost.AuthFlow.SAML;
356     },
357
358     /**
359      * Invoked when the authFlow property is changed no the gaia host.
360      * @param {Event} e Property change event.
361      */
362     onAuthFlowChange_: function(e) {
363       var isSAML = this.isSAML();
364
365       if (isSAML) {
366         $('saml-notice-message').textContent = loadTimeData.getStringF(
367             'samlNotice',
368             this.gaiaAuthHost_.authDomain);
369       }
370
371       this.classList.toggle('saml', isSAML);
372       $('saml-notice-container').hidden = !isSAML;
373
374       if (Oobe.getInstance().currentScreen === this) {
375         Oobe.getInstance().updateScreenSize(this);
376         $('login-header-bar').allowCancel = isSAML || this.cancelAllowed_;
377       }
378     },
379
380     /**
381      * Invoked when the auth host emits 'ready' event.
382      * @private
383      */
384     onAuthReady_: function() {
385       this.loading = false;
386       this.clearLoadingTimer_();
387
388       // Show deferred error bubble.
389       if (this.errorBubble_) {
390         this.showErrorBubble(this.errorBubble_[0], this.errorBubble_[1]);
391         this.errorBubble_ = undefined;
392       }
393
394       chrome.send('loginWebuiReady');
395       chrome.send('loginVisible', ['gaia-signin']);
396
397       // Warm up the user images screen.
398       Oobe.getInstance().preloadScreen({id: SCREEN_USER_IMAGE_PICKER});
399     },
400
401     /**
402      * Invoked when the user has successfully authenticated via SAML, the
403      * principals API was not used and the auth host needs the user to confirm
404      * the scraped password.
405      * @param {number} passwordCount The number of passwords that were scraped.
406      * @private
407      */
408     onAuthConfirmPassword_: function(passwordCount) {
409       this.loading = true;
410       Oobe.getInstance().headerHidden = false;
411
412       if (this.samlPasswordConfirmAttempt_ == 0)
413         chrome.send('scrapedPasswordCount', [passwordCount]);
414
415       if (this.samlPasswordConfirmAttempt_ < 2) {
416         login.ConfirmPasswordScreen.show(
417             this.samlPasswordConfirmAttempt_,
418             this.onConfirmPasswordCollected_.bind(this));
419       } else {
420         chrome.send('scrapedPasswordVerificationFailed');
421         this.showFatalAuthError(
422             loadTimeData.getString('fatalErrorMessageVerificationFailed'));
423       }
424     },
425
426     /**
427      * Invoked when the confirm password screen is dismissed.
428      * @private
429      */
430     onConfirmPasswordCollected_: function(password) {
431       this.samlPasswordConfirmAttempt_++;
432       this.gaiaAuthHost_.verifyConfirmedPassword(password);
433
434       // Shows signin UI again without changing states.
435       Oobe.showScreen({id: SCREEN_GAIA_SIGNIN});
436     },
437
438     /**
439      * Inovked when the user has successfully authenticated via SAML, the
440      * principals API was not used and no passwords could be scraped.
441      * @param {string} email The authenticated user's e-mail.
442      */
443     onAuthNoPassword_: function(email) {
444       this.showFatalAuthError(loadTimeData.getString(
445           'fatalErrorMessageNoPassword'));
446       chrome.send('scrapedPasswordCount', [0]);
447     },
448
449     /**
450      * Invoked when the authentication flow had to be aborted because content
451      * served over an unencrypted connection was detected. Shows a fatal error.
452      * This method is only called on Chrome OS, where the entire authentication
453      * flow is required to be encrypted.
454      * @param {string} url The URL that was blocked.
455      */
456     onInsecureContentBlocked_: function(url) {
457       this.showFatalAuthError(loadTimeData.getStringF(
458           'fatalErrorMessageInsecureURL',
459           url));
460     },
461
462     /**
463      * Shows the fatal auth error.
464      * @param {string} message The error message to show.
465      */
466     showFatalAuthError: function(message) {
467       login.FatalErrorScreen.show(message, Oobe.showSigninUI);
468     },
469
470     /**
471      * Show fatal auth error when information is missing from GAIA.
472      */
473     missingGaiaInfo_: function() {
474       this.showFatalAuthError(
475           loadTimeData.getString('fatalErrorMessageNoAccountDetails'));
476     },
477
478     /**
479      * Record that SAML API was used during sign-in.
480      */
481     samlApiUsed_: function() {
482       chrome.send('usingSAMLAPI');
483     },
484
485     /**
486      * Invoked when auth is completed successfully.
487      * @param {!Object} credentials Credentials of the completed authentication.
488      * @private
489      */
490     onAuthCompleted_: function(credentials) {
491       if (credentials.useOffline) {
492         this.email = credentials.email;
493         chrome.send('authenticateUser',
494                     [credentials.gaiaId,
495                      credentials.email,
496                      credentials.password]);
497       } else if (credentials.authCode) {
498         chrome.send('completeAuthentication',
499                     [credentials.gaiaId,
500                      credentials.email,
501                      credentials.password,
502                      credentials.authCode]);
503       } else {
504         chrome.send('completeLogin',
505                     [credentials.gaiaId,
506                      credentials.email,
507                      credentials.password,
508                      credentials.usingSAML]);
509       }
510
511       this.loading = true;
512       // Now that we're in logged in state header should be hidden.
513       Oobe.getInstance().headerHidden = true;
514       // Clear any error messages that were shown before login.
515       Oobe.clearErrors();
516     },
517
518     /**
519      * Clears input fields and switches to input mode.
520      * @param {boolean} takeFocus True to take focus.
521      * @param {boolean} forceOnline Whether online sign-in should be forced.
522      * If |forceOnline| is false previously used sign-in type will be used.
523      */
524     reset: function(takeFocus, forceOnline) {
525       // Reload and show the sign-in UI if needed.
526       if (takeFocus) {
527         if (!forceOnline && this.isLocal) {
528           // Show 'Cancel' button to allow user to return to the main screen
529           // (e.g. this makes sense when connection is back).
530           Oobe.getInstance().headerHidden = false;
531           $('login-header-bar').signinUIState = SIGNIN_UI_STATE.GAIA_SIGNIN;
532           // Do nothing, since offline version is reloaded after an error comes.
533         } else {
534           Oobe.showSigninUI();
535         }
536       }
537     },
538
539     /**
540      * Reloads extension frame.
541      */
542     doReload: function() {
543       this.error_ = 0;
544       this.gaiaAuthHost_.reload();
545       this.loading = true;
546       this.startLoadingTimer_();
547     },
548
549     /**
550      * Updates localized content of the screen that is not updated via template.
551      */
552     updateLocalizedContent: function() {
553       $('createAccount').innerHTML = loadTimeData.getStringF(
554           'createAccount',
555           '<a id="createAccountLink" class="signin-link" href="#">',
556           '</a>');
557       $('guestSignin').innerHTML = loadTimeData.getStringF(
558           'guestSignin',
559           '<a id="guestSigninLink" class="signin-link" href="#">',
560           '</a>');
561       $('createSupervisedUserLinkPlaceholder').innerHTML =
562           loadTimeData.getStringF(
563               'createSupervisedUser',
564               '<a id="createSupervisedUserLink" class="signin-link" href="#">',
565               '</a>');
566       $('consumerManagementEnrollment').innerHTML = loadTimeData.getString(
567           'consumerManagementEnrollmentSigninMessage');
568       $('createAccountLink').addEventListener('click', function(e) {
569         chrome.send('createAccount');
570         e.preventDefault();
571       });
572       $('guestSigninLink').addEventListener('click', function(e) {
573         chrome.send('launchIncognito');
574         e.preventDefault();
575       });
576       $('createSupervisedUserLink').addEventListener('click', function(e) {
577         chrome.send('showSupervisedUserCreationScreen');
578         e.preventDefault();
579       });
580     },
581
582     /**
583      * Shows sign-in error bubble.
584      * @param {number} loginAttempts Number of login attemps tried.
585      * @param {HTMLElement} content Content to show in bubble.
586      */
587     showErrorBubble: function(loginAttempts, error) {
588       if (this.isLocal) {
589         $('add-user-button').hidden = true;
590         $('cancel-add-user-button').hidden = false;
591         // Reload offline version of the sign-in extension, which will show
592         // error itself.
593         chrome.send('offlineLogin', [this.email]);
594       } else if (!this.loading) {
595         // We want to show bubble near "Email" field, but we can't calculate
596         // it's position because it is located inside iframe. So we only
597         // can hardcode some constants.
598         /** @const */ var ERROR_BUBBLE_OFFSET = 84;
599         /** @const */ var ERROR_BUBBLE_PADDING = 0;
600         $('bubble').showContentForElement($('login-box'),
601                                           cr.ui.Bubble.Attachment.LEFT,
602                                           error,
603                                           ERROR_BUBBLE_OFFSET,
604                                           ERROR_BUBBLE_PADDING);
605       } else {
606         // Defer the bubble until the frame has been loaded.
607         this.errorBubble_ = [loginAttempts, error];
608       }
609     },
610
611     /**
612      * Called when user canceled signin.
613      */
614     cancel: function() {
615       if (!this.cancelAllowed_) {
616         // In OOBE signin screen, cancel is not allowed because there is
617         // no other screen to show. If user is in middle of a saml flow,
618         // reset signin screen to get out of the saml flow.
619         if (this.isSAML())
620           Oobe.resetSigninUI(true);
621
622         return;
623       }
624
625       $('pod-row').loadLastWallpaper();
626       Oobe.showScreen({id: SCREEN_ACCOUNT_PICKER});
627       Oobe.resetSigninUI(true);
628     },
629
630     /**
631      * Handler for iframe's error notification coming from the outside.
632      * For more info see C++ class 'WebUILoginView' which calls this method.
633      * @param {number} error Error code.
634      * @param {string} url The URL that failed to load.
635      */
636     onFrameError: function(error, url) {
637       this.error_ = error;
638       chrome.send('frameLoadingCompleted', [this.error_]);
639     },
640   };
641 });