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