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