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