Remove sync-related login code on OS_CHROMEOS platforms.
[chromium-blink-merge.git] / chrome / browser / resources / sync_setup_overlay.js
blobe2fd4592336585c2497ed2aae5a14b336ea1a0a6
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
13   // account' link.
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
30   // each other).
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
35   // email address.
36   var lastEmailAddress_ = '';
38   /**
39    * SyncSetupOverlay class
40    * Encapsulated handling of the 'Sync Setup' overlay page.
41    * @class
42    */
43   function SyncSetupOverlay() {
44     OptionsPage.call(this, 'syncSetup',
45                      loadTimeData.getString('syncSetupOverlayTabTitle'),
46                      'sync-setup-overlay');
47   }
49   cr.addSingletonGetter(SyncSetupOverlay);
51   SyncSetupOverlay.prototype = {
52     __proto__: OptionsPage.prototype,
54     /**
55      * Initializes the page.
56      */
57     initializePage: function() {
58       OptionsPage.prototype.initializePage.call(this);
60       var self = this;
61       $('gaia-login-form').onsubmit = function() {
62         self.sendCredentialsAndClose_();
63         return false;
64       };
65       $('google-option').onchange = $('explicit-option').onchange = function() {
66         self.onPassphraseRadioChanged_();
67       };
68       $('basic-encryption-option').onchange =
69           $('full-encryption-option').onchange = function() {
70         self.onEncryptionRadioChanged_();
71       }
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() {
77         self.closeOverlay_();
78       };
79       $('confirm-everything-ok').onclick = function() {
80         self.sendConfiguration_();
81       };
82       $('timeout-ok').onclick = function() {
83         chrome.send('CloseTimeout');
84         self.closeOverlay_();
85       };
86       $('stop-syncing-ok').onclick = function() {
87         chrome.send('SyncSetupStopSyncing');
88         self.closeOverlay_();
89       };
90       $('different-email').innerHTML = loadTimeData.getString('differentEmail');
91     },
93     showOverlay_: function() {
94       OptionsPage.navigateToPage('syncSetup');
95     },
97     closeOverlay_: function() {
98       OptionsPage.closeOverlay();
99     },
101     /** @override */
102     didShowPage: function() {
103       chrome.send('SyncSetupShowSetupUI');
104     },
106     /** @override */
107     didClosePage: function() {
108       chrome.send('SyncSetupDidClosePage');
109     },
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;
116       }
118       return undefined;
119     },
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;
126         }
127       }
129       return undefined;
130     },
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;
136     },
138     onPassphraseRadioChanged_: function() {
139       var visible = this.getPassphraseRadioCheckedValue_() == 'explicit';
140       $('sync-custom-passphrase').hidden = !visible;
141     },
143     onEncryptionRadioChanged_: function() {
144       var visible = $('full-encryption-option').checked;
145       $('sync-custom-passphrase').hidden = !visible;
146     },
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;
155       }
156     },
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;
162       }
163     },
165     setCheckboxesToKeepEverythingSynced_: function(value) {
166       this.setDataTypeCheckboxesEnabled_(!value);
167       if (value)
168         this.checkAllDataTypeCheckboxes_();
169     },
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;
176     },
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)) {
189         return true;
190       }
192       var customPassphrase = $('custom-passphrase');
193       if (customPassphrase.value.length == 0) {
194         emptyError.hidden = false;
195         return false;
196       }
198       var confirmPassphrase = $('confirm-passphrase');
199       if (confirmPassphrase.value != customPassphrase.value) {
200         mismatchError.hidden = false;
201         return false;
202       }
204       return true;
205     },
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;
214         return;
215       }
217       var encryptAllData = this.getEncryptionRadioCheckedValue_() == 'all';
218       if (!encryptAllData &&
219           $('full-encryption-option').checked &&
220           this.keystoreEncryptionEnabled_) {
221         encryptAllData = true;
222       }
224       var usePassphrase;
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_())
245           return;
246         customPassphrase = $('custom-passphrase').value;
247         usePassphrase = true;
248       } else {
249         // The user is not setting a custom passphrase.
250         usePassphrase = false;
251       }
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
275       });
276       chrome.send('SyncSetupConfigure', [result]);
277     },
279     /**
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.
285      * @private
286      */
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;
294       var self = this;
295       this.animateDisableLink_($('customize-link'), disabled, function() {
296         self.showCustomizePage_(null, true);
297       });
298     },
300     /**
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
307      *     enabled.
308      * @private
309      */
310     animateDisableLink_: function(elt, disabled, enabledFunction) {
311       if (disabled) {
312         elt.classList.add('transparent');
313         elt.onclick = null;
314         elt.addEventListener('webkitTransitionEnd', function f(e) {
315           if (e.propertyName != 'opacity')
316             return;
317           elt.removeEventListener('webkitTransitionEnd', f);
318           elt.classList.remove('transparent');
319           elt.hidden = true;
320         });
321       } else {
322         elt.hidden = false;
323         elt.onclick = enabledFunction;
324       }
325     },
327     /**
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.
331      * @private
332      */
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;
344       } else {
345         $('passwords-item').hidden = true;
346       }
347       if (args.autofillRegistered) {
348         $('autofill-checkbox').checked = args.autofillSynced;
349         $('autofill-item').hidden = false;
350       } else {
351         $('autofill-item').hidden = true;
352       }
353       if (args.extensionsRegistered) {
354         $('extensions-checkbox').checked = args.extensionsSynced;
355         $('extensions-item').hidden = false;
356       } else {
357         $('extensions-item').hidden = true;
358       }
359       if (args.typedUrlsRegistered) {
360         $('typed-urls-checkbox').checked = args.typedUrlsSynced;
361         $('omnibox-item').hidden = false;
362       } else {
363         $('omnibox-item').hidden = true;
364       }
365       if (args.appsRegistered) {
366         $('apps-checkbox').checked = args.appsSynced;
367         $('apps-item').hidden = false;
368       } else {
369         $('apps-item').hidden = true;
370       }
371       if (args.tabsRegistered) {
372         $('tabs-checkbox').checked = args.tabsSynced;
373         $('tabs-item').hidden = false;
374       } else {
375         $('tabs-item').hidden = true;
376       }
378       this.setCheckboxesToKeepEverythingSynced_(args.syncAllDataTypes);
379     },
381     setEncryptionRadios_: function(args) {
382       if (args.encryptAllData) {
383         $('encrypt-all-option').checked = true;
384         this.disableEncryptionRadioGroup_();
385       } else {
386         $('encrypt-sensitive-option').checked = true;
387       }
389       if (!args.encryptAllData && !args.usePassphrase) {
390         $('basic-encryption-option').checked = true;
391       } else {
392         $('full-encryption-option').checked = true;
393         $('full-encryption-option').disabled = true;
394         $('basic-encryption-option').disabled = true;
395       }
396     },
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;
406       } else {
407         $('google-option').checked = true;
408       }
409     },
411     setCheckboxesAndErrors_: function(args) {
412       this.setChooseDataTypesCheckboxes_(args);
413       this.setEncryptionRadios_(args);
414       this.setPassphraseRadios_(args);
415     },
417     showConfigure_: function(args) {
418       var datatypeSelect = $('sync-select-datatypes');
419       var self = this;
420       datatypeSelect.onchange = function() {
421         var syncAll = this.selectedIndex == 0;
422         self.setCheckboxesToKeepEverythingSynced_(syncAll);
423       };
425       this.resetPage_('sync-setup-configure');
426       $('sync-setup-configure').hidden = false;
428       // onsubmit is changed when submitting a passphrase. Reset it to its
429       // default.
430       $('choose-data-types-form').onsubmit = function() {
431         self.sendConfiguration_();
432         return false;
433       };
435       if (args) {
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);
448         } else {
449           this.showSyncEverythingPage_();
450         }
451       }
452     },
454     showSpinner_: function() {
455       this.resetPage_('sync-setup-spinner');
456       $('sync-setup-spinner').hidden = false;
457     },
459     showTimeoutPage_: function() {
460       this.resetPage_('sync-setup-timeout');
461       $('sync-setup-timeout').hidden = false;
462     },
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;
484       }
486       if (!this.useEncryptEverything_ && !this.usePassphrase_)
487         $('basic-encryption-option').checked = true;
489       $('confirm-everything-ok').focus();
490     },
492     /**
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
495      * different machine.
496      * @param {Array} args The args that contain the passphrase UI
497      *     configuration.
498      * @private
499      */
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;
517       else
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();
529     },
531     /** @private */
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;
543       } else {
544         $('customize-sync-encryption').hidden = false;
545         $('sync-custom-passphrase-options').hidden = false;
546         $('customize-sync-encryption-new').hidden = true;
547       }
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);
564       } else {
565         // We only show the 'Use Default' link if we're not prompting for an
566         // existing passphrase.
567         var self = this;
568         this.animateDisableLink_($('use-default-link'), false, function() {
569           self.showSyncEverythingPage_();
570         });
571       }
572     },
574     /**
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
578      *     section.
579      */
580     showSyncSetupPage_: function(page, args) {
581       this.setThrobbersVisible_(false);
583       // Hide an existing visible overlay (ensuring the close button is not
584       // hidden).
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;
598       }
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
605       // doesn't work).
606       if (page == 'done')
607         this.closeOverlay_();
608       else
609         this.showOverlay_();
611       if (page == 'login')
612         this.showGaiaLogin_(args);
613       else if (page == 'configure' || page == 'passphrase')
614         this.showConfigure_(args);
615       else if (page == 'spinner')
616         this.showSpinner_();
617       else if (page == 'timeout')
618         this.showTimeoutPage_();
619     },
621     /**
622      * Changes the visibility of throbbers on this page.
623      * @param {boolean} visible Whether or not to set all throbber nodes
624      *     visible.
625      */
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';
630     },
632     /**
633      * Set the appropriate focus on the GAIA login section of the overlay.
634      * @private
635      */
636     loginSetFocus_: function() {
637       var email = this.getLoginEmail_();
638       if (email && !email.value) {
639         email.focus();
640         return;
641       }
643       var passwd = this.getLoginPasswd_();
644       if (passwd)
645         passwd.focus();
646     },
648     /**
649      * Get the login email text input DOM element.
650      * @return {DOMElement} The login email text input.
651      * @private
652      */
653     getLoginEmail_: function() {
654       return $('gaia-email');
655     },
657     /**
658      * Get the login password text input DOM element.
659      * @return {DOMElement} The login password text input.
660      * @private
661      */
662     getLoginPasswd_: function() {
663       return $('gaia-passwd');
664     },
666     /**
667      * Get the sign in button DOM element.
668      * @return {DOMElement} The sign in button.
669      * @private
670      */
671     getSignInButton_: function() {
672       return $('sign-in');
673     },
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();
685     },
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;
696       $('otp').focus();
697     },
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;
707       } else {
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;
713       }
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);
719     },
721     /**
722      * Reset the state of all descendant elements of a root element to their
723      * initial state.
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.
727      * @private
728      */
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++) {
734           fn(arr[i]);
735         }
736       };
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'); });
750     },
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');
763       if (f) {
764         if (args.user != undefined) {
765           if (email.value != args.user)
766             passwd.value = ''; // Reset the password field
767           email.value = args.user;
768         }
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;
776         }
778         f.accessCode.disabled = true;
779         f.otp.disabled = true;
780       }
782       if (1 == args.error) {
783         if (oldAccessCodeValue) {
784           $('errormsg-0-access-code').hidden = false;
785           this.showAccessCodeRequired_();
786         } else {
787           $('errormsg-1-password').hidden = (args.errorMessage != undefined);
788         }
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_();
800         } else {
801           if (oldAccessCodeValue)
802             $('errormsg-0-access-code').hidden = false;
803           this.showAccessCodeRequired_();
804         }
805       } else if (args.errorMessage) {
806         this.setBlurbError_(args.errorMessage);
807       }
809       if (args.fatalError) {
810         $('errormsg-fatal').hidden = false;
811         $('sign-in').disabled = true;
812         return;
813       }
815       $('sign-in').disabled = false;
816       $('sign-in').value = loadTimeData.getString('signin');
817       this.loginSetFocus_();
818     },
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;
828     },
830     setBlurbError_: function(errorMessage) {
831       if (this.captchaChallengeActive_)
832         return;  // No blurb in captcha challenge mode.
834       if (errorMessage) {
835         $('error-signing-in').hidden = true;
836         $('error-custom').hidden = false;
837         $('error-custom').textContent = errorMessage;
838       } else {
839         $('error-signing-in').hidden = false;
840         $('error-custom').hidden = true;
841       }
843       $('top-blurb-error').hidden = false;
844       $('gaia-email').disabled = false;
845       $('gaia-passwd').disabled = false;
846     },
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))
852         return true;
853       return false;
854     },
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');
863       if (!email.value) {
864         $('errormsg-0-email').hidden = false;
865         this.setBlurbError_();
866         return false;
867       }
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');
873       }
874       if (this.lastEmailAddress_.length > 0 &&
875           normalized_email(email.value) !=
876               normalized_email(this.lastEmailAddress_) &&
877           isErrormsgDifferentEmailHidden) {
878         errormsgDifferentEmail.hidden = false;
879         return false;
880       }
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_();
886         return false;
887       }
889       if (!f.accessCode.disabled && !f.accessCode.value) {
890         $('errormsg-0-access-code').hidden = false;
891         return false;
892       }
894       if (f.accessCode.disabled && this.matchesASPRegex_(passwd.value) &&
895           $('asp-warning-div').hidden) {
896         $('asp-warning-div').hidden = false;
897         $('gaia-passwd').value = '';
898         return false;
899       }
901       if (!f.otp.disabled && !f.otp.value) {
902         $('errormsg-0-otp').hidden = false;
903         return false;
904       }
906       return true;
907     },
909     sendCredentialsAndClose_: function() {
910       if (!this.setErrorVisibility_()) {
911         return false;
912       }
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,
928         'otp': f.otp.value,
929         'accessCode': f.accessCode.value
930       });
931       $('sign-in').disabled = true;
932       chrome.send('SyncSetupSubmitAuth', [result]);
933     },
935     showSuccessAndClose_: function() {
936       $('sign-in').value = loadTimeData.getString('loginSuccess');
937       setTimeout(this.closeOverlay_, 1600);
938     },
940     showSuccessAndSettingUp_: function() {
941       $('sign-in').value = loadTimeData.getString('settingUp');
942       this.setThrobbersVisible_(true);
943       $('top-blurb-error').hidden = true;
944     },
946     /**
947      * Displays the stop syncing dialog.
948      * @private
949      */
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.
958       this.visible = true;
960       $('sync-setup-stop-syncing').hidden = false;
961       $('stop-syncing-cancel').focus();
962     },
964     /**
965      * Steps into the appropriate Sync Setup error UI.
966      * @private
967      */
968     showErrorUI_: function() {
969       chrome.send('SyncSetupShowErrorUI');
970     },
972     /**
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.
975      * @private
976      */
977     showSetupUI_: function() {
978       chrome.send('SyncSetupShowSetupUI');
979     },
981     /**
982      * Starts the signin process for the user. Does nothing if the user is
983      * already signed in.
984      * @private
985      */
986     startSignIn_: function() {
987       chrome.send('SyncSetupStartSignIn');
988     },
990     /**
991      * Forces user to sign out of Chrome for Chrome OS.
992      * @private
993      */
994     doSignOutOnAuthError_: function() {
995       chrome.send('SyncSetupDoSignOutOnAuthError');
996     },
998     /**
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.
1001      */
1002     hideOuterLoginUI_: function() {
1003       $('sync-setup-overlay-title').hidden = true;
1004       $('sync-setup-cancel').hidden = true;
1005     }
1006   };
1008   // These get methods should only be called by the WebUI tests.
1009   SyncSetupOverlay.getLoginEmail = function() {
1010     return SyncSetupOverlay.getInstance().getLoginEmail_();
1011   };
1013   SyncSetupOverlay.getLoginPasswd = function() {
1014     return SyncSetupOverlay.getInstance().getLoginPasswd_();
1015   };
1017   SyncSetupOverlay.getSignInButton = function() {
1018     return SyncSetupOverlay.getInstance().getSignInButton_();
1019   };
1021   // These methods are for general consumption.
1022   SyncSetupOverlay.showErrorUI = function() {
1023     SyncSetupOverlay.getInstance().showErrorUI_();
1024   };
1026   SyncSetupOverlay.showSetupUI = function() {
1027     SyncSetupOverlay.getInstance().showSetupUI_();
1028   };
1030   SyncSetupOverlay.startSignIn = function() {
1031     SyncSetupOverlay.getInstance().startSignIn_();
1032   };
1034   SyncSetupOverlay.doSignOutOnAuthError = function() {
1035     SyncSetupOverlay.getInstance().doSignOutOnAuthError_();
1036   };
1038   SyncSetupOverlay.showSyncSetupPage = function(page, args) {
1039     SyncSetupOverlay.getInstance().showSyncSetupPage_(page, args);
1040   };
1042   SyncSetupOverlay.showSuccessAndClose = function() {
1043     SyncSetupOverlay.getInstance().showSuccessAndClose_();
1044   };
1046   SyncSetupOverlay.showSuccessAndSettingUp = function() {
1047     SyncSetupOverlay.getInstance().showSuccessAndSettingUp_();
1048   };
1050   SyncSetupOverlay.showStopSyncingUI = function() {
1051     SyncSetupOverlay.getInstance().showStopSyncingUI_();
1052   };
1054   // Export
1055   return {
1056     SyncSetupOverlay: SyncSetupOverlay
1057   };