sync_setup_overlay.js revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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
5cr.define('options', function() {
6  /** @const */ var OptionsPage = options.OptionsPage;
7
8  // True if the synced account uses a custom passphrase.
9  var usePassphrase_ = false;
10
11  // True if the synced account uses 'encrypt everything'.
12  var useEncryptEverything_ = false;
13
14  // An object used as a cache of the arguments passed in while initially
15  // displaying the advanced sync settings dialog. Used to switch between the
16  // options in the main drop-down menu. Reset when the dialog is closed.
17  var syncConfigureArgs_ = null;
18
19  // A dictionary that maps the sync data type checkbox names to their checked
20  // state. Initialized when the advanced settings dialog is first brought up,
21  // updated any time a box is checked / unchecked, and reset when the dialog is
22  // closed. Used to restore checkbox state while switching between the options
23  // in the main drop-down menu. All checkboxes are checked and disabled when
24  // the "Sync everything" menu-item is selected, and unchecked and disabled
25  // when "Sync nothing" is selected. When "Choose what to sync" is selected,
26  // the boxes are restored to their most recent checked state from this cache.
27  var dataTypeBoxes_ = {};
28
29  /**
30   * The user's selection in the synced data type drop-down menu, as an index.
31   * @enum {number}
32   * @const
33   */
34  var DataTypeSelection = {
35    SYNC_EVERYTHING: 0,
36    CHOOSE_WHAT_TO_SYNC: 1,
37    SYNC_NOTHING: 2
38  };
39
40  /**
41   * SyncSetupOverlay class
42   * Encapsulated handling of the 'Sync Setup' overlay page.
43   * @class
44   */
45  function SyncSetupOverlay() {
46    OptionsPage.call(this, 'syncSetup',
47                     loadTimeData.getString('syncSetupOverlayTabTitle'),
48                     'sync-setup-overlay');
49  }
50
51  cr.addSingletonGetter(SyncSetupOverlay);
52
53  SyncSetupOverlay.prototype = {
54    __proto__: OptionsPage.prototype,
55
56    /**
57     * Initializes the page.
58     */
59    initializePage: function() {
60      OptionsPage.prototype.initializePage.call(this);
61
62      var self = this;
63      $('basic-encryption-option').onchange =
64          $('full-encryption-option').onchange = function() {
65        self.onEncryptionRadioChanged_();
66      }
67      $('choose-datatypes-cancel').onclick =
68          $('confirm-everything-cancel').onclick =
69          $('stop-syncing-cancel').onclick =
70          $('sync-spinner-cancel').onclick = function() {
71        self.closeOverlay_();
72      };
73      $('confirm-everything-ok').onclick = function() {
74        self.sendConfiguration_();
75      };
76      $('timeout-ok').onclick = function() {
77        chrome.send('CloseTimeout');
78        self.closeOverlay_();
79      };
80      $('stop-syncing-ok').onclick = function() {
81        chrome.send('SyncSetupStopSyncing');
82        self.closeOverlay_();
83      };
84    },
85
86    showOverlay_: function() {
87      OptionsPage.navigateToPage('syncSetup');
88    },
89
90    closeOverlay_: function() {
91      this.syncConfigureArgs_ = null;
92      this.dataTypeBoxes_ = {};
93
94      // Required in order to determine whether to give focus to the OK button
95      // or passphrase field. See crbug.com/279770.
96      $('confirm-sync-preferences').hidden = true;
97      $('customize-sync-preferences').hidden = true;
98
99      var overlay = $('sync-setup-overlay');
100      if (!overlay.hidden)
101        OptionsPage.closeOverlay();
102    },
103
104    /** @override */
105    didShowPage: function() {
106      chrome.send('SyncSetupShowSetupUI');
107    },
108
109    /** @override */
110    didClosePage: function() {
111      chrome.send('SyncSetupDidClosePage');
112    },
113
114    onEncryptionRadioChanged_: function() {
115      var visible = $('full-encryption-option').checked;
116      $('sync-custom-passphrase').hidden = !visible;
117    },
118
119    /**
120     * Sets the checked state of the individual sync data type checkboxes in the
121     * advanced sync settings dialog.
122     * @param {boolean} value True for checked, false for unchecked.
123     * @private
124     */
125    checkAllDataTypeCheckboxes_: function(value) {
126      // Only check / uncheck the visible ones (since there's no way to uncheck
127      // / check the invisible ones).
128      var checkboxes = $('choose-data-types-body').querySelectorAll(
129          '.sync-type-checkbox:not([hidden]) input');
130      for (var i = 0; i < checkboxes.length; i++) {
131        checkboxes[i].checked = value;
132      }
133    },
134
135    /**
136     * Restores the checked states of the sync data type checkboxes in the
137     * advanced sync settings dialog. Called when "Choose what to sync" is
138     * selected. Required because all the checkboxes are checked when
139     * "Sync everything" is selected, and unchecked when "Sync nothing" is
140     * selected. Note: We only restore checkboxes for data types that are
141     * actually visible and whose old values are found in the cache, since it's
142     * possible for some data types to not be registered, and therefore, their
143     * checkboxes remain hidden, and never get cached.
144     * @private
145     */
146    restoreDataTypeCheckboxes_: function() {
147      for (dataType in dataTypeBoxes_) {
148        $(dataType).checked = dataTypeBoxes_[dataType];
149      }
150    },
151
152    /**
153     * Enables / grays out the sync data type checkboxes in the advanced
154     * settings dialog.
155     * @param {boolean} enabled True for enabled, false for grayed out.
156     * @private
157     */
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    },
164
165    /**
166     * Sets the state of the sync data type checkboxes based on whether "Sync
167     * everything", "Choose what to sync", or "Sync nothing" are selected in the
168     * drop-down menu of the advanced settings dialog.
169     * @param {cr.DataTypeSelection} selectedIndex Index of user's selection.
170     * @private
171     */
172    setDataTypeCheckboxes_: function(selectedIndex) {
173      if (selectedIndex == DataTypeSelection.CHOOSE_WHAT_TO_SYNC) {
174        this.setDataTypeCheckboxesEnabled_(true);
175        this.restoreDataTypeCheckboxes_();
176      } else {
177        this.setDataTypeCheckboxesEnabled_(false);
178        this.checkAllDataTypeCheckboxes_(selectedIndex ==
179                                         DataTypeSelection.SYNC_EVERYTHING);
180      }
181    },
182
183    // Returns true if none of the visible checkboxes are checked.
184    noDataTypesChecked_: function() {
185      var query = '.sync-type-checkbox:not([hidden]) input:checked';
186      var checkboxes = $('choose-data-types-body').querySelectorAll(query);
187      return checkboxes.length == 0;
188    },
189
190    checkPassphraseMatch_: function() {
191      var emptyError = $('empty-error');
192      var mismatchError = $('mismatch-error');
193      emptyError.hidden = true;
194      mismatchError.hidden = true;
195
196      var f = $('choose-data-types-form');
197      if (!$('full-encryption-option').checked ||
198           $('basic-encryption-option').disabled) {
199        return true;
200      }
201
202      var customPassphrase = $('custom-passphrase');
203      if (customPassphrase.value.length == 0) {
204        emptyError.hidden = false;
205        return false;
206      }
207
208      var confirmPassphrase = $('confirm-passphrase');
209      if (confirmPassphrase.value != customPassphrase.value) {
210        mismatchError.hidden = false;
211        return false;
212      }
213
214      return true;
215    },
216
217    sendConfiguration_: function() {
218      // Trying to submit, so hide previous errors.
219      $('error-text').hidden = true;
220
221      var chooseWhatToSync = $('sync-select-datatypes').selectedIndex ==
222                             DataTypeSelection.CHOOSE_WHAT_TO_SYNC;
223      if (chooseWhatToSync && this.noDataTypesChecked_()) {
224        $('error-text').hidden = false;
225        return;
226      }
227
228      var encryptAllData = $('full-encryption-option').checked;
229
230      var usePassphrase;
231      var customPassphrase;
232      var googlePassphrase = false;
233      if (!$('sync-existing-passphrase-container').hidden) {
234        // If we were prompted for an existing passphrase, use it.
235        customPassphrase = $('choose-data-types-form').passphrase.value;
236        usePassphrase = true;
237        // If we were displaying the 'enter your old google password' prompt,
238        // then that means this is the user's google password.
239        googlePassphrase = !$('google-passphrase-needed-body').hidden;
240        // We allow an empty passphrase, in case the user has disabled
241        // all their encrypted datatypes. In that case, the PSS will accept
242        // the passphrase and finish configuration. If the user has enabled
243        // encrypted datatypes, the PSS will prompt again specifying that the
244        // passphrase failed.
245      } else if (!$('basic-encryption-option').disabled &&
246                  $('full-encryption-option').checked) {
247        // The user is setting a custom passphrase for the first time.
248        if (!this.checkPassphraseMatch_())
249          return;
250        customPassphrase = $('custom-passphrase').value;
251        usePassphrase = true;
252      } else {
253        // The user is not setting a custom passphrase.
254        usePassphrase = false;
255      }
256
257      // Don't allow the user to tweak the settings once we send the
258      // configuration to the backend.
259      this.setInputElementsDisabledState_(true);
260      $('use-default-link').hidden = true;
261      $('use-default-link').disabled = true;
262      $('use-default-link').onclick = null;
263
264      // These values need to be kept in sync with where they are read in
265      // SyncSetupFlow::GetDataTypeChoiceData().
266      var syncAll = $('sync-select-datatypes').selectedIndex ==
267                    DataTypeSelection.SYNC_EVERYTHING;
268      var syncNothing = $('sync-select-datatypes').selectedIndex ==
269                        DataTypeSelection.SYNC_NOTHING;
270      var result = JSON.stringify({
271        'syncAllDataTypes': syncAll,
272        'syncNothing': syncNothing,
273        'bookmarksSynced': syncAll || $('bookmarks-checkbox').checked,
274        'preferencesSynced': syncAll || $('preferences-checkbox').checked,
275        'themesSynced': syncAll || $('themes-checkbox').checked,
276        'passwordsSynced': syncAll || $('passwords-checkbox').checked,
277        'autofillSynced': syncAll || $('autofill-checkbox').checked,
278        'extensionsSynced': syncAll || $('extensions-checkbox').checked,
279        'typedUrlsSynced': syncAll || $('typed-urls-checkbox').checked,
280        'appsSynced': syncAll || $('apps-checkbox').checked,
281        'tabsSynced': syncAll || $('tabs-checkbox').checked,
282        'encryptAllData': encryptAllData,
283        'usePassphrase': usePassphrase,
284        'isGooglePassphrase': googlePassphrase,
285        'passphrase': customPassphrase
286      });
287      chrome.send('SyncSetupConfigure', [result]);
288    },
289
290    /**
291     * Sets the disabled property of all input elements within the 'Customize
292     * Sync Preferences' screen. This is used to prohibit the user from changing
293     * the inputs after confirming the customized sync preferences, or resetting
294     * the state when re-showing the dialog.
295     * @param {boolean} disabled True if controls should be set to disabled.
296     * @private
297     */
298    setInputElementsDisabledState_: function(disabled) {
299      var configureElements =
300          $('customize-sync-preferences').querySelectorAll('input');
301      for (var i = 0; i < configureElements.length; i++)
302        configureElements[i].disabled = disabled;
303      $('sync-select-datatypes').disabled = disabled;
304
305      $('customize-link').hidden = disabled;
306      $('customize-link').disabled = disabled;
307      $('customize-link').onclick = (disabled ? null : function() {
308        SyncSetupOverlay.showCustomizePage(null,
309                                           DataTypeSelection.SYNC_EVERYTHING);
310        return false;
311      });
312    },
313
314    /**
315     * Shows or hides the sync data type checkboxes in the advanced sync
316     * settings dialog. Also initializes |dataTypeBoxes_| with their values, and
317     * makes their onclick handlers update |dataTypeBoxes_|.
318     * @param {Object} args The configuration data used to show/hide UI.
319     * @private
320     */
321    setChooseDataTypesCheckboxes_: function(args) {
322      var datatypeSelect = $('sync-select-datatypes');
323      datatypeSelect.selectedIndex = args.syncAllDataTypes ?
324                                         DataTypeSelection.SYNC_EVERYTHING :
325                                         DataTypeSelection.CHOOSE_WHAT_TO_SYNC;
326
327      $('bookmarks-checkbox').checked = args.bookmarksSynced;
328      dataTypeBoxes_['bookmarks-checkbox'] = args.bookmarksSynced;
329      $('bookmarks-checkbox').onclick = this.handleDataTypeClick_;
330
331      $('preferences-checkbox').checked = args.preferencesSynced;
332      dataTypeBoxes_['preferences-checkbox'] = args.preferencesSynced;
333      $('preferences-checkbox').onclick = this.handleDataTypeClick_;
334
335      $('themes-checkbox').checked = args.themesSynced;
336      dataTypeBoxes_['themes-checkbox'] = args.themesSynced;
337      $('themes-checkbox').onclick = this.handleDataTypeClick_;
338
339      if (args.passwordsRegistered) {
340        $('passwords-checkbox').checked = args.passwordsSynced;
341        dataTypeBoxes_['passwords-checkbox'] = args.passwordsSynced;
342        $('passwords-checkbox').onclick = this.handleDataTypeClick_;
343        $('passwords-item').hidden = false;
344      } else {
345        $('passwords-item').hidden = true;
346      }
347      if (args.autofillRegistered) {
348        $('autofill-checkbox').checked = args.autofillSynced;
349        dataTypeBoxes_['autofill-checkbox'] = args.autofillSynced;
350        $('autofill-checkbox').onclick = this.handleDataTypeClick_;
351        $('autofill-item').hidden = false;
352      } else {
353        $('autofill-item').hidden = true;
354      }
355      if (args.extensionsRegistered) {
356        $('extensions-checkbox').checked = args.extensionsSynced;
357        dataTypeBoxes_['extensions-checkbox'] = args.extensionsSynced;
358        $('extensions-checkbox').onclick = this.handleDataTypeClick_;
359        $('extensions-item').hidden = false;
360      } else {
361        $('extensions-item').hidden = true;
362      }
363      if (args.typedUrlsRegistered) {
364        $('typed-urls-checkbox').checked = args.typedUrlsSynced;
365        dataTypeBoxes_['typed-urls-checkbox'] = args.typedUrlsSynced;
366        $('typed-urls-checkbox').onclick = this.handleDataTypeClick_;
367        $('omnibox-item').hidden = false;
368      } else {
369        $('omnibox-item').hidden = true;
370      }
371      if (args.appsRegistered) {
372        $('apps-checkbox').checked = args.appsSynced;
373        dataTypeBoxes_['apps-checkbox'] = args.appsSynced;
374        $('apps-checkbox').onclick = this.handleDataTypeClick_;
375        $('apps-item').hidden = false;
376      } else {
377        $('apps-item').hidden = true;
378      }
379      if (args.tabsRegistered) {
380        $('tabs-checkbox').checked = args.tabsSynced;
381        dataTypeBoxes_['tabs-checkbox'] = args.tabsSynced;
382        $('tabs-checkbox').onclick = this.handleDataTypeClick_;
383        $('tabs-item').hidden = false;
384      } else {
385        $('tabs-item').hidden = true;
386      }
387
388      this.setDataTypeCheckboxes_(datatypeSelect.selectedIndex);
389    },
390
391    /**
392     * Updates the cached values of the sync data type checkboxes stored in
393     * |dataTypeBoxes_|. Used as an onclick handler for each data type checkbox.
394     * @private
395     */
396    handleDataTypeClick_: function() {
397      dataTypeBoxes_[this.id] = this.checked;
398    },
399
400    setEncryptionRadios_: function(args) {
401      if (!args.encryptAllData && !args.usePassphrase) {
402        $('basic-encryption-option').checked = true;
403      } else {
404        $('full-encryption-option').checked = true;
405        $('full-encryption-option').disabled = true;
406        $('basic-encryption-option').disabled = true;
407      }
408    },
409
410    setCheckboxesAndErrors_: function(args) {
411      this.setChooseDataTypesCheckboxes_(args);
412      this.setEncryptionRadios_(args);
413    },
414
415    showConfigure_: function(args) {
416      var datatypeSelect = $('sync-select-datatypes');
417      var self = this;
418
419      // Cache the sync config args so they can be reused when we transition
420      // between the drop-down menu items in the advanced settings dialog.
421      if (args)
422        this.syncConfigureArgs_ = args;
423
424      // Once the advanced sync settings dialog is visible, we transition
425      // between its drop-down menu items as follows:
426      // "Sync everything": Show encryption and passphrase sections, and disable
427      // and check all data type checkboxes.
428      // "Sync nothing": Hide encryption and passphrase sections, and disable
429      // and uncheck all data type checkboxes.
430      // "Choose what to sync": Show encryption and passphrase sections, enable
431      // data type checkboxes, and restore their checked state to the last time
432      // the "Choose what to sync" was selected while the dialog was still up.
433      datatypeSelect.onchange = function() {
434        if (this.selectedIndex == DataTypeSelection.SYNC_NOTHING) {
435          self.showSyncNothingPage_();
436        } else {
437          self.showCustomizePage_(self.syncConfigureArgs_, this.selectedIndex);
438          if (this.selectedIndex == DataTypeSelection.SYNC_EVERYTHING)
439            self.checkAllDataTypeCheckboxes_(true);
440          else
441            self.restoreDataTypeCheckboxes_();
442        }
443      };
444
445      this.resetPage_('sync-setup-configure');
446      $('sync-setup-configure').hidden = false;
447
448      // onsubmit is changed when submitting a passphrase. Reset it to its
449      // default.
450      $('choose-data-types-form').onsubmit = function() {
451        self.sendConfiguration_();
452        return false;
453      };
454
455      if (args) {
456        this.setCheckboxesAndErrors_(args);
457
458        this.useEncryptEverything_ = args.encryptAllData;
459
460        // Determine whether to display the 'OK, sync everything' confirmation
461        // dialog or the advanced sync settings dialog, and assign focus to the
462        // OK button, or to the passphrase field if a passphrase is required.
463        this.usePassphrase_ = args.usePassphrase;
464        if (args.showSyncEverythingPage == false || this.usePassphrase_ ||
465            args.syncAllDataTypes == false || args.showPassphrase) {
466          var index = args.syncAllDataTypes ?
467                          DataTypeSelection.SYNC_EVERYTHING :
468                          DataTypeSelection.CHOOSE_WHAT_TO_SYNC;
469          this.showCustomizePage_(args, index);
470        } else {
471          this.showSyncEverythingPage_();
472        }
473      }
474    },
475
476    showSpinner_: function() {
477      this.resetPage_('sync-setup-spinner');
478      $('sync-setup-spinner').hidden = false;
479    },
480
481    showTimeoutPage_: function() {
482      this.resetPage_('sync-setup-timeout');
483      $('sync-setup-timeout').hidden = false;
484    },
485
486    showSyncEverythingPage_: function() {
487      // Determine whether to bring the OK button into focus.
488      var wasConfirmPageHidden = $('confirm-sync-preferences').hidden;
489
490      $('confirm-sync-preferences').hidden = false;
491      $('customize-sync-preferences').hidden = true;
492
493      // Reset the selection to 'Sync everything'.
494      $('sync-select-datatypes').selectedIndex = 0;
495
496      // The default state is to sync everything.
497      this.setDataTypeCheckboxes_(DataTypeSelection.SYNC_EVERYTHING);
498
499      if (!this.usePassphrase_)
500        $('sync-custom-passphrase').hidden = true;
501
502      if (!this.useEncryptEverything_ && !this.usePassphrase_)
503        $('basic-encryption-option').checked = true;
504
505      // Give the OK button focus only when the dialog wasn't already visible.
506      if (wasConfirmPageHidden)
507        $('confirm-everything-ok').focus();
508    },
509
510    /**
511     * Reveals the UI for when the user chooses not to sync any data types.
512     * This happens when the user signs in and selects "Sync nothing" in the
513     * advanced sync settings dialog.
514     * @private
515     */
516    showSyncNothingPage_: function() {
517      // Reset the selection to 'Sync nothing'.
518      $('sync-select-datatypes').selectedIndex = DataTypeSelection.SYNC_NOTHING;
519
520      // Uncheck and disable the individual data type checkboxes.
521      this.checkAllDataTypeCheckboxes_(false);
522      this.setDataTypeCheckboxesEnabled_(false);
523
524      // Hide the encryption section.
525      $('customize-sync-encryption-new').hidden = true;
526      $('sync-custom-passphrase-container').hidden = true;
527      $('sync-existing-passphrase-container').hidden = true;
528
529      // Hide the "use default settings" link.
530      $('use-default-link').hidden = true;
531      $('use-default-link').disabled = true;
532      $('use-default-link').onclick = null;
533    },
534
535    /**
536     * Reveals the UI for entering a custom passphrase during initial setup.
537     * This happens if the user has previously enabled a custom passphrase on a
538     * different machine.
539     * @param {Array} args The args that contain the passphrase UI
540     *     configuration.
541     * @private
542     */
543    showPassphraseContainer_: function(args) {
544      // Once we require a passphrase, we prevent the user from returning to
545      // the Sync Everything pane.
546      $('use-default-link').hidden = true;
547      $('use-default-link').disabled = true;
548      $('use-default-link').onclick = null;
549      $('sync-custom-passphrase-container').hidden = true;
550      $('sync-existing-passphrase-container').hidden = false;
551
552      // Hide the selection options within the new encryption section when
553      // prompting for a passphrase.
554      $('sync-new-encryption-section-container').hidden = true;
555
556      $('normal-body').hidden = true;
557      $('google-passphrase-needed-body').hidden = true;
558      // Display the correct prompt to the user depending on what type of
559      // passphrase is needed.
560      if (args.usePassphrase)
561        $('normal-body').hidden = false;
562      else
563        $('google-passphrase-needed-body').hidden = false;
564
565      $('passphrase-learn-more').hidden = false;
566      // Warn the user about their incorrect passphrase if we need a passphrase
567      // and the passphrase field is non-empty (meaning they tried to set it
568      // previously but failed).
569      $('incorrect-passphrase').hidden =
570          !(args.usePassphrase && args.passphraseFailed);
571
572      $('sync-passphrase-warning').hidden = false;
573    },
574
575    /**
576     * Displays the advanced sync setting dialog, and pre-selects either the
577     * "Sync everything" or the "Choose what to sync" drop-down menu item.
578     * @param {cr.DataTypeSelection} index Index of item to pre-select.
579     * @private
580     */
581    showCustomizePage_: function(args, index) {
582      // Determine whether to bring the OK button field into focus.
583      var wasCustomizePageHidden = $('customize-sync-preferences').hidden;
584
585      $('confirm-sync-preferences').hidden = true;
586      $('customize-sync-preferences').hidden = false;
587
588      $('sync-custom-passphrase-container').hidden = false;
589      $('sync-new-encryption-section-container').hidden = false;
590      $('customize-sync-encryption-new').hidden = false;
591
592      $('sync-existing-passphrase-container').hidden = true;
593
594      $('sync-select-datatypes').selectedIndex = index;
595      this.setDataTypeCheckboxesEnabled_(
596          index == DataTypeSelection.CHOOSE_WHAT_TO_SYNC);
597
598      // Give the OK button focus only when the dialog wasn't already visible.
599      if (wasCustomizePageHidden)
600        $('choose-datatypes-ok').focus();
601
602      if (args && args.showPassphrase) {
603        this.showPassphraseContainer_(args);
604        // Give the passphrase field focus only when the dialog wasn't already
605        // visible.
606        if (wasCustomizePageHidden)
607          $('passphrase').focus();
608      } else {
609        // We only show the 'Use Default' link if we're not prompting for an
610        // existing passphrase.
611        $('use-default-link').hidden = false;
612        $('use-default-link').disabled = false;
613        $('use-default-link').onclick = function() {
614          SyncSetupOverlay.showSyncEverythingPage();
615          return false;
616        };
617      }
618    },
619
620    /**
621     * Shows the appropriate sync setup page.
622     * @param {string} page A page of the sync setup to show.
623     * @param {object} args Data from the C++ to forward on to the right
624     *     section.
625     */
626    showSyncSetupPage_: function(page, args) {
627      this.setThrobbersVisible_(false);
628
629      // Hide an existing visible overlay (ensuring the close button is not
630      // hidden).
631      var children = document.querySelectorAll(
632          '#sync-setup-overlay > *:not(.close-button)');
633      for (var i = 0; i < children.length; i++)
634        children[i].hidden = true;
635
636      this.setInputElementsDisabledState_(false);
637
638      // If new passphrase bodies are present, overwrite the existing ones.
639      if (args && args.enterPassphraseBody != undefined)
640        $('normal-body').innerHTML = args.enterPassphraseBody;
641      if (args && args.enterGooglePassphraseBody != undefined) {
642        $('google-passphrase-needed-body').innerHTML =
643            args.enterGooglePassphraseBody;
644      }
645      if (args && args.fullEncryptionBody != undefined)
646        $('full-encryption-body').innerHTML = args.fullEncryptionBody;
647
648      // NOTE: Because both showGaiaLogin_() and showConfigure_() change the
649      // focus, we need to ensure that the overlay container and dialog aren't
650      // [hidden] (as trying to focus() nodes inside of a [hidden] DOM section
651      // doesn't work).
652      if (page == 'done')
653        this.closeOverlay_();
654      else
655        this.showOverlay_();
656
657      if (page == 'configure' || page == 'passphrase')
658        this.showConfigure_(args);
659      else if (page == 'spinner')
660        this.showSpinner_();
661      else if (page == 'timeout')
662        this.showTimeoutPage_();
663    },
664
665    /**
666     * Changes the visibility of throbbers on this page.
667     * @param {boolean} visible Whether or not to set all throbber nodes
668     *     visible.
669     */
670    setThrobbersVisible_: function(visible) {
671      var throbbers = this.pageDiv.getElementsByClassName('throbber');
672      for (var i = 0; i < throbbers.length; i++)
673        throbbers[i].style.visibility = visible ? 'visible' : 'hidden';
674    },
675
676    /**
677     * Reset the state of all descendant elements of a root element to their
678     * initial state.
679     * The initial state is specified by adding a class to the descendant
680     * element in sync_setup_overlay.html.
681     * @param {HTMLElement} pageElementId The root page element id.
682     * @private
683     */
684    resetPage_: function(pageElementId) {
685      var page = $(pageElementId);
686      var forEach = function(arr, fn) {
687        var length = arr.length;
688        for (var i = 0; i < length; i++) {
689          fn(arr[i]);
690        }
691      };
692
693      forEach(page.getElementsByClassName('reset-hidden'),
694          function(elt) { elt.hidden = true; });
695      forEach(page.getElementsByClassName('reset-shown'),
696          function(elt) { elt.hidden = false; });
697      forEach(page.getElementsByClassName('reset-disabled'),
698          function(elt) { elt.disabled = true; });
699      forEach(page.getElementsByClassName('reset-enabled'),
700          function(elt) { elt.disabled = false; });
701      forEach(page.getElementsByClassName('reset-value'),
702          function(elt) { elt.value = ''; });
703      forEach(page.getElementsByClassName('reset-opaque'),
704          function(elt) { elt.classList.remove('transparent'); });
705    },
706
707    /**
708     * Displays the stop syncing dialog.
709     * @private
710     */
711    showStopSyncingUI_: function() {
712      // Hide any visible children of the overlay.
713      var overlay = $('sync-setup-overlay');
714      for (var i = 0; i < overlay.children.length; i++)
715        overlay.children[i].hidden = true;
716
717      // Bypass OptionsPage.navigateToPage because it will call didShowPage
718      // which will set its own visible page, based on the flow state.
719      this.visible = true;
720
721      $('sync-setup-stop-syncing').hidden = false;
722      $('stop-syncing-cancel').focus();
723    },
724
725    /**
726     * Determines the appropriate page to show in the Sync Setup UI based on
727     * the state of the Sync backend. Does nothing if the user is not signed in.
728     * @private
729     */
730    showSetupUI_: function() {
731      chrome.send('SyncSetupShowSetupUI');
732    },
733
734    /**
735     * Starts the signin process for the user. Does nothing if the user is
736     * already signed in.
737     * @private
738     */
739    startSignIn_: function() {
740      chrome.send('SyncSetupStartSignIn');
741    },
742
743    /**
744     * Forces user to sign out of Chrome for Chrome OS.
745     * @private
746     */
747    doSignOutOnAuthError_: function() {
748      chrome.send('SyncSetupDoSignOutOnAuthError');
749    },
750  };
751
752  // These methods are for general consumption.
753  SyncSetupOverlay.closeOverlay = function() {
754    SyncSetupOverlay.getInstance().closeOverlay_();
755  };
756
757  SyncSetupOverlay.showSetupUI = function() {
758    SyncSetupOverlay.getInstance().showSetupUI_();
759  };
760
761  SyncSetupOverlay.startSignIn = function() {
762    SyncSetupOverlay.getInstance().startSignIn_();
763  };
764
765  SyncSetupOverlay.doSignOutOnAuthError = function() {
766    SyncSetupOverlay.getInstance().doSignOutOnAuthError_();
767  };
768
769  SyncSetupOverlay.showSyncSetupPage = function(page, args) {
770    SyncSetupOverlay.getInstance().showSyncSetupPage_(page, args);
771  };
772
773  SyncSetupOverlay.showCustomizePage = function(args, index) {
774    SyncSetupOverlay.getInstance().showCustomizePage_(args, index);
775  };
776
777  SyncSetupOverlay.showSyncEverythingPage = function() {
778    SyncSetupOverlay.getInstance().showSyncEverythingPage_();
779  };
780
781  SyncSetupOverlay.showStopSyncingUI = function() {
782    SyncSetupOverlay.getInstance().showStopSyncingUI_();
783  };
784
785  // Export
786  return {
787    SyncSetupOverlay: SyncSetupOverlay
788  };
789});
790