- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / options / browser_options.js
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
5 cr.define('options', function() {
6   var OptionsPage = options.OptionsPage;
7   var ArrayDataModel = cr.ui.ArrayDataModel;
8   var RepeatingButton = cr.ui.RepeatingButton;
9
10   //
11   // BrowserOptions class
12   // Encapsulated handling of browser options page.
13   //
14   function BrowserOptions() {
15     OptionsPage.call(this, 'settings', loadTimeData.getString('settingsTitle'),
16                      'settings');
17   }
18
19   cr.addSingletonGetter(BrowserOptions);
20
21   BrowserOptions.prototype = {
22     __proto__: options.OptionsPage.prototype,
23
24     /**
25      * Keeps track of whether the user is signed in or not.
26      * @type {boolean}
27      * @private
28      */
29     signedIn_: false,
30
31     /**
32      * Keeps track of whether |onShowHomeButtonChanged_| has been called. See
33      * |onShowHomeButtonChanged_|.
34      * @type {boolean}
35      * @private
36      */
37     onShowHomeButtonChangedCalled_: false,
38
39     /**
40      * Track if page initialization is complete.  All C++ UI handlers have the
41      * chance to manipulate page content within their InitializePage methods.
42      * This flag is set to true after all initializers have been called.
43      * @type {boolean}
44      * @private
45      */
46     initializationComplete_: false,
47
48     /** @override */
49     initializePage: function() {
50       OptionsPage.prototype.initializePage.call(this);
51       var self = this;
52
53       // Ensure that navigation events are unblocked on uber page. A reload of
54       // the settings page while an overlay is open would otherwise leave uber
55       // page in a blocked state, where tab switching is not possible.
56       uber.invokeMethodOnParent('stopInterceptingEvents');
57
58       window.addEventListener('message', this.handleWindowMessage_.bind(this));
59
60       $('advanced-settings-expander').onclick = function() {
61         self.toggleSectionWithAnimation_(
62             $('advanced-settings'),
63             $('advanced-settings-container'));
64
65         // If the link was focused (i.e., it was activated using the keyboard)
66         // and it was used to show the section (rather than hiding it), focus
67         // the first element in the container.
68         if (document.activeElement === $('advanced-settings-expander') &&
69                 $('advanced-settings').style.height === '') {
70           var focusElement = $('advanced-settings-container').querySelector(
71               'button, input, list, select, a[href]');
72           if (focusElement)
73             focusElement.focus();
74         }
75       }
76
77       $('advanced-settings').addEventListener('webkitTransitionEnd',
78           this.updateAdvancedSettingsExpander_.bind(this));
79
80       if (cr.isChromeOS)
81         UIAccountTweaks.applyGuestModeVisibility(document);
82
83       // Sync (Sign in) section.
84       this.updateSyncState_(loadTimeData.getValue('syncData'));
85
86       $('start-stop-sync').onclick = function(event) {
87         if (self.signedIn_)
88           SyncSetupOverlay.showStopSyncingUI();
89         else if (cr.isChromeOS)
90           SyncSetupOverlay.showSetupUI();
91         else
92           SyncSetupOverlay.startSignIn();
93       };
94       $('customize-sync').onclick = function(event) {
95         SyncSetupOverlay.showSetupUI();
96       };
97
98       // Internet connection section (ChromeOS only).
99       if (cr.isChromeOS) {
100         options.network.NetworkList.decorate($('network-list'));
101         options.network.NetworkList.refreshNetworkData(
102             loadTimeData.getValue('networkData'));
103       }
104
105       // On Startup section.
106       Preferences.getInstance().addEventListener('session.restore_on_startup',
107           this.onRestoreOnStartupChanged_.bind(this));
108       Preferences.getInstance().addEventListener(
109           'session.startup_urls',
110           function(event) {
111             $('startup-set-pages').disabled = event.value.disabled;
112           });
113
114       $('startup-set-pages').onclick = function() {
115         OptionsPage.navigateToPage('startup');
116       };
117
118       // Appearance section.
119       Preferences.getInstance().addEventListener('browser.show_home_button',
120           this.onShowHomeButtonChanged_.bind(this));
121
122       Preferences.getInstance().addEventListener('homepage',
123           this.onHomePageChanged_.bind(this));
124       Preferences.getInstance().addEventListener('homepage_is_newtabpage',
125           this.onHomePageIsNtpChanged_.bind(this));
126
127       $('change-home-page').onclick = function(event) {
128         OptionsPage.navigateToPage('homePageOverlay');
129       };
130
131       if ($('set-wallpaper')) {
132         $('set-wallpaper').onclick = function(event) {
133           chrome.send('openWallpaperManager');
134         };
135       }
136
137       $('themes-gallery').onclick = function(event) {
138         window.open(loadTimeData.getString('themesGalleryURL'));
139       };
140       $('themes-reset').onclick = function(event) {
141         chrome.send('themesReset');
142       };
143
144       if (loadTimeData.getBoolean('profileIsManaged')) {
145         if ($('themes-native-button')) {
146           $('themes-native-button').disabled = true;
147           $('themes-native-button').hidden = true;
148         }
149         // Supervised users have just one default theme, even on Linux. So use
150         // the same button for Linux as for the other platforms.
151         $('themes-reset').textContent = loadTimeData.getString('themesReset');
152       }
153
154       // Device section (ChromeOS only).
155       if (cr.isChromeOS) {
156         $('keyboard-settings-button').onclick = function(evt) {
157           OptionsPage.navigateToPage('keyboard-overlay');
158         };
159         $('pointer-settings-button').onclick = function(evt) {
160           OptionsPage.navigateToPage('pointer-overlay');
161         };
162       }
163
164       // Search section.
165       $('manage-default-search-engines').onclick = function(event) {
166         OptionsPage.navigateToPage('searchEngines');
167         chrome.send('coreOptionsUserMetricsAction',
168                     ['Options_ManageSearchEngines']);
169       };
170       $('default-search-engine').addEventListener('change',
171           this.setDefaultSearchEngine_);
172
173       // Users section.
174       if (loadTimeData.valueExists('profilesInfo')) {
175         $('profiles-section').hidden = false;
176
177         var profilesList = $('profiles-list');
178         options.browser_options.ProfileList.decorate(profilesList);
179         profilesList.autoExpands = true;
180
181         // The profiles info data in |loadTimeData| might be stale.
182         this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
183         chrome.send('requestProfilesInfo');
184
185         profilesList.addEventListener('change',
186             this.setProfileViewButtonsStatus_);
187         $('profiles-create').onclick = function(event) {
188           ManageProfileOverlay.showCreateDialog();
189         };
190         if (OptionsPage.isSettingsApp()) {
191           $('profiles-app-list-switch').onclick = function(event) {
192             var selectedProfile = self.getSelectedProfileItem_();
193             chrome.send('switchAppListProfile', [selectedProfile.filePath]);
194           };
195         }
196         $('profiles-manage').onclick = function(event) {
197           ManageProfileOverlay.showManageDialog();
198         };
199         $('profiles-delete').onclick = function(event) {
200           var selectedProfile = self.getSelectedProfileItem_();
201           if (selectedProfile)
202             ManageProfileOverlay.showDeleteDialog(selectedProfile);
203         };
204         if (loadTimeData.getBoolean('profileIsManaged')) {
205           $('profiles-create').disabled = true;
206           $('profiles-delete').disabled = true;
207           $('profiles-list').canDeleteItems = false;
208         }
209       }
210
211       if (cr.isChromeOS) {
212         if (!UIAccountTweaks.loggedInAsGuest()) {
213           var pictureWrapper = $('account-picture-wrapper');
214           pictureWrapper.setAttribute('role', 'button');
215           pictureWrapper.tabIndex = 0;
216           function activate(event) {
217             if (event.type == 'click' ||
218                 (event.type == 'keydown' && event.keyCode == 32)) {
219               OptionsPage.navigateToPage('changePicture');
220             }
221           };
222           pictureWrapper.onclick = activate;
223           pictureWrapper.addEventListener('keydown', activate);
224
225         }
226
227         // Username (canonical email) of the currently logged in user or
228         // |kGuestUser| if a guest session is active.
229         this.username_ = loadTimeData.getString('username');
230
231         this.updateAccountPicture_();
232
233         $('account-picture-wrapper').oncontextmenu = function(e) {
234           e.preventDefault();
235         };
236
237         $('manage-accounts-button').onclick = function(event) {
238           OptionsPage.navigateToPage('accounts');
239           chrome.send('coreOptionsUserMetricsAction',
240               ['Options_ManageAccounts']);
241         };
242       } else {
243         $('import-data').onclick = function(event) {
244           ImportDataOverlay.show();
245           chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
246         };
247
248         if ($('themes-native-button')) {
249           $('themes-native-button').onclick = function(event) {
250             chrome.send('themesSetNative');
251           };
252         }
253       }
254
255       // Default browser section.
256       if (!cr.isChromeOS) {
257         $('set-as-default-browser').onclick = function(event) {
258           chrome.send('becomeDefaultBrowser');
259         };
260
261         $('auto-launch').onclick = this.handleAutoLaunchChanged_;
262       }
263
264       // Privacy section.
265       $('privacyContentSettingsButton').onclick = function(event) {
266         OptionsPage.navigateToPage('content');
267         OptionsPage.showTab($('cookies-nav-tab'));
268         chrome.send('coreOptionsUserMetricsAction',
269             ['Options_ContentSettings']);
270       };
271       $('privacyClearDataButton').onclick = function(event) {
272         OptionsPage.navigateToPage('clearBrowserData');
273         chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
274       };
275       $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
276       // 'metricsReportingEnabled' element is only present on Chrome branded
277       // builds.
278       if ($('metricsReportingEnabled')) {
279         $('metricsReportingEnabled').onclick = function(event) {
280           chrome.send('metricsReportingCheckboxAction',
281               [String(event.currentTarget.checked)]);
282         };
283       }
284
285       // Bluetooth (CrOS only).
286       if (cr.isChromeOS) {
287         options.system.bluetooth.BluetoothDeviceList.decorate(
288             $('bluetooth-paired-devices-list'));
289
290         $('bluetooth-add-device').onclick =
291             this.handleAddBluetoothDevice_.bind(this);
292
293         $('enable-bluetooth').onchange = function(event) {
294           var state = $('enable-bluetooth').checked;
295           chrome.send('bluetoothEnableChange', [Boolean(state)]);
296         };
297
298         $('bluetooth-reconnect-device').onclick = function(event) {
299           var device = $('bluetooth-paired-devices-list').selectedItem;
300           var address = device.address;
301           chrome.send('updateBluetoothDevice', [address, 'connect']);
302           OptionsPage.closeOverlay();
303         };
304
305         $('bluetooth-paired-devices-list').addEventListener('change',
306             function() {
307           var item = $('bluetooth-paired-devices-list').selectedItem;
308           var disabled = !item || item.connected || !item.connectable;
309           $('bluetooth-reconnect-device').disabled = disabled;
310         });
311       }
312
313       // Passwords and Forms section.
314       $('autofill-settings').onclick = function(event) {
315         OptionsPage.navigateToPage('autofill');
316         chrome.send('coreOptionsUserMetricsAction',
317             ['Options_ShowAutofillSettings']);
318       };
319       $('manage-passwords').onclick = function(event) {
320         OptionsPage.navigateToPage('passwords');
321         OptionsPage.showTab($('passwords-nav-tab'));
322         chrome.send('coreOptionsUserMetricsAction',
323             ['Options_ShowPasswordManager']);
324       };
325       if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
326         // Disable and turn off Autofill in guest mode.
327         var autofillEnabled = $('autofill-enabled');
328         autofillEnabled.disabled = true;
329         autofillEnabled.checked = false;
330         cr.dispatchSimpleEvent(autofillEnabled, 'change');
331         $('autofill-settings').disabled = true;
332
333         // Disable and turn off Password Manager in guest mode.
334         var passwordManagerEnabled = $('password-manager-enabled');
335         passwordManagerEnabled.disabled = true;
336         passwordManagerEnabled.checked = false;
337         cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
338         $('manage-passwords').disabled = true;
339       }
340
341       if (cr.isMac) {
342         $('mac-passwords-warning').hidden =
343             !loadTimeData.getBoolean('multiple_profiles');
344       }
345
346       // Network section.
347       if (!cr.isChromeOS) {
348         $('proxiesConfigureButton').onclick = function(event) {
349           chrome.send('showNetworkProxySettings');
350         };
351       }
352
353       // Web Content section.
354       $('fontSettingsCustomizeFontsButton').onclick = function(event) {
355         OptionsPage.navigateToPage('fonts');
356         chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
357       };
358       $('defaultFontSize').onchange = function(event) {
359         var value = event.target.options[event.target.selectedIndex].value;
360         Preferences.setIntegerPref(
361              'webkit.webprefs.default_fixed_font_size',
362              value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
363         chrome.send('defaultFontSizeAction', [String(value)]);
364       };
365       $('defaultZoomFactor').onchange = function(event) {
366         chrome.send('defaultZoomFactorAction',
367             [String(event.target.options[event.target.selectedIndex].value)]);
368       };
369
370       // Languages section.
371       var showLanguageOptions = function(event) {
372         OptionsPage.navigateToPage('languages');
373         chrome.send('coreOptionsUserMetricsAction',
374             ['Options_LanuageAndSpellCheckSettings']);
375       };
376       $('language-button').onclick = showLanguageOptions;
377       $('manage-languages').onclick = showLanguageOptions;
378
379       // Downloads section.
380       Preferences.getInstance().addEventListener('download.default_directory',
381           this.onDefaultDownloadDirectoryChanged_.bind(this));
382       $('downloadLocationChangeButton').onclick = function(event) {
383         chrome.send('selectDownloadLocation');
384       };
385       if (!cr.isChromeOS) {
386         $('autoOpenFileTypesResetToDefault').onclick = function(event) {
387           chrome.send('autoOpenFileTypesAction');
388         };
389       } else {
390         $('disable-drive-row').hidden =
391             UIAccountTweaks.loggedInAsLocallyManagedUser();
392       }
393
394       // HTTPS/SSL section.
395       if (cr.isWindows || cr.isMac) {
396         $('certificatesManageButton').onclick = function(event) {
397           chrome.send('showManageSSLCertificates');
398         };
399       } else {
400         $('certificatesManageButton').onclick = function(event) {
401           OptionsPage.navigateToPage('certificates');
402           chrome.send('coreOptionsUserMetricsAction',
403                       ['Options_ManageSSLCertificates']);
404         };
405       }
406
407       // Cloud Print section.
408       // 'cloudPrintProxyEnabled' is true for Chrome branded builds on
409       // certain platforms, or could be enabled by a lab.
410       if (!cr.isChromeOS) {
411         $('cloudPrintConnectorSetupButton').onclick = function(event) {
412           if ($('cloudPrintManageButton').style.display == 'none') {
413             // Disable the button, set its text to the intermediate state.
414             $('cloudPrintConnectorSetupButton').textContent =
415               loadTimeData.getString('cloudPrintConnectorEnablingButton');
416             $('cloudPrintConnectorSetupButton').disabled = true;
417             chrome.send('showCloudPrintSetupDialog');
418           } else {
419             chrome.send('disableCloudPrintConnector');
420           }
421         };
422       }
423       $('cloudPrintManageButton').onclick = function(event) {
424         chrome.send('showCloudPrintManagePage');
425       };
426
427       if (loadTimeData.getBoolean('cloudPrintShowMDnsOptions')) {
428         $('cloudprint-options-mdns').hidden = false;
429         $('cloudprint-options-nomdns').hidden = true;
430         $('cloudPrintDevicesPageButton').onclick = function() {
431           chrome.send('showCloudPrintDevicesPage');
432         };
433       }
434
435       // Accessibility section (CrOS only).
436       if (cr.isChromeOS) {
437         $('accessibility-spoken-feedback-check').onchange = function(event) {
438           chrome.send('spokenFeedbackChange',
439                       [$('accessibility-spoken-feedback-check').checked]);
440         };
441
442         $('accessibility-high-contrast-check').onchange = function(event) {
443           chrome.send('highContrastChange',
444                       [$('accessibility-high-contrast-check').checked]);
445         };
446         $('accessibility-sticky-keys').hidden =
447             !loadTimeData.getBoolean('enableStickyKeys');
448       }
449
450       // Display management section (CrOS only).
451       if (cr.isChromeOS) {
452         $('display-options').onclick = function(event) {
453           OptionsPage.navigateToPage('display');
454           chrome.send('coreOptionsUserMetricsAction',
455                       ['Options_Display']);
456         };
457       }
458
459       // Factory reset section (CrOS only).
460       if (cr.isChromeOS) {
461         $('factory-reset-restart').onclick = function(event) {
462           OptionsPage.navigateToPage('factoryResetData');
463         };
464       }
465
466       // System section.
467       if (!cr.isChromeOS) {
468         var updateGpuRestartButton = function() {
469           $('gpu-mode-reset-restart').hidden =
470               loadTimeData.getBoolean('gpuEnabledAtStart') ==
471               $('gpu-mode-checkbox').checked;
472         };
473         Preferences.getInstance().addEventListener(
474             $('gpu-mode-checkbox').getAttribute('pref'),
475             updateGpuRestartButton);
476         $('gpu-mode-reset-restart-button').onclick = function(event) {
477           chrome.send('restartBrowser');
478         };
479         updateGpuRestartButton();
480       }
481
482       // Reset profile settings section.
483       $('reset-profile-settings').onclick = function(event) {
484         OptionsPage.navigateToPage('resetProfileSettings');
485       };
486     },
487
488     /** @override */
489     didShowPage: function() {
490       $('search-field').focus();
491     },
492
493    /**
494     * Called after all C++ UI handlers have called InitializePage to notify
495     * that initialization is complete.
496     * @private
497     */
498     notifyInitializationComplete_: function() {
499       this.initializationComplete_ = true;
500       cr.dispatchSimpleEvent(document, 'initializationComplete');
501     },
502
503     /**
504      * Event listener for the 'session.restore_on_startup' pref.
505      * @param {Event} event The preference change event.
506      * @private
507      */
508     onRestoreOnStartupChanged_: function(event) {
509       /** @const */ var showHomePageValue = 0;
510
511       if (event.value.value == showHomePageValue) {
512         // If the user previously selected "Show the homepage", the
513         // preference will already be migrated to "Open a specific page". So
514         // the only way to reach this code is if the 'restore on startup'
515         // preference is managed.
516         assert(event.value.controlledBy);
517
518         // Select "open the following pages" and lock down the list of URLs
519         // to reflect the intention of the policy.
520         $('startup-show-pages').checked = true;
521         StartupOverlay.getInstance().setControlsDisabled(true);
522       } else {
523         // Re-enable the controls in the startup overlay if necessary.
524         StartupOverlay.getInstance().updateControlStates();
525       }
526     },
527
528     /**
529      * Handler for messages sent from the main uber page.
530      * @param {Event} e The 'message' event from the uber page.
531      * @private
532      */
533     handleWindowMessage_: function(e) {
534       if (e.data.method == 'frameSelected')
535         $('search-field').focus();
536     },
537
538     /**
539      * Shows the given section.
540      * @param {HTMLElement} section The section to be shown.
541      * @param {HTMLElement} container The container for the section. Must be
542      *     inside of |section|.
543      * @param {boolean} animate Indicate if the expansion should be animated.
544      * @private
545      */
546     showSection_: function(section, container, animate) {
547       if (animate)
548         this.addTransitionEndListener_(section);
549
550       // Unhide
551       section.hidden = false;
552
553       var expander = function() {
554         // Reveal the section using a WebKit transition if animating.
555         if (animate) {
556           section.classList.add('sliding');
557           section.style.height = container.offsetHeight + 'px';
558         } else {
559           section.style.height = 'auto';
560         }
561       };
562
563       // Delay starting the transition if animating so that hidden change will
564       // be processed.
565       if (animate)
566         setTimeout(expander, 0);
567       else
568         expander();
569       },
570
571     /**
572      * Shows the given section, with animation.
573      * @param {HTMLElement} section The section to be shown.
574      * @param {HTMLElement} container The container for the section. Must be
575      *     inside of |section|.
576      * @private
577      */
578     showSectionWithAnimation_: function(section, container) {
579       this.showSection_(section, container, /*animate */ true);
580     },
581
582     /**
583      * See showSectionWithAnimation_.
584      */
585     hideSectionWithAnimation_: function(section, container) {
586       this.addTransitionEndListener_(section);
587
588       // Before we start hiding the section, we need to set
589       // the height to a pixel value.
590       section.style.height = container.offsetHeight + 'px';
591
592       // Delay starting the transition so that the height change will be
593       // processed.
594       setTimeout(function() {
595         // Hide the section using a WebKit transition.
596         section.classList.add('sliding');
597         section.style.height = '';
598       }, 0);
599     },
600
601     /**
602      * See showSectionWithAnimation_.
603      */
604     toggleSectionWithAnimation_: function(section, container) {
605       if (section.style.height == '')
606         this.showSectionWithAnimation_(section, container);
607       else
608         this.hideSectionWithAnimation_(section, container);
609     },
610
611     /**
612      * Scrolls the settings page to make the section visible auto-expanding
613      * advanced settings if required.  The transition is not animated.  This
614      * method is used to ensure that a section associated with an overlay
615      * is visible when the overlay is closed.
616      * @param {!Element} section  The section to make visible.
617      * @private
618      */
619     scrollToSection_: function(section) {
620       var advancedSettings = $('advanced-settings');
621       var container = $('advanced-settings-container');
622       if (advancedSettings.hidden && section.parentNode == container) {
623         this.showSection_($('advanced-settings'),
624                           $('advanced-settings-container'),
625                           /* animate */ false);
626         this.updateAdvancedSettingsExpander_();
627       }
628
629       if (!this.initializationComplete_) {
630         var self = this;
631         var callback = function() {
632            document.removeEventListener('initializationComplete', callback);
633            self.scrollToSection_(section);
634         };
635         document.addEventListener('initializationComplete', callback);
636         return;
637       }
638
639       var pageContainer = $('page-container');
640       // pageContainer.offsetTop is relative to the screen.
641       var pageTop = pageContainer.offsetTop;
642       var sectionBottom = section.offsetTop + section.offsetHeight;
643       // section.offsetTop is relative to the 'page-container'.
644       var sectionTop = section.offsetTop;
645       if (pageTop + sectionBottom > document.body.scrollHeight ||
646           pageTop + sectionTop < 0) {
647         pageContainer.oldScrollTop = -pageTop;
648         // Currently not all layout updates are guaranteed to precede the
649         // initializationComplete event (for example 'set-as-default-browser'
650         // button) leaving some uncertainty in the optimal scroll position.
651         // The section is placed approximately in the middle of the screen.
652         pageContainer.style.top = document.body.scrollHeight / 2 -
653             sectionBottom + 'px';
654       }
655     },
656
657     /**
658      * Adds a |webkitTransitionEnd| listener to the given section so that
659      * it can be animated. The listener will only be added to a given section
660      * once, so this can be called as multiple times.
661      * @param {HTMLElement} section The section to be animated.
662      * @private
663      */
664     addTransitionEndListener_: function(section) {
665       if (section.hasTransitionEndListener_)
666         return;
667
668       section.addEventListener('webkitTransitionEnd',
669           this.onTransitionEnd_.bind(this));
670       section.hasTransitionEndListener_ = true;
671     },
672
673     /**
674      * Called after an animation transition has ended.
675      * @private
676      */
677     onTransitionEnd_: function(event) {
678       if (event.propertyName != 'height')
679         return;
680
681       var section = event.target;
682
683       // Disable WebKit transitions.
684       section.classList.remove('sliding');
685
686       if (section.style.height == '') {
687         // Hide the content so it can't get tab focus.
688         section.hidden = true;
689       } else {
690         // Set the section height to 'auto' to allow for size changes
691         // (due to font change or dynamic content).
692         section.style.height = 'auto';
693       }
694     },
695
696     updateAdvancedSettingsExpander_: function() {
697       var expander = $('advanced-settings-expander');
698       if ($('advanced-settings').style.height == '')
699         expander.textContent = loadTimeData.getString('showAdvancedSettings');
700       else
701         expander.textContent = loadTimeData.getString('hideAdvancedSettings');
702     },
703
704     /**
705      * Updates the sync section with the given state.
706      * @param {Object} syncData A bunch of data records that describe the status
707      *     of the sync system.
708      * @private
709      */
710     updateSyncState_: function(syncData) {
711       if (!syncData.signinAllowed &&
712           (!syncData.supervisedUser || !cr.isChromeOS)) {
713         $('sync-section').hidden = true;
714         return;
715       }
716
717       $('sync-section').hidden = false;
718
719       var subSection = $('sync-section').firstChild;
720       while (subSection) {
721         if (subSection.nodeType == Node.ELEMENT_NODE)
722           subSection.hidden = syncData.supervisedUser;
723         subSection = subSection.nextSibling;
724       }
725
726       if (syncData.supervisedUser) {
727         $('account-picture-wrapper').hidden = false;
728         $('sync-general').hidden = false;
729         $('sync-status').hidden = true;
730         return;
731       }
732
733       // If the user gets signed out while the advanced sync settings dialog is
734       // visible, say, due to a dashboard clear, close the dialog.
735       // However, if the user gets signed out as a result of abandoning first
736       // time sync setup, do not call closeOverlay as it will redirect the
737       // browser to the main settings page and override any in-progress
738       // user-initiated navigation. See crbug.com/278030.
739       // Note: SyncSetupOverlay.closeOverlay is a no-op if the overlay is
740       // already hidden.
741       if (this.signedIn_ && !syncData.signedIn && !syncData.setupInProgress)
742         SyncSetupOverlay.closeOverlay();
743
744       this.signedIn_ = syncData.signedIn;
745
746       // Display the "advanced settings" button if we're signed in and sync is
747       // not managed/disabled. If the user is signed in, but sync is disabled,
748       // this button is used to re-enable sync.
749       var customizeSyncButton = $('customize-sync');
750       customizeSyncButton.hidden = !this.signedIn_ ||
751                                    syncData.managed ||
752                                    !syncData.syncSystemEnabled;
753
754       // Only modify the customize button's text if the new text is different.
755       // Otherwise, it can affect search-highlighting in the settings page.
756       // See http://crbug.com/268265.
757       var customizeSyncButtonNewText = syncData.setupCompleted ?
758           loadTimeData.getString('customizeSync') :
759           loadTimeData.getString('syncButtonTextStart');
760       if (customizeSyncButton.textContent != customizeSyncButtonNewText)
761         customizeSyncButton.textContent = customizeSyncButtonNewText;
762
763       // Disable the "sign in" button if we're currently signing in, or if we're
764       // already signed in and signout is not allowed.
765       var signInButton = $('start-stop-sync');
766       signInButton.disabled = syncData.setupInProgress ||
767                               !syncData.signoutAllowed;
768       if (!syncData.signoutAllowed)
769         $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
770       else
771         $('start-stop-sync-indicator').removeAttribute('controlled-by');
772
773       // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome.
774       signInButton.hidden = cr.isChromeOS;
775
776       signInButton.textContent =
777           this.signedIn_ ?
778               loadTimeData.getString('syncButtonTextStop') :
779               syncData.setupInProgress ?
780                   loadTimeData.getString('syncButtonTextInProgress') :
781                   loadTimeData.getString('syncButtonTextSignIn');
782       $('start-stop-sync-indicator').hidden = signInButton.hidden;
783
784       // TODO(estade): can this just be textContent?
785       $('sync-status-text').innerHTML = syncData.statusText;
786       var statusSet = syncData.statusText.length != 0;
787       $('sync-overview').hidden = statusSet;
788       $('sync-status').hidden = !statusSet;
789
790       $('sync-action-link').textContent = syncData.actionLinkText;
791       // Don't show the action link if it is empty or undefined.
792       $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
793       $('sync-action-link').disabled = syncData.managed ||
794                                        !syncData.syncSystemEnabled;
795
796       // On Chrome OS, sign out the user and sign in again to get fresh
797       // credentials on auth errors.
798       $('sync-action-link').onclick = function(event) {
799         if (cr.isChromeOS && syncData.hasError)
800           SyncSetupOverlay.doSignOutOnAuthError();
801         else
802           SyncSetupOverlay.showSetupUI();
803       };
804
805       if (syncData.hasError)
806         $('sync-status').classList.add('sync-error');
807       else
808         $('sync-status').classList.remove('sync-error');
809
810       // Disable the "customize / set up sync" button if sync has an
811       // unrecoverable error. Also disable the button if sync has not been set
812       // up and the user is being presented with a link to re-auth.
813       // See crbug.com/289791.
814       customizeSyncButton.disabled =
815           syncData.hasUnrecoverableError ||
816           (!syncData.setupCompleted && !$('sync-action-link').hidden);
817
818       // Move #enable-auto-login-checkbox to a different location on CrOS.
819       if (cr.isChromeOs) {
820         $('sync-general').insertBefore($('sync-status').nextSibling,
821                                        $('enable-auto-login-checkbox'));
822       }
823       $('enable-auto-login-checkbox').hidden = !syncData.autoLoginVisible;
824     },
825
826     /**
827      * Update the UI depending on whether the current profile manages any
828      * supervised users.
829      * @param {boolean} value True if the current profile manages any supervised
830      *     users.
831      */
832     updateManagesSupervisedUsers_: function(value) {
833       $('profiles-supervised-dashboard-tip').hidden = !value;
834     },
835
836     /**
837      * Get the start/stop sync button DOM element. Used for testing.
838      * @return {DOMElement} The start/stop sync button.
839      * @private
840      */
841     getStartStopSyncButton_: function() {
842       return $('start-stop-sync');
843     },
844
845     /**
846      * Event listener for the 'show home button' preference. Shows/hides the
847      * UI for changing the home page with animation, unless this is the first
848      * time this function is called, in which case there is no animation.
849      * @param {Event} event The preference change event.
850      */
851     onShowHomeButtonChanged_: function(event) {
852       var section = $('change-home-page-section');
853       if (this.onShowHomeButtonChangedCalled_) {
854         var container = $('change-home-page-section-container');
855         if (event.value.value)
856           this.showSectionWithAnimation_(section, container);
857         else
858           this.hideSectionWithAnimation_(section, container);
859       } else {
860         section.hidden = !event.value.value;
861         this.onShowHomeButtonChangedCalled_ = true;
862       }
863     },
864
865     /**
866      * Event listener for the 'homepage is NTP' preference. Updates the label
867      * next to the 'Change' button.
868      * @param {Event} event The preference change event.
869      */
870     onHomePageIsNtpChanged_: function(event) {
871       if (!event.value.uncommitted) {
872         $('home-page-url').hidden = event.value.value;
873         $('home-page-ntp').hidden = !event.value.value;
874       }
875     },
876
877     /**
878      * Event listener for changes to the homepage preference. Updates the label
879      * next to the 'Change' button.
880      * @param {Event} event The preference change event.
881      */
882     onHomePageChanged_: function(event) {
883       if (!event.value.uncommitted)
884         $('home-page-url').textContent = this.stripHttp_(event.value.value);
885     },
886
887     /**
888      * Removes the 'http://' from a URL, like the omnibox does. If the string
889      * doesn't start with 'http://' it is returned unchanged.
890      * @param {string} url The url to be processed
891      * @return {string} The url with the 'http://' removed.
892      */
893     stripHttp_: function(url) {
894       return url.replace(/^http:\/\//, '');
895     },
896
897    /**
898     * Shows the autoLaunch preference and initializes its checkbox value.
899     * @param {bool} enabled Whether autolaunch is enabled or or not.
900     * @private
901     */
902     updateAutoLaunchState_: function(enabled) {
903       $('auto-launch-option').hidden = false;
904       $('auto-launch').checked = enabled;
905     },
906
907     /**
908      * Called when the value of the download.default_directory preference
909      * changes.
910      * @param {Event} event Change event.
911      * @private
912      */
913     onDefaultDownloadDirectoryChanged_: function(event) {
914       $('downloadLocationPath').value = event.value.value;
915       if (cr.isChromeOS) {
916         // On ChromeOS, replace /special/drive/root with Drive for drive paths,
917         // /home/chronos/user/Downloads or /home/chronos/u-<hash>/Downloads
918         // with Downloads for local paths, and '/' with ' \u203a ' (angled quote
919         // sign) everywhere. The modified path is used only for display purpose.
920         var path = $('downloadLocationPath').value;
921         path = path.replace(/^\/special\/drive\/root/, 'Google Drive');
922         path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, '');
923         path = path.replace(/\//g, ' \u203a ');
924         $('downloadLocationPath').value = path;
925       }
926       $('download-location-label').classList.toggle('disabled',
927                                                     event.value.disabled);
928       $('downloadLocationChangeButton').disabled = event.value.disabled;
929     },
930
931     /**
932      * Update the Default Browsers section based on the current state.
933      * @param {string} statusString Description of the current default state.
934      * @param {boolean} isDefault Whether or not the browser is currently
935      *     default.
936      * @param {boolean} canBeDefault Whether or not the browser can be default.
937      * @private
938      */
939     updateDefaultBrowserState_: function(statusString, isDefault,
940                                          canBeDefault) {
941       if (!cr.isChromeOS) {
942         var label = $('default-browser-state');
943         label.textContent = statusString;
944
945         $('set-as-default-browser').hidden = !canBeDefault || isDefault;
946       }
947     },
948
949     /**
950      * Clears the search engine popup.
951      * @private
952      */
953     clearSearchEngines_: function() {
954       $('default-search-engine').textContent = '';
955     },
956
957     /**
958      * Updates the search engine popup with the given entries.
959      * @param {Array} engines List of available search engines.
960      * @param {number} defaultValue The value of the current default engine.
961      * @param {boolean} defaultManaged Whether the default search provider is
962      *     managed. If true, the default search provider can't be changed.
963      * @private
964      */
965     updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
966       this.clearSearchEngines_();
967       engineSelect = $('default-search-engine');
968       engineSelect.disabled = defaultManaged;
969       if (defaultManaged && defaultValue == -1)
970         return;
971       engineCount = engines.length;
972       var defaultIndex = -1;
973       for (var i = 0; i < engineCount; i++) {
974         var engine = engines[i];
975         var option = new Option(engine.name, engine.index);
976         if (defaultValue == option.value)
977           defaultIndex = i;
978         engineSelect.appendChild(option);
979       }
980       if (defaultIndex >= 0)
981         engineSelect.selectedIndex = defaultIndex;
982     },
983
984     /**
985      * Set the default search engine based on the popup selection.
986      * @private
987      */
988     setDefaultSearchEngine_: function() {
989       var engineSelect = $('default-search-engine');
990       var selectedIndex = engineSelect.selectedIndex;
991       if (selectedIndex >= 0) {
992         var selection = engineSelect.options[selectedIndex];
993         chrome.send('setDefaultSearchEngine', [String(selection.value)]);
994       }
995     },
996
997    /**
998      * Sets or clear whether Chrome should Auto-launch on computer startup.
999      * @private
1000      */
1001     handleAutoLaunchChanged_: function() {
1002       chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
1003     },
1004
1005     /**
1006      * Get the selected profile item from the profile list. This also works
1007      * correctly if the list is not displayed.
1008      * @return {Object} the profile item object, or null if nothing is selected.
1009      * @private
1010      */
1011     getSelectedProfileItem_: function() {
1012       var profilesList = $('profiles-list');
1013       if (profilesList.hidden) {
1014         if (profilesList.dataModel.length > 0)
1015           return profilesList.dataModel.item(0);
1016       } else {
1017         return profilesList.selectedItem;
1018       }
1019       return null;
1020     },
1021
1022     /**
1023      * Helper function to set the status of profile view buttons to disabled or
1024      * enabled, depending on the number of profiles and selection status of the
1025      * profiles list.
1026      * @private
1027      */
1028     setProfileViewButtonsStatus_: function() {
1029       var profilesList = $('profiles-list');
1030       var selectedProfile = profilesList.selectedItem;
1031       var hasSelection = selectedProfile != null;
1032       var hasSingleProfile = profilesList.dataModel.length == 1;
1033       var isManaged = loadTimeData.getBoolean('profileIsManaged');
1034       $('profiles-manage').disabled = !hasSelection ||
1035           !selectedProfile.isCurrentProfile;
1036       if (hasSelection && !selectedProfile.isCurrentProfile)
1037         $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1038       else
1039         $('profiles-manage').title = '';
1040       $('profiles-delete').disabled = isManaged ||
1041                                       (!hasSelection && !hasSingleProfile);
1042       if (OptionsPage.isSettingsApp()) {
1043         $('profiles-app-list-switch').disabled = !hasSelection ||
1044             selectedProfile.isCurrentProfile;
1045       }
1046       var importData = $('import-data');
1047       if (importData) {
1048         importData.disabled = $('import-data').disabled = hasSelection &&
1049           !selectedProfile.isCurrentProfile;
1050       }
1051     },
1052
1053     /**
1054      * Display the correct dialog layout, depending on how many profiles are
1055      * available.
1056      * @param {number} numProfiles The number of profiles to display.
1057      * @private
1058      */
1059     setProfileViewSingle_: function(numProfiles) {
1060       var hasSingleProfile = numProfiles == 1;
1061       $('profiles-list').hidden = hasSingleProfile;
1062       $('profiles-single-message').hidden = !hasSingleProfile;
1063       $('profiles-manage').hidden =
1064           hasSingleProfile || OptionsPage.isSettingsApp();
1065       $('profiles-delete').textContent = hasSingleProfile ?
1066           loadTimeData.getString('profilesDeleteSingle') :
1067           loadTimeData.getString('profilesDelete');
1068       if (OptionsPage.isSettingsApp())
1069         $('profiles-app-list-switch').hidden = hasSingleProfile;
1070     },
1071
1072     /**
1073      * Adds all |profiles| to the list.
1074      * @param {Array.<Object>} profiles An array of profile info objects.
1075      *     each object is of the form:
1076      *       profileInfo = {
1077      *         name: "Profile Name",
1078      *         iconURL: "chrome://path/to/icon/image",
1079      *         filePath: "/path/to/profile/data/on/disk",
1080      *         isCurrentProfile: false
1081      *       };
1082      * @private
1083      */
1084     setProfilesInfo_: function(profiles) {
1085       this.setProfileViewSingle_(profiles.length);
1086       // add it to the list, even if the list is hidden so we can access it
1087       // later.
1088       $('profiles-list').dataModel = new ArrayDataModel(profiles);
1089
1090       // Received new data. If showing the "manage" overlay, keep it up to
1091       // date. If showing the "delete" overlay, close it.
1092       if (ManageProfileOverlay.getInstance().visible &&
1093           !$('manage-profile-overlay-manage').hidden) {
1094         ManageProfileOverlay.showManageDialog();
1095       } else {
1096         ManageProfileOverlay.getInstance().visible = false;
1097       }
1098
1099       this.setProfileViewButtonsStatus_();
1100     },
1101
1102     /**
1103      * Reports managed user import errors to the ManagedUserImportOverlay.
1104      * @param {string} error The error message to display.
1105      * @private
1106      */
1107     showManagedUserImportError_: function(error) {
1108       ManagedUserImportOverlay.onError(error);
1109     },
1110
1111     /**
1112      * Reports successful importing of a managed user to
1113      * the ManagedUserImportOverlay.
1114      * @private
1115      */
1116     showManagedUserImportSuccess_: function() {
1117       ManagedUserImportOverlay.onSuccess();
1118     },
1119
1120     /**
1121      * Reports an error to the "create" overlay during profile creation.
1122      * @param {string} error The error message to display.
1123      * @private
1124      */
1125     showCreateProfileError_: function(error) {
1126       CreateProfileOverlay.onError(error);
1127     },
1128
1129     /**
1130     * Sends a warning message to the "create" overlay during profile creation.
1131     * @param {string} warning The warning message to display.
1132     * @private
1133     */
1134     showCreateProfileWarning_: function(warning) {
1135       CreateProfileOverlay.onWarning(warning);
1136     },
1137
1138     /**
1139     * Reports successful profile creation to the "create" overlay.
1140      * @param {Object} profileInfo An object of the form:
1141      *     profileInfo = {
1142      *       name: "Profile Name",
1143      *       filePath: "/path/to/profile/data/on/disk"
1144      *       isManaged: (true|false),
1145      *     };
1146     * @private
1147     */
1148     showCreateProfileSuccess_: function(profileInfo) {
1149       CreateProfileOverlay.onSuccess(profileInfo);
1150     },
1151
1152     /**
1153      * Returns the currently active profile for this browser window.
1154      * @return {Object} A profile info object.
1155      * @private
1156      */
1157     getCurrentProfile_: function() {
1158       for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1159         var profile = $('profiles-list').dataModel.item(i);
1160         if (profile.isCurrentProfile)
1161           return profile;
1162       }
1163
1164       assert(false,
1165              'There should always be a current profile, but none found.');
1166     },
1167
1168     setNativeThemeButtonEnabled_: function(enabled) {
1169       var button = $('themes-native-button');
1170       if (button)
1171         button.disabled = !enabled;
1172     },
1173
1174     setThemesResetButtonEnabled_: function(enabled) {
1175       $('themes-reset').disabled = !enabled;
1176     },
1177
1178     /**
1179      * (Re)loads IMG element with current user account picture.
1180      * @private
1181      */
1182     updateAccountPicture_: function() {
1183       var picture = $('account-picture');
1184       if (picture) {
1185         picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1186             Date.now();
1187       }
1188     },
1189
1190     /**
1191      * Handle the 'add device' button click.
1192      * @private
1193      */
1194     handleAddBluetoothDevice_: function() {
1195       chrome.send('findBluetoothDevices');
1196       OptionsPage.showPageByName('bluetooth', false);
1197     },
1198
1199     /**
1200      * Enables factory reset section.
1201      * @private
1202      */
1203     enableFactoryResetSection_: function() {
1204       $('factory-reset-section').hidden = false;
1205     },
1206
1207     /**
1208      * Set the checked state of the metrics reporting checkbox.
1209      * @private
1210      */
1211     setMetricsReportingCheckboxState_: function(checked, disabled) {
1212       $('metricsReportingEnabled').checked = checked;
1213       $('metricsReportingEnabled').disabled = disabled;
1214     },
1215
1216     /**
1217      * @private
1218      */
1219     setMetricsReportingSettingVisibility_: function(visible) {
1220       if (visible)
1221         $('metricsReportingSetting').style.display = 'block';
1222       else
1223         $('metricsReportingSetting').style.display = 'none';
1224     },
1225
1226     /**
1227      * Set the visibility of the password generation checkbox.
1228      * @private
1229      */
1230     setPasswordGenerationSettingVisibility_: function(visible) {
1231       if (visible)
1232         $('password-generation-checkbox').style.display = 'block';
1233       else
1234         $('password-generation-checkbox').style.display = 'none';
1235     },
1236
1237     /**
1238      * Set the font size selected item. This item actually reflects two
1239      * preferences: the default font size and the default fixed font size.
1240      *
1241      * @param {Object} pref Information about the font size preferences.
1242      * @param {number} pref.value The value of the default font size pref.
1243      * @param {boolean} pref.disabled True if either pref not user modifiable.
1244      * @param {string} pref.controlledBy The source of the pref value(s) if
1245      *     either pref is currently not controlled by the user.
1246      * @private
1247      */
1248     setFontSize_: function(pref) {
1249       var selectCtl = $('defaultFontSize');
1250       selectCtl.disabled = pref.disabled;
1251       // Create a synthetic pref change event decorated as
1252       // CoreOptionsHandler::CreateValueForPref() does.
1253       var event = new Event('synthetic-font-size');
1254       event.value = {
1255         value: pref.value,
1256         controlledBy: pref.controlledBy,
1257         disabled: pref.disabled
1258       };
1259       $('font-size-indicator').handlePrefChange(event);
1260
1261       for (var i = 0; i < selectCtl.options.length; i++) {
1262         if (selectCtl.options[i].value == pref.value) {
1263           selectCtl.selectedIndex = i;
1264           if ($('Custom'))
1265             selectCtl.remove($('Custom').index);
1266           return;
1267         }
1268       }
1269
1270       // Add/Select Custom Option in the font size label list.
1271       if (!$('Custom')) {
1272         var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1273                                 -1, false, true);
1274         option.setAttribute('id', 'Custom');
1275         selectCtl.add(option);
1276       }
1277       $('Custom').selected = true;
1278     },
1279
1280     /**
1281      * Populate the page zoom selector with values received from the caller.
1282      * @param {Array} items An array of items to populate the selector.
1283      *     each object is an array with three elements as follows:
1284      *       0: The title of the item (string).
1285      *       1: The value of the item (number).
1286      *       2: Whether the item should be selected (boolean).
1287      * @private
1288      */
1289     setupPageZoomSelector_: function(items) {
1290       var element = $('defaultZoomFactor');
1291
1292       // Remove any existing content.
1293       element.textContent = '';
1294
1295       // Insert new child nodes into select element.
1296       var value, title, selected;
1297       for (var i = 0; i < items.length; i++) {
1298         title = items[i][0];
1299         value = items[i][1];
1300         selected = items[i][2];
1301         element.appendChild(new Option(title, value, false, selected));
1302       }
1303     },
1304
1305     /**
1306      * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1307      * animation.
1308      * @param {boolean} display Whether to show the button and label or not.
1309      * @private
1310      */
1311     setAutoOpenFileTypesDisplayed_: function(display) {
1312       if (cr.isChromeOS)
1313         return;
1314
1315       if ($('advanced-settings').hidden) {
1316         // If the Advanced section is hidden, don't animate the transition.
1317         $('auto-open-file-types-section').hidden = !display;
1318       } else {
1319         if (display) {
1320           this.showSectionWithAnimation_(
1321               $('auto-open-file-types-section'),
1322               $('auto-open-file-types-container'));
1323         } else {
1324           this.hideSectionWithAnimation_(
1325               $('auto-open-file-types-section'),
1326               $('auto-open-file-types-container'));
1327         }
1328       }
1329     },
1330
1331     /**
1332      * Set the enabled state for the proxy settings button.
1333      * @private
1334      */
1335     setupProxySettingsSection_: function(disabled, extensionControlled) {
1336       if (!cr.isChromeOS) {
1337         $('proxiesConfigureButton').disabled = disabled;
1338         $('proxiesLabel').textContent =
1339             loadTimeData.getString(extensionControlled ?
1340                 'proxiesLabelExtension' : 'proxiesLabelSystem');
1341       }
1342     },
1343
1344     /**
1345      * Set the Cloud Print proxy UI to enabled, disabled, or processing.
1346      * @private
1347      */
1348     setupCloudPrintConnectorSection_: function(disabled, label, allowed) {
1349       if (!cr.isChromeOS) {
1350         $('cloudPrintConnectorLabel').textContent = label;
1351         if (disabled || !allowed) {
1352           $('cloudPrintConnectorSetupButton').textContent =
1353             loadTimeData.getString('cloudPrintConnectorDisabledButton');
1354           $('cloudPrintManageButton').style.display = 'none';
1355         } else {
1356           $('cloudPrintConnectorSetupButton').textContent =
1357             loadTimeData.getString('cloudPrintConnectorEnabledButton');
1358           $('cloudPrintManageButton').style.display = 'inline';
1359         }
1360         $('cloudPrintConnectorSetupButton').disabled = !allowed;
1361       }
1362     },
1363
1364     /**
1365      * @private
1366      */
1367     removeCloudPrintConnectorSection_: function() {
1368      if (!cr.isChromeOS) {
1369         var connectorSectionElm = $('cloud-print-connector-section');
1370         if (connectorSectionElm)
1371           connectorSectionElm.parentNode.removeChild(connectorSectionElm);
1372       }
1373     },
1374
1375     /**
1376      * Set the initial state of the spoken feedback checkbox.
1377      * @private
1378      */
1379     setSpokenFeedbackCheckboxState_: function(checked) {
1380       $('accessibility-spoken-feedback-check').checked = checked;
1381     },
1382
1383     /**
1384      * Set the initial state of the high contrast checkbox.
1385      * @private
1386      */
1387     setHighContrastCheckboxState_: function(checked) {
1388       $('accessibility-high-contrast-check').checked = checked;
1389     },
1390
1391     /**
1392      * Set the initial state of the virtual keyboard checkbox.
1393      * @private
1394      */
1395     setVirtualKeyboardCheckboxState_: function(checked) {
1396       // TODO(zork): Update UI
1397     },
1398
1399     /**
1400      * Show/hide mouse settings slider.
1401      * @private
1402      */
1403     showMouseControls_: function(show) {
1404       $('mouse-settings').hidden = !show;
1405     },
1406
1407     /**
1408      * Show/hide touchpad-related settings.
1409      * @private
1410      */
1411     showTouchpadControls_: function(show) {
1412       $('touchpad-settings').hidden = !show;
1413       $('accessibility-tap-dragging').hidden = !show;
1414     },
1415
1416     /**
1417      * Activate the Bluetooth settings section on the System settings page.
1418      * @private
1419      */
1420     showBluetoothSettings_: function() {
1421       $('bluetooth-devices').hidden = false;
1422     },
1423
1424     /**
1425      * Dectivates the Bluetooth settings section from the System settings page.
1426      * @private
1427      */
1428     hideBluetoothSettings_: function() {
1429       $('bluetooth-devices').hidden = true;
1430     },
1431
1432     /**
1433      * Sets the state of the checkbox indicating if Bluetooth is turned on. The
1434      * state of the "Find devices" button and the list of discovered devices may
1435      * also be affected by a change to the state.
1436      * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
1437      * @private
1438      */
1439     setBluetoothState_: function(checked) {
1440       $('enable-bluetooth').checked = checked;
1441       $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
1442       $('bluetooth-add-device').hidden = !checked;
1443       $('bluetooth-reconnect-device').hidden = !checked;
1444       // Flush list of previously discovered devices if bluetooth is turned off.
1445       if (!checked) {
1446         $('bluetooth-paired-devices-list').clear();
1447         $('bluetooth-unpaired-devices-list').clear();
1448       } else {
1449         chrome.send('getPairedBluetoothDevices');
1450       }
1451     },
1452
1453     /**
1454      * Adds an element to the list of available Bluetooth devices. If an element
1455      * with a matching address is found, the existing element is updated.
1456      * @param {{name: string,
1457      *          address: string,
1458      *          paired: boolean,
1459      *          connected: boolean}} device
1460      *     Decription of the Bluetooth device.
1461      * @private
1462      */
1463     addBluetoothDevice_: function(device) {
1464       var list = $('bluetooth-unpaired-devices-list');
1465       // Display the "connecting" (already paired or not yet paired) and the
1466       // paired devices in the same list.
1467       if (device.paired || device.connecting) {
1468         // Test to see if the device is currently in the unpaired list, in which
1469         // case it should be removed from that list.
1470         var index = $('bluetooth-unpaired-devices-list').find(device.address);
1471         if (index != undefined)
1472           $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1473         list = $('bluetooth-paired-devices-list');
1474       } else {
1475         // Test to see if the device is currently in the paired list, in which
1476         // case it should be removed from that list.
1477         var index = $('bluetooth-paired-devices-list').find(device.address);
1478         if (index != undefined)
1479           $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1480       }
1481       list.appendDevice(device);
1482
1483       // One device can be in the process of pairing.  If found, display
1484       // the Bluetooth pairing overlay.
1485       if (device.pairing)
1486         BluetoothPairing.showDialog(device);
1487     },
1488
1489     /**
1490      * Removes an element from the list of available devices.
1491      * @param {string} address Unique address of the device.
1492      * @private
1493      */
1494     removeBluetoothDevice_: function(address) {
1495       var index = $('bluetooth-unpaired-devices-list').find(address);
1496       if (index != undefined) {
1497         $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1498       } else {
1499         index = $('bluetooth-paired-devices-list').find(address);
1500         if (index != undefined)
1501           $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1502       }
1503     }
1504   };
1505
1506   //Forward public APIs to private implementations.
1507   [
1508     'addBluetoothDevice',
1509     'enableFactoryResetSection',
1510     'getCurrentProfile',
1511     'getStartStopSyncButton',
1512     'hideBluetoothSettings',
1513     'notifyInitializationComplete',
1514     'removeBluetoothDevice',
1515     'removeCloudPrintConnectorSection',
1516     'scrollToSection',
1517     'setAutoOpenFileTypesDisplayed',
1518     'setBluetoothState',
1519     'setFontSize',
1520     'setNativeThemeButtonEnabled',
1521     'setHighContrastCheckboxState',
1522     'setMetricsReportingCheckboxState',
1523     'setMetricsReportingSettingVisibility',
1524     'setPasswordGenerationSettingVisibility',
1525     'setProfilesInfo',
1526     'setSpokenFeedbackCheckboxState',
1527     'setThemesResetButtonEnabled',
1528     'setVirtualKeyboardCheckboxState',
1529     'setupCloudPrintConnectorSection',
1530     'setupPageZoomSelector',
1531     'setupProxySettingsSection',
1532     'showBluetoothSettings',
1533     'showCreateProfileError',
1534     'showCreateProfileSuccess',
1535     'showCreateProfileWarning',
1536     'showManagedUserImportError',
1537     'showManagedUserImportSuccess',
1538     'showMouseControls',
1539     'showTouchpadControls',
1540     'updateAccountPicture',
1541     'updateAutoLaunchState',
1542     'updateDefaultBrowserState',
1543     'updateManagesSupervisedUsers',
1544     'updateSearchEngines',
1545     'updateStartupPages',
1546     'updateSyncState',
1547   ].forEach(function(name) {
1548     BrowserOptions[name] = function() {
1549       var instance = BrowserOptions.getInstance();
1550       return instance[name + '_'].apply(instance, arguments);
1551     };
1552   });
1553
1554   if (cr.isChromeOS) {
1555     /**
1556      * Returns username (canonical email) of the user logged in (ChromeOS only).
1557      * @return {string} user email.
1558      */
1559     // TODO(jhawkins): Investigate the use case for this method.
1560     BrowserOptions.getLoggedInUsername = function() {
1561       return BrowserOptions.getInstance().username_;
1562     };
1563   }
1564
1565   // Export
1566   return {
1567     BrowserOptions: BrowserOptions
1568   };
1569 });