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