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 cr.define('options', function() {
6 /** @const */ var OptionsPage = options.OptionsPage;
8 // Variable to track if a captcha challenge was issued. If this gets set to
9 // true, it stays that way until we are told about successful login from
10 // the browser. This means subsequent errors (like invalid password) are
11 // rendered in the captcha state, which is basically identical except we
12 // don't show the top error blurb 'Error Signing in' or the 'Create
14 var captchaChallengeActive_ = false;
16 // When true, the password value may be empty when submitting auth info.
17 // This is true when requesting an access code or when requesting an OTP or
18 // captcha with the oauth sign in flow.
19 var allowEmptyPassword_ = false;
21 // True if the synced account uses a custom passphrase.
22 var usePassphrase_ = false;
24 // True if the synced account uses 'encrypt everything'.
25 var useEncryptEverything_ = false;
27 // True if the support for keystore encryption is enabled. Controls whether
28 // the new unified encryption UI is displayed instead of the old encryption
29 // ui (where passphrase and encrypted types could be set independently of
31 var keystoreEncryptionEnabled_ = false;
33 // The last email address that this profile was connected to. If the profile
34 // was never connected this is an empty string. Otherwise it is a normalized
36 var lastEmailAddress_ = '';
39 * SyncSetupOverlay class
40 * Encapsulated handling of the 'Sync Setup' overlay page.
43 function SyncSetupOverlay() {
44 OptionsPage.call(this, 'syncSetup',
45 loadTimeData.getString('syncSetupOverlayTabTitle'),
46 'sync-setup-overlay');
49 cr.addSingletonGetter(SyncSetupOverlay);
51 SyncSetupOverlay.prototype = {
52 __proto__: OptionsPage.prototype,
55 * Initializes the page.
57 initializePage: function() {
58 OptionsPage.prototype.initializePage.call(this);
61 $('gaia-login-form').onsubmit = function() {
62 self.sendCredentialsAndClose_();
65 $('google-option').onchange = $('explicit-option').onchange = function() {
66 self.onPassphraseRadioChanged_();
68 $('basic-encryption-option').onchange =
69 $('full-encryption-option').onchange = function() {
70 self.onEncryptionRadioChanged_();
72 $('choose-datatypes-cancel').onclick =
73 $('sync-setup-cancel').onclick =
74 $('confirm-everything-cancel').onclick =
75 $('stop-syncing-cancel').onclick =
76 $('sync-spinner-cancel').onclick = function() {
79 $('confirm-everything-ok').onclick = function() {
80 self.sendConfiguration_();
82 $('timeout-ok').onclick = function() {
83 chrome.send('CloseTimeout');
86 $('stop-syncing-ok').onclick = function() {
87 chrome.send('SyncSetupStopSyncing');
90 $('different-email').innerHTML = loadTimeData.getString('differentEmail');
93 showOverlay_: function() {
94 OptionsPage.navigateToPage('syncSetup');
97 closeOverlay_: function() {
98 OptionsPage.closeOverlay();
102 didShowPage: function() {
103 chrome.send('SyncSetupShowSetupUI');
107 didClosePage: function() {
108 chrome.send('SyncSetupDidClosePage');
111 getEncryptionRadioCheckedValue_: function() {
112 var f = $('choose-data-types-form');
113 for (var i = 0; i < f.encrypt.length; ++i) {
114 if (f.encrypt[i].checked)
115 return f.encrypt[i].value;
121 getPassphraseRadioCheckedValue_: function() {
122 var f = $('choose-data-types-form');
123 for (var i = 0; i < f.option.length; ++i) {
124 if (f.option[i].checked) {
125 return f.option[i].value;
132 disableEncryptionRadioGroup_: function() {
133 var f = $('choose-data-types-form');
134 for (var i = 0; i < f.encrypt.length; ++i)
135 f.encrypt[i].disabled = true;
138 onPassphraseRadioChanged_: function() {
139 var visible = this.getPassphraseRadioCheckedValue_() == 'explicit';
140 $('sync-custom-passphrase').hidden = !visible;
143 onEncryptionRadioChanged_: function() {
144 var visible = $('full-encryption-option').checked;
145 $('sync-custom-passphrase').hidden = !visible;
148 checkAllDataTypeCheckboxes_: function() {
149 // Only check the visible ones (since there's no way to uncheck
150 // the invisible ones).
151 var checkboxes = $('choose-data-types-body').querySelectorAll(
152 '.sync-type-checkbox:not([hidden]) input');
153 for (var i = 0; i < checkboxes.length; i++) {
154 checkboxes[i].checked = true;
158 setDataTypeCheckboxesEnabled_: function(enabled) {
159 var checkboxes = $('choose-data-types-body').querySelectorAll('input');
160 for (var i = 0; i < checkboxes.length; i++) {
161 checkboxes[i].disabled = !enabled;
165 setCheckboxesToKeepEverythingSynced_: function(value) {
166 this.setDataTypeCheckboxesEnabled_(!value);
168 this.checkAllDataTypeCheckboxes_();
171 // Returns true if none of the visible checkboxes are checked.
172 noDataTypesChecked_: function() {
173 var query = '.sync-type-checkbox:not([hidden]) input:checked';
174 var checkboxes = $('choose-data-types-body').querySelectorAll(query);
175 return checkboxes.length == 0;
178 checkPassphraseMatch_: function() {
179 var emptyError = $('empty-error');
180 var mismatchError = $('mismatch-error');
181 emptyError.hidden = true;
182 mismatchError.hidden = true;
184 var f = $('choose-data-types-form');
185 if ((this.getPassphraseRadioCheckedValue_() != 'explicit' ||
186 $('google-option').disabled) &&
187 (!$('full-encryption-option').checked ||
188 $('basic-encryption-option').disabled)) {
192 var customPassphrase = $('custom-passphrase');
193 if (customPassphrase.value.length == 0) {
194 emptyError.hidden = false;
198 var confirmPassphrase = $('confirm-passphrase');
199 if (confirmPassphrase.value != customPassphrase.value) {
200 mismatchError.hidden = false;
207 sendConfiguration_: function() {
208 // Trying to submit, so hide previous errors.
209 $('error-text').hidden = true;
211 var syncAll = $('sync-select-datatypes').selectedIndex == 0;
212 if (!syncAll && this.noDataTypesChecked_()) {
213 $('error-text').hidden = false;
217 var encryptAllData = this.getEncryptionRadioCheckedValue_() == 'all';
218 if (!encryptAllData &&
219 $('full-encryption-option').checked &&
220 this.keystoreEncryptionEnabled_) {
221 encryptAllData = true;
225 var customPassphrase;
226 var googlePassphrase = false;
227 if (!$('sync-existing-passphrase-container').hidden) {
228 // If we were prompted for an existing passphrase, use it.
229 customPassphrase = $('choose-data-types-form').passphrase.value;
230 usePassphrase = true;
231 // If we were displaying the 'enter your old google password' prompt,
232 // then that means this is the user's google password.
233 googlePassphrase = !$('google-passphrase-needed-body').hidden;
234 // We allow an empty passphrase, in case the user has disabled
235 // all their encrypted datatypes. In that case, the PSS will accept
236 // the passphrase and finish configuration. If the user has enabled
237 // encrypted datatypes, the PSS will prompt again specifying that the
238 // passphrase failed.
239 } else if ((!$('google-option').disabled &&
240 this.getPassphraseRadioCheckedValue_() == 'explicit') ||
241 (!$('basic-encryption-option').disabled &&
242 $('full-encryption-option').checked)) {
243 // The user is setting a custom passphrase for the first time.
244 if (!this.checkPassphraseMatch_())
246 customPassphrase = $('custom-passphrase').value;
247 usePassphrase = true;
249 // The user is not setting a custom passphrase.
250 usePassphrase = false;
253 // Don't allow the user to tweak the settings once we send the
254 // configuration to the backend.
255 this.setInputElementsDisabledState_(true);
256 this.animateDisableLink_($('use-default-link'), true, null);
258 // These values need to be kept in sync with where they are read in
259 // SyncSetupFlow::GetDataTypeChoiceData().
260 var result = JSON.stringify({
261 'syncAllDataTypes': syncAll,
262 'bookmarksSynced': syncAll || $('bookmarks-checkbox').checked,
263 'preferencesSynced': syncAll || $('preferences-checkbox').checked,
264 'themesSynced': syncAll || $('themes-checkbox').checked,
265 'passwordsSynced': syncAll || $('passwords-checkbox').checked,
266 'autofillSynced': syncAll || $('autofill-checkbox').checked,
267 'extensionsSynced': syncAll || $('extensions-checkbox').checked,
268 'typedUrlsSynced': syncAll || $('typed-urls-checkbox').checked,
269 'appsSynced': syncAll || $('apps-checkbox').checked,
270 'tabsSynced': syncAll || $('tabs-checkbox').checked,
271 'encryptAllData': encryptAllData,
272 'usePassphrase': usePassphrase,
273 'isGooglePassphrase': googlePassphrase,
274 'passphrase': customPassphrase
276 chrome.send('SyncSetupConfigure', [result]);
280 * Sets the disabled property of all input elements within the 'Customize
281 * Sync Preferences' screen. This is used to prohibit the user from changing
282 * the inputs after confirming the customized sync preferences, or resetting
283 * the state when re-showing the dialog.
284 * @param {boolean} disabled True if controls should be set to disabled.
287 setInputElementsDisabledState_: function(disabled) {
288 var configureElements =
289 $('customize-sync-preferences').querySelectorAll('input');
290 for (var i = 0; i < configureElements.length; i++)
291 configureElements[i].disabled = disabled;
292 $('sync-select-datatypes').disabled = disabled;
295 this.animateDisableLink_($('customize-link'), disabled, function() {
296 self.showCustomizePage_(null, true);
301 * Animate a link being enabled/disabled. The link is hidden by animating
302 * its opacity, but to ensure the user doesn't click it during that time,
303 * its onclick handler is changed to null as well.
304 * @param {HTMLElement} elt The anchor element to enable/disable.
305 * @param {boolean} disabled True if the link should be disabled.
306 * @param {function} enabledFunction The onclick handler when the link is
310 animateDisableLink_: function(elt, disabled, enabledFunction) {
312 elt.classList.add('transparent');
314 elt.addEventListener('webkitTransitionEnd', function f(e) {
315 if (e.propertyName != 'opacity')
317 elt.removeEventListener('webkitTransitionEnd', f);
318 elt.classList.remove('transparent');
323 elt.onclick = enabledFunction;
328 * Shows or hides the Sync data type checkboxes in the advanced
329 * configuration screen.
330 * @param {Object} args The configuration data used to show/hide UI.
333 setChooseDataTypesCheckboxes_: function(args) {
334 var datatypeSelect = $('sync-select-datatypes');
335 datatypeSelect.selectedIndex = args.syncAllDataTypes ? 0 : 1;
337 $('bookmarks-checkbox').checked = args.bookmarksSynced;
338 $('preferences-checkbox').checked = args.preferencesSynced;
339 $('themes-checkbox').checked = args.themesSynced;
341 if (args.passwordsRegistered) {
342 $('passwords-checkbox').checked = args.passwordsSynced;
343 $('passwords-item').hidden = false;
345 $('passwords-item').hidden = true;
347 if (args.autofillRegistered) {
348 $('autofill-checkbox').checked = args.autofillSynced;
349 $('autofill-item').hidden = false;
351 $('autofill-item').hidden = true;
353 if (args.extensionsRegistered) {
354 $('extensions-checkbox').checked = args.extensionsSynced;
355 $('extensions-item').hidden = false;
357 $('extensions-item').hidden = true;
359 if (args.typedUrlsRegistered) {
360 $('typed-urls-checkbox').checked = args.typedUrlsSynced;
361 $('omnibox-item').hidden = false;
363 $('omnibox-item').hidden = true;
365 if (args.appsRegistered) {
366 $('apps-checkbox').checked = args.appsSynced;
367 $('apps-item').hidden = false;
369 $('apps-item').hidden = true;
371 if (args.tabsRegistered) {
372 $('tabs-checkbox').checked = args.tabsSynced;
373 $('tabs-item').hidden = false;
375 $('tabs-item').hidden = true;
378 this.setCheckboxesToKeepEverythingSynced_(args.syncAllDataTypes);
381 setEncryptionRadios_: function(args) {
382 if (args.encryptAllData) {
383 $('encrypt-all-option').checked = true;
384 this.disableEncryptionRadioGroup_();
386 $('encrypt-sensitive-option').checked = true;
389 if (!args.encryptAllData && !args.usePassphrase) {
390 $('basic-encryption-option').checked = true;
392 $('full-encryption-option').checked = true;
393 $('full-encryption-option').disabled = true;
394 $('basic-encryption-option').disabled = true;
398 setPassphraseRadios_: function(args) {
399 if (args.usePassphrase) {
400 $('explicit-option').checked = true;
402 // The passphrase, once set, cannot be unset, but we show a reset link.
403 $('explicit-option').disabled = true;
404 $('google-option').disabled = true;
405 $('sync-custom-passphrase').hidden = true;
407 $('google-option').checked = true;
411 setCheckboxesAndErrors_: function(args) {
412 this.setChooseDataTypesCheckboxes_(args);
413 this.setEncryptionRadios_(args);
414 this.setPassphraseRadios_(args);
417 showConfigure_: function(args) {
418 var datatypeSelect = $('sync-select-datatypes');
420 datatypeSelect.onchange = function() {
421 var syncAll = this.selectedIndex == 0;
422 self.setCheckboxesToKeepEverythingSynced_(syncAll);
425 this.resetPage_('sync-setup-configure');
426 $('sync-setup-configure').hidden = false;
428 // onsubmit is changed when submitting a passphrase. Reset it to its
430 $('choose-data-types-form').onsubmit = function() {
431 self.sendConfiguration_();
436 this.setCheckboxesAndErrors_(args);
438 this.useEncryptEverything_ = args.encryptAllData;
440 // Whether to display the 'Sync everything' confirmation page or the
441 // customize data types page.
442 var syncAllDataTypes = args.syncAllDataTypes;
443 this.usePassphrase_ = args.usePassphrase;
444 this.keystoreEncryptionEnabled_ = args.keystoreEncryptionEnabled;
445 if (args.showSyncEverythingPage == false || this.usePassphrase_ ||
446 syncAllDataTypes == false || args.showPassphrase) {
447 this.showCustomizePage_(args, syncAllDataTypes);
449 this.showSyncEverythingPage_();
454 showSpinner_: function() {
455 this.resetPage_('sync-setup-spinner');
456 $('sync-setup-spinner').hidden = false;
459 showTimeoutPage_: function() {
460 this.resetPage_('sync-setup-timeout');
461 $('sync-setup-timeout').hidden = false;
464 showSyncEverythingPage_: function() {
465 $('confirm-sync-preferences').hidden = false;
466 $('customize-sync-preferences').hidden = true;
468 // Reset the selection to 'Sync everything'.
469 $('sync-select-datatypes').selectedIndex = 0;
471 // The default state is to sync everything.
472 this.setCheckboxesToKeepEverythingSynced_(true);
474 // Encrypt passwords is the default, but don't set it if the previously
475 // synced account is already set to encrypt everything.
476 if (!this.useEncryptEverything_)
477 $('encrypt-sensitive-option').checked = true;
479 // If the account is not synced with a custom passphrase, reset the
480 // passphrase radio when switching to the 'Sync everything' page.
481 if (!this.usePassphrase_) {
482 $('google-option').checked = true;
483 $('sync-custom-passphrase').hidden = true;
486 if (!this.useEncryptEverything_ && !this.usePassphrase_)
487 $('basic-encryption-option').checked = true;
489 $('confirm-everything-ok').focus();
493 * Reveals the UI for entering a custom passphrase during initial setup.
494 * This happens if the user has previously enabled a custom passphrase on a
496 * @param {Array} args The args that contain the passphrase UI
500 showPassphraseContainer_: function(args) {
501 // Once we require a passphrase, we prevent the user from returning to
502 // the Sync Everything pane.
503 $('use-default-link').hidden = true;
504 $('sync-custom-passphrase-container').hidden = true;
505 $('sync-existing-passphrase-container').hidden = false;
507 // Hide the selection options within the new encryption section when
508 // prompting for a passphrase.
509 $('sync-new-encryption-section-container').hidden = true;
511 $('normal-body').hidden = true;
512 $('google-passphrase-needed-body').hidden = true;
513 // Display the correct prompt to the user depending on what type of
514 // passphrase is needed.
515 if (args.usePassphrase)
516 $('normal-body').hidden = false;
518 $('google-passphrase-needed-body').hidden = false;
520 $('passphrase-learn-more').hidden = false;
521 // Warn the user about their incorrect passphrase if we need a passphrase
522 // and the passphrase field is non-empty (meaning they tried to set it
523 // previously but failed).
524 $('incorrect-passphrase').hidden =
525 !(args.usePassphrase && args.passphraseFailed);
527 $('sync-passphrase-warning').hidden = false;
528 $('passphrase').focus();
532 showCustomizePage_: function(args, syncEverything) {
533 $('confirm-sync-preferences').hidden = true;
534 $('customize-sync-preferences').hidden = false;
536 $('sync-custom-passphrase-container').hidden = false;
538 if (this.keystoreEncryptionEnabled_) {
539 $('customize-sync-encryption').hidden = true;
540 $('sync-custom-passphrase-options').hidden = true;
541 $('sync-new-encryption-section-container').hidden = false;
542 $('customize-sync-encryption-new').hidden = false;
544 $('customize-sync-encryption').hidden = false;
545 $('sync-custom-passphrase-options').hidden = false;
546 $('customize-sync-encryption-new').hidden = true;
549 $('sync-existing-passphrase-container').hidden = true;
551 // If the user has selected the 'Customize' page on initial set up, it's
552 // likely he intends to change the data types. Select the
553 // 'Choose data types' option in this case.
554 var index = syncEverything ? 0 : 1;
555 $('sync-select-datatypes').selectedIndex = index;
556 this.setDataTypeCheckboxesEnabled_(!syncEverything);
558 // The passphrase input may need to take over focus from the OK button, so
559 // set focus before that logic.
560 $('choose-datatypes-ok').focus();
562 if (args && args.showPassphrase) {
563 this.showPassphraseContainer_(args);
565 // We only show the 'Use Default' link if we're not prompting for an
566 // existing passphrase.
568 this.animateDisableLink_($('use-default-link'), false, function() {
569 self.showSyncEverythingPage_();
575 * Shows the appropriate sync setup page.
576 * @param {string} page A page of the sync setup to show.
577 * @param {object} args Data from the C++ to forward on to the right
580 showSyncSetupPage_: function(page, args) {
581 this.setThrobbersVisible_(false);
583 // Hide an existing visible overlay (ensuring the close button is not
585 var children = document.querySelectorAll(
586 '#sync-setup-overlay > *:not(.close-button)');
587 for (var i = 0; i < children.length; i++)
588 children[i].hidden = true;
590 this.setInputElementsDisabledState_(false);
592 // If new passphrase bodies are present, overwrite the existing ones.
593 if (args && args.enterPassphraseBody != undefined)
594 $('normal-body').innerHTML = args.enterPassphraseBody;
595 if (args && args.enterGooglePassphraseBody != undefined) {
596 $('google-passphrase-needed-body').innerHTML =
597 args.enterGooglePassphraseBody;
599 if (args && args.fullEncryptionBody != undefined)
600 $('full-encryption-body').innerHTML = args.fullEncryptionBody;
602 // NOTE: Because both showGaiaLogin_() and showConfigure_() change the
603 // focus, we need to ensure that the overlay container and dialog aren't
604 // [hidden] (as trying to focus() nodes inside of a [hidden] DOM section
607 this.closeOverlay_();
612 this.showGaiaLogin_(args);
613 else if (page == 'configure' || page == 'passphrase')
614 this.showConfigure_(args);
615 else if (page == 'spinner')
617 else if (page == 'timeout')
618 this.showTimeoutPage_();
622 * Changes the visibility of throbbers on this page.
623 * @param {boolean} visible Whether or not to set all throbber nodes
626 setThrobbersVisible_: function(visible) {
627 var throbbers = document.getElementsByClassName('throbber');
628 for (var i = 0; i < throbbers.length; i++)
629 throbbers[i].style.visibility = visible ? 'visible' : 'hidden';
633 * Set the appropriate focus on the GAIA login section of the overlay.
636 loginSetFocus_: function() {
637 var email = this.getLoginEmail_();
638 if (email && !email.value) {
643 var passwd = this.getLoginPasswd_();
649 * Get the login email text input DOM element.
650 * @return {DOMElement} The login email text input.
653 getLoginEmail_: function() {
654 return $('gaia-email');
658 * Get the login password text input DOM element.
659 * @return {DOMElement} The login password text input.
662 getLoginPasswd_: function() {
663 return $('gaia-passwd');
667 * Get the sign in button DOM element.
668 * @return {DOMElement} The sign in button.
671 getSignInButton_: function() {
675 showAccessCodeRequired_: function() {
676 this.allowEmptyPassword_ = true;
678 $('password-row').hidden = true;
679 $('email-row').hidden = true;
680 $('otp-input-row').hidden = true;
682 $('access-code-input-row').hidden = false;
683 $('access-code').disabled = false;
684 $('access-code').focus();
687 showOtpRequired_: function() {
688 this.allowEmptyPassword_ = true;
690 $('password-row').hidden = true;
691 $('email-row').hidden = true;
692 $('access-code-input-row').hidden = true;
694 $('otp-input-row').hidden = false;
695 $('otp').disabled = false;
699 showCaptcha_: function(args) {
700 this.allowEmptyPassword_ = args.hideEmailAndPassword;
701 this.captchaChallengeActive_ = true;
703 if (args.hideEmailAndPassword) {
704 $('password-row').hidden = true;
705 $('email-row').hidden = true;
706 $('create-account-div').hidden = true;
708 // The captcha takes up lots of space, so make room.
709 $('top-blurb-error').hidden = true;
710 $('create-account-div').hidden = true;
711 $('gaia-email').disabled = true;
712 $('gaia-passwd').disabled = false;
715 // It's showtime for the captcha now.
716 $('captcha-div').hidden = false;
717 $('captcha-value').disabled = false;
718 $('captcha-wrapper').style.backgroundImage = url(args.captchaUrl);
722 * Reset the state of all descendant elements of a root element to their
724 * The initial state is specified by adding a class to the descendant
725 * element in sync_setup_overlay.html.
726 * @param {HTMLElement} pageElementId The root page element id.
729 resetPage_: function(pageElementId) {
730 var page = $(pageElementId);
731 var forEach = function(arr, fn) {
732 var length = arr.length;
733 for (var i = 0; i < length; i++) {
738 forEach(page.getElementsByClassName('reset-hidden'),
739 function(elt) { elt.hidden = true; });
740 forEach(page.getElementsByClassName('reset-shown'),
741 function(elt) { elt.hidden = false; });
742 forEach(page.getElementsByClassName('reset-disabled'),
743 function(elt) { elt.disabled = true; });
744 forEach(page.getElementsByClassName('reset-enabled'),
745 function(elt) { elt.disabled = false; });
746 forEach(page.getElementsByClassName('reset-value'),
747 function(elt) { elt.value = ''; });
748 forEach(page.getElementsByClassName('reset-opaque'),
749 function(elt) { elt.classList.remove('transparent'); });
752 showGaiaLogin_: function(args) {
753 var oldAccessCodeValue = $('access-code').value;
754 this.resetPage_('sync-setup-login');
755 $('sync-setup-login').hidden = false;
756 this.allowEmptyPassword_ = false;
757 this.captchaChallengeActive_ = false;
758 this.lastEmailAddress_ = args.lastEmailAddress;
760 var f = $('gaia-login-form');
761 var email = $('gaia-email');
762 var passwd = $('gaia-passwd');
764 if (args.user != undefined) {
765 if (email.value != args.user)
766 passwd.value = ''; // Reset the password field
767 email.value = args.user;
770 if (!args.editableUser) {
771 $('email-row').hidden = true;
772 var span = $('email-readonly');
773 span.textContent = email.value;
774 $('email-readonly-row').hidden = false;
775 $('create-account-div').hidden = true;
778 f.accessCode.disabled = true;
779 f.otp.disabled = true;
782 if (1 == args.error) {
783 if (oldAccessCodeValue) {
784 $('errormsg-0-access-code').hidden = false;
785 this.showAccessCodeRequired_();
787 $('errormsg-1-password').hidden = (args.errorMessage != undefined);
789 this.setBlurbError_(args.errorMessage);
790 } else if (3 == args.error) {
791 $('errormsg-0-connection').hidden = false;
792 this.setBlurbError_(args.errorMessage);
793 } else if (4 == args.error) {
794 this.showCaptcha_(args);
795 } else if (7 == args.error) {
796 this.setBlurbError_(loadTimeData.getString('serviceUnavailableError'));
797 } else if (8 == args.error) {
798 if (args.askForOtp) {
799 this.showOtpRequired_();
801 if (oldAccessCodeValue)
802 $('errormsg-0-access-code').hidden = false;
803 this.showAccessCodeRequired_();
805 } else if (args.errorMessage) {
806 this.setBlurbError_(args.errorMessage);
809 if (args.fatalError) {
810 $('errormsg-fatal').hidden = false;
811 $('sign-in').disabled = true;
815 $('sign-in').disabled = false;
816 $('sign-in').value = loadTimeData.getString('signin');
817 this.loginSetFocus_();
820 resetErrorVisibility_: function() {
821 $('errormsg-0-email').hidden = true;
822 $('errormsg-0-password').hidden = true;
823 $('errormsg-1-password').hidden = true;
824 $('errormsg-different-email').hidden = true;
825 $('errormsg-0-connection').hidden = true;
826 $('errormsg-0-access-code').hidden = true;
827 $('errormsg-0-otp').hidden = true;
830 setBlurbError_: function(errorMessage) {
831 if (this.captchaChallengeActive_)
832 return; // No blurb in captcha challenge mode.
835 $('error-signing-in').hidden = true;
836 $('error-custom').hidden = false;
837 $('error-custom').textContent = errorMessage;
839 $('error-signing-in').hidden = false;
840 $('error-custom').hidden = true;
843 $('top-blurb-error').hidden = false;
844 $('gaia-email').disabled = false;
845 $('gaia-passwd').disabled = false;
848 matchesASPRegex_: function(toMatch) {
849 var noSpaces = /[a-z]{16}/;
850 var withSpaces = /([a-z]{4}\s){3}[a-z]{4}/;
851 if (toMatch.match(noSpaces) || toMatch.match(withSpaces))
856 setErrorVisibility_: function() {
857 var errormsgDifferentEmail = $('errormsg-different-email');
858 var isErrormsgDifferentEmailHidden = errormsgDifferentEmail.hidden;
859 this.resetErrorVisibility_();
860 var f = $('gaia-login-form');
861 var email = $('gaia-email');
862 var passwd = $('gaia-passwd');
864 $('errormsg-0-email').hidden = false;
865 this.setBlurbError_();
868 // If email is different from last email, and we have not already warned
869 // the user, tell them now. Otherwise proceed as usual. When comparing
870 // email ids, use @gmail.com as the domain if not provided.
871 function normalized_email(id) {
872 return ((id.indexOf('@') != -1) ? id : id + '@gmail.com');
874 if (this.lastEmailAddress_.length > 0 &&
875 normalized_email(email.value) !=
876 normalized_email(this.lastEmailAddress_) &&
877 isErrormsgDifferentEmailHidden) {
878 errormsgDifferentEmail.hidden = false;
881 // Don't enforce password being non-blank when checking access code (it
882 // will have been cleared when the page was displayed).
883 if (!this.allowEmptyPassword_ && !passwd.value) {
884 $('errormsg-0-password').hidden = false;
885 this.setBlurbError_();
889 if (!f.accessCode.disabled && !f.accessCode.value) {
890 $('errormsg-0-access-code').hidden = false;
894 if (f.accessCode.disabled && this.matchesASPRegex_(passwd.value) &&
895 $('asp-warning-div').hidden) {
896 $('asp-warning-div').hidden = false;
897 $('gaia-passwd').value = '';
901 if (!f.otp.disabled && !f.otp.value) {
902 $('errormsg-0-otp').hidden = false;
909 sendCredentialsAndClose_: function() {
910 if (!this.setErrorVisibility_()) {
914 $('gaia-email').disabled = true;
915 $('gaia-passwd').disabled = true;
916 $('captcha-value').disabled = true;
917 $('access-code').disabled = true;
918 $('otp').disabled = true;
920 this.setThrobbersVisible_(true);
922 var f = $('gaia-login-form');
923 var email = $('gaia-email');
924 var passwd = $('gaia-passwd');
925 var result = JSON.stringify({'user': email.value,
926 'pass': passwd.value,
927 'captcha': f.captchaValue.value,
929 'accessCode': f.accessCode.value
931 $('sign-in').disabled = true;
932 chrome.send('SyncSetupSubmitAuth', [result]);
935 showSuccessAndClose_: function() {
936 $('sign-in').value = loadTimeData.getString('loginSuccess');
937 setTimeout(this.closeOverlay_, 1600);
940 showSuccessAndSettingUp_: function() {
941 $('sign-in').value = loadTimeData.getString('settingUp');
942 this.setThrobbersVisible_(true);
943 $('top-blurb-error').hidden = true;
947 * Displays the stop syncing dialog.
950 showStopSyncingUI_: function() {
951 // Hide any visible children of the overlay.
952 var overlay = $('sync-setup-overlay');
953 for (var i = 0; i < overlay.children.length; i++)
954 overlay.children[i].hidden = true;
956 // Bypass OptionsPage.navigateToPage because it will call didShowPage
957 // which will set its own visible page, based on the flow state.
960 $('sync-setup-stop-syncing').hidden = false;
961 $('stop-syncing-cancel').focus();
965 * Steps into the appropriate Sync Setup error UI.
968 showErrorUI_: function() {
969 chrome.send('SyncSetupShowErrorUI');
973 * Determines the appropriate page to show in the Sync Setup UI based on
974 * the state of the Sync backend. Does nothing if the user is not signed in.
977 showSetupUI_: function() {
978 chrome.send('SyncSetupShowSetupUI');
982 * Starts the signin process for the user. Does nothing if the user is
986 startSignIn_: function() {
987 chrome.send('SyncSetupStartSignIn');
991 * Forces user to sign out of Chrome for Chrome OS.
994 doSignOutOnAuthError_: function() {
995 chrome.send('SyncSetupDoSignOutOnAuthError');
999 * Hides the outer elements of the login UI. This is used by the sync promo
1000 * to customize the look of the login box.
1002 hideOuterLoginUI_: function() {
1003 $('sync-setup-overlay-title').hidden = true;
1004 $('sync-setup-cancel').hidden = true;
1008 // These get methods should only be called by the WebUI tests.
1009 SyncSetupOverlay.getLoginEmail = function() {
1010 return SyncSetupOverlay.getInstance().getLoginEmail_();
1013 SyncSetupOverlay.getLoginPasswd = function() {
1014 return SyncSetupOverlay.getInstance().getLoginPasswd_();
1017 SyncSetupOverlay.getSignInButton = function() {
1018 return SyncSetupOverlay.getInstance().getSignInButton_();
1021 // These methods are for general consumption.
1022 SyncSetupOverlay.showErrorUI = function() {
1023 SyncSetupOverlay.getInstance().showErrorUI_();
1026 SyncSetupOverlay.showSetupUI = function() {
1027 SyncSetupOverlay.getInstance().showSetupUI_();
1030 SyncSetupOverlay.startSignIn = function() {
1031 SyncSetupOverlay.getInstance().startSignIn_();
1034 SyncSetupOverlay.doSignOutOnAuthError = function() {
1035 SyncSetupOverlay.getInstance().doSignOutOnAuthError_();
1038 SyncSetupOverlay.showSyncSetupPage = function(page, args) {
1039 SyncSetupOverlay.getInstance().showSyncSetupPage_(page, args);
1042 SyncSetupOverlay.showSuccessAndClose = function() {
1043 SyncSetupOverlay.getInstance().showSuccessAndClose_();
1046 SyncSetupOverlay.showSuccessAndSettingUp = function() {
1047 SyncSetupOverlay.getInstance().showSuccessAndSettingUp_();
1050 SyncSetupOverlay.showStopSyncingUI = function() {
1051 SyncSetupOverlay.getInstance().showStopSyncingUI_();
1056 SyncSetupOverlay: SyncSetupOverlay