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