Extract code handling PrinterProviderAPI from PrintPreviewHandler
[chromium-blink-merge.git] / chrome / browser / resources / options / browser_options.js
blobe0d7865a891d343c9c50734be47ab3ddaed687b1
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.exportPath('options');
7 /**
8  * @typedef {{actionLinkText: (string|undefined),
9  *            childUser: (boolean|undefined),
10  *            hasError: (boolean|undefined),
11  *            hasUnrecoverableError: (boolean|undefined),
12  *            managed: (boolean|undefined),
13  *            setupCompleted: (boolean|undefined),
14  *            setupInProgress: (boolean|undefined),
15  *            signedIn: (boolean|undefined),
16  *            signinAllowed: (boolean|undefined),
17  *            signoutAllowed: (boolean|undefined),
18  *            statusText: (string|undefined),
19  *            supervisedUser: (boolean|undefined),
20  *            syncSystemEnabled: (boolean|undefined)}}
21  * @see chrome/browser/ui/webui/options/browser_options_handler.cc
22  */
23 options.SyncStatus;
25 /**
26  * @typedef {{id: string, name: string}}
27  */
28 options.ExtensionData;
30 /**
31  * @typedef {{name: string,
32  *            filePath: string,
33  *            isCurrentProfile: boolean,
34  *            isSupervised: boolean,
35  *            isChild: boolean,
36  *            iconUrl: string}}
37  * @see chrome/browser/ui/webui/options/browser_options_handler.cc
38  */
39 options.Profile;
41 cr.define('options', function() {
42   var OptionsPage = options.OptionsPage;
43   var Page = cr.ui.pageManager.Page;
44   var PageManager = cr.ui.pageManager.PageManager;
45   var ArrayDataModel = cr.ui.ArrayDataModel;
46   var RepeatingButton = cr.ui.RepeatingButton;
47   var HotwordSearchSettingIndicator = options.HotwordSearchSettingIndicator;
48   var NetworkPredictionOptions = {
49     ALWAYS: 0,
50     WIFI_ONLY: 1,
51     NEVER: 2,
52     UNSET: 3,
53     DEFAULT: 1
54   };
56   /**
57    * Encapsulated handling of browser options page.
58    * @constructor
59    * @extends {cr.ui.pageManager.Page}
60    */
61   function BrowserOptions() {
62     Page.call(this, 'settings', loadTimeData.getString('settingsTitle'),
63               'settings');
64   }
66   cr.addSingletonGetter(BrowserOptions);
68   /**
69    * @param {HTMLElement} section The section to show or hide.
70    * @return {boolean} Whether the section should be shown.
71    * @private
72    */
73   BrowserOptions.shouldShowSection_ = function(section) {
74     // If the section is hidden or hiding, it should be shown.
75     return section.style.height == '' || section.style.height == '0px';
76   };
78   BrowserOptions.prototype = {
79     __proto__: Page.prototype,
81     /**
82      * Keeps track of whether the user is signed in or not.
83      * @type {boolean}
84      * @private
85      */
86     signedIn_: false,
88     /**
89      * Indicates whether signing out is allowed or whether a complete profile
90      * wipe is required to remove the current enterprise account.
91      * @type {boolean}
92      * @private
93      */
94     signoutAllowed_: true,
96     /**
97      * Keeps track of whether |onShowHomeButtonChanged_| has been called. See
98      * |onShowHomeButtonChanged_|.
99      * @type {boolean}
100      * @private
101      */
102     onShowHomeButtonChangedCalled_: false,
104     /**
105      * Track if page initialization is complete.  All C++ UI handlers have the
106      * chance to manipulate page content within their InitializePage methods.
107      * This flag is set to true after all initializers have been called.
108      * @type {boolean}
109      * @private
110      */
111     initializationComplete_: false,
113     /** @override */
114     initializePage: function() {
115       Page.prototype.initializePage.call(this);
116       var self = this;
118       if (window.top != window) {
119         // The options page is not in its own window.
120         document.body.classList.add('uber-frame');
121         PageManager.horizontalOffset = 155;
122       }
124       // Ensure that navigation events are unblocked on uber page. A reload of
125       // the settings page while an overlay is open would otherwise leave uber
126       // page in a blocked state, where tab switching is not possible.
127       uber.invokeMethodOnParent('stopInterceptingEvents');
129       window.addEventListener('message', this.handleWindowMessage_.bind(this));
131       if (loadTimeData.getBoolean('allowAdvancedSettings')) {
132         $('advanced-settings-expander').onclick = function(e) {
133           var showAdvanced =
134               BrowserOptions.shouldShowSection_($('advanced-settings'));
135           if (showAdvanced) {
136             chrome.send('coreOptionsUserMetricsAction',
137                         ['Options_ShowAdvancedSettings']);
138           }
139           self.toggleSectionWithAnimation_(
140               $('advanced-settings'),
141               $('advanced-settings-container'));
143           // If the click was triggered using the keyboard and it showed the
144           // section (rather than hiding it), focus the first element in the
145           // container.
146           if (e.detail == 0 && showAdvanced) {
147             var focusElement = $('advanced-settings-container').querySelector(
148                 'button, input, list, select, a[href]');
149             if (focusElement)
150               focusElement.focus();
151           }
152         };
153       } else {
154         $('advanced-settings-footer').hidden = true;
155         $('advanced-settings').hidden = true;
156       }
158       $('advanced-settings').addEventListener('webkitTransitionEnd',
159           this.updateAdvancedSettingsExpander_.bind(this));
161       if (loadTimeData.getBoolean('showAbout')) {
162         $('about-button').hidden = false;
163         $('about-button').addEventListener('click', function() {
164           PageManager.showPageByName('help');
165           chrome.send('coreOptionsUserMetricsAction',
166                       ['Options_About']);
167         });
168       }
170       if (cr.isChromeOS) {
171         UIAccountTweaks.applyGuestSessionVisibility(document);
172         UIAccountTweaks.applyPublicSessionVisibility(document);
173         if (loadTimeData.getBoolean('secondaryUser'))
174           $('secondary-user-banner').hidden = false;
175       }
177       // Sync (Sign in) section.
178       this.updateSyncState_(loadTimeData.getValue('syncData'));
180       $('start-stop-sync').onclick = function(event) {
181         if (self.signedIn_) {
182           if (self.signoutAllowed_)
183             SyncSetupOverlay.showStopSyncingUI();
184           else
185             chrome.send('showDisconnectManagedProfileDialog');
186         } else if (cr.isChromeOS) {
187           SyncSetupOverlay.showSetupUI();
188         } else {
189           SyncSetupOverlay.startSignIn();
190         }
191       };
192       $('customize-sync').onclick = function(event) {
193         SyncSetupOverlay.showSetupUI();
194       };
196       // Internet connection section (ChromeOS only).
197       if (cr.isChromeOS) {
198         options.network.NetworkList.decorate($('network-list'));
199         // Show that the network settings are shared if this is a secondary user
200         // in a multi-profile session.
201         if (loadTimeData.getBoolean('secondaryUser')) {
202           var networkIndicator = document.querySelector(
203               '#network-section-header > .controlled-setting-indicator');
204           networkIndicator.setAttribute('controlled-by', 'shared');
205           networkIndicator.location = cr.ui.ArrowLocation.TOP_START;
206         }
207         options.network.NetworkList.refreshNetworkData(
208             loadTimeData.getValue('networkData'));
209       }
211       // On Startup section.
212       Preferences.getInstance().addEventListener('session.restore_on_startup',
213           this.onRestoreOnStartupChanged_.bind(this));
214       Preferences.getInstance().addEventListener(
215           'session.startup_urls',
216           function(event) {
217             $('startup-set-pages').disabled = event.value.disabled;
218           });
220       $('startup-set-pages').onclick = function() {
221         PageManager.showPageByName('startup');
222       };
224       // Appearance section.
225       Preferences.getInstance().addEventListener('browser.show_home_button',
226           this.onShowHomeButtonChanged_.bind(this));
228       Preferences.getInstance().addEventListener('homepage',
229           this.onHomePageChanged_.bind(this));
230       Preferences.getInstance().addEventListener('homepage_is_newtabpage',
231           this.onHomePageIsNtpChanged_.bind(this));
233       $('change-home-page').onclick = function(event) {
234         PageManager.showPageByName('homePageOverlay');
235         chrome.send('coreOptionsUserMetricsAction',
236                     ['Options_Homepage_ShowSettings']);
237       };
239       HotwordSearchSettingIndicator.decorate(
240           $('hotword-search-setting-indicator'));
241       HotwordSearchSettingIndicator.decorate(
242           $('hotword-no-dsp-search-setting-indicator'));
243       var hotwordIndicator = $('hotword-always-on-search-setting-indicator');
244       HotwordSearchSettingIndicator.decorate(hotwordIndicator);
245       hotwordIndicator.disabledOnErrorSection =
246           $('hotword-always-on-search-checkbox');
247       chrome.send('requestHotwordAvailable');
249       if ($('set-wallpaper')) {
250         $('set-wallpaper').onclick = function(event) {
251           chrome.send('openWallpaperManager');
252           chrome.send('coreOptionsUserMetricsAction',
253                       ['Options_OpenWallpaperManager']);
254         };
255       }
257       // Control the hotword-always-on pref with the Hotword Audio
258       // Verification app.
259       $('hotword-always-on-search-checkbox').customChangeHandler =
260           function(event) {
261         if (!$('hotword-always-on-search-checkbox').checked)
262           return false;
264         $('hotword-always-on-search-checkbox').checked = false;
265         chrome.send('launchHotwordAudioVerificationApp', [false]);
266         return true;
267       };
269       // Open the Hotword Audio Verification app to retrain a voice model.
270       $('hotword-retrain-link').onclick = function(event) {
271         chrome.send('launchHotwordAudioVerificationApp', [true]);
272       };
273       Preferences.getInstance().addEventListener(
274           'hotword.always_on_search_enabled',
275           this.onHotwordAlwaysOnChanged_.bind(this));
277       $('themes-gallery').onclick = function(event) {
278         window.open(loadTimeData.getString('themesGalleryURL'));
279         chrome.send('coreOptionsUserMetricsAction',
280                     ['Options_ThemesGallery']);
281       };
282       $('themes-reset').onclick = function(event) {
283         chrome.send('themesReset');
284       };
286       if (loadTimeData.getBoolean('profileIsSupervised')) {
287         if ($('themes-native-button')) {
288           $('themes-native-button').disabled = true;
289           $('themes-native-button').hidden = true;
290         }
291         // Supervised users have just one default theme, even on Linux. So use
292         // the same button for Linux as for the other platforms.
293         $('themes-reset').textContent = loadTimeData.getString('themesReset');
294       }
296       // Device section (ChromeOS only).
297       if (cr.isChromeOS) {
298         $('power-settings-button').onclick = function(evt) {
299           PageManager.showPageByName('power-overlay');
300           chrome.send('coreOptionsUserMetricsAction',
301                       ['Options_ShowPowerSettings']);
302         };
303         $('battery-button').onclick = function(evt) {
304           WebsiteSettingsManager.showWebsiteSettings('battery');
305         };
306         $('stored-data-button').onclick = function(evt) {
307           WebsiteSettingsManager.showWebsiteSettings('storage');
308         };
309         $('keyboard-settings-button').onclick = function(evt) {
310           PageManager.showPageByName('keyboard-overlay');
311           chrome.send('coreOptionsUserMetricsAction',
312                       ['Options_ShowKeyboardSettings']);
313         };
314         $('pointer-settings-button').onclick = function(evt) {
315           PageManager.showPageByName('pointer-overlay');
316           chrome.send('coreOptionsUserMetricsAction',
317                       ['Options_ShowTouchpadSettings']);
318         };
319       }
321       // Search section.
322       $('manage-default-search-engines').onclick = function(event) {
323         PageManager.showPageByName('searchEngines');
324         chrome.send('coreOptionsUserMetricsAction',
325                     ['Options_ManageSearchEngines']);
326       };
327       $('default-search-engine').addEventListener('change',
328           this.setDefaultSearchEngine_);
330       // Users section.
331       if (loadTimeData.valueExists('profilesInfo')) {
332         $('profiles-section').hidden = false;
333         this.maybeShowUserSection_();
335         var profilesList = $('profiles-list');
336         options.browser_options.ProfileList.decorate(profilesList);
337         profilesList.autoExpands = true;
339         // The profiles info data in |loadTimeData| might be stale.
340         this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
341         chrome.send('requestProfilesInfo');
343         profilesList.addEventListener('change',
344             this.setProfileViewButtonsStatus_);
345         $('profiles-create').onclick = function(event) {
346           ManageProfileOverlay.showCreateDialog();
347         };
348         if (OptionsPage.isSettingsApp()) {
349           $('profiles-app-list-switch').onclick = function(event) {
350             var selectedProfile = self.getSelectedProfileItem_();
351             chrome.send('switchAppListProfile', [selectedProfile.filePath]);
352           };
353         }
354         $('profiles-manage').onclick = function(event) {
355           ManageProfileOverlay.showManageDialog();
356         };
357         $('profiles-delete').onclick = function(event) {
358           var selectedProfile = self.getSelectedProfileItem_();
359           if (selectedProfile)
360             ManageProfileOverlay.showDeleteDialog(selectedProfile);
361         };
362         if (loadTimeData.getBoolean('profileIsSupervised')) {
363           $('profiles-create').disabled = true;
364         }
365         if (!loadTimeData.getBoolean('allowProfileDeletion')) {
366           $('profiles-delete').disabled = true;
367           $('profiles-list').canDeleteItems = false;
368         }
369       }
371       if (cr.isChromeOS) {
372         // Username (canonical email) of the currently logged in user or
373         // |kGuestUser| if a guest session is active.
374         this.username_ = loadTimeData.getString('username');
376         this.updateAccountPicture_();
378         $('account-picture').onclick = this.showImagerPickerOverlay_;
379         $('change-picture-caption').onclick = this.showImagerPickerOverlay_;
381         $('manage-accounts-button').onclick = function(event) {
382           PageManager.showPageByName('accounts');
383           chrome.send('coreOptionsUserMetricsAction',
384               ['Options_ManageAccounts']);
385         };
386       } else {
387         $('import-data').onclick = function(event) {
388           ImportDataOverlay.show();
389           chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
390         };
392         if ($('themes-native-button')) {
393           $('themes-native-button').onclick = function(event) {
394             chrome.send('themesSetNative');
395           };
396         }
397       }
399       // Date and time section (CrOS only).
400       if (cr.isChromeOS) {
401         if ($('set-time-button'))
402           $('set-time-button').onclick = this.handleSetTime_.bind(this);
404         // Timezone
405         if (loadTimeData.getBoolean('enableTimeZoneTrackingOption')) {
406           $('resolve-timezone-by-geolocation-selection').hidden = false;
407           this.setSystemTimezoneManaged_(false);
408         }
409       }
411       // Default browser section.
412       if (!cr.isChromeOS) {
413         if (!loadTimeData.getBoolean('showSetDefault')) {
414           $('set-default-browser-section').hidden = true;
415         }
416         $('set-as-default-browser').onclick = function(event) {
417           chrome.send('becomeDefaultBrowser');
418         };
420         $('auto-launch').onclick = this.handleAutoLaunchChanged_;
421       }
423       // Privacy section.
424       $('privacyContentSettingsButton').onclick = function(event) {
425         PageManager.showPageByName('content');
426         OptionsPage.showTab($('cookies-nav-tab'));
427         chrome.send('coreOptionsUserMetricsAction',
428             ['Options_ContentSettings']);
429       };
430       $('privacyClearDataButton').onclick = function(event) {
431         PageManager.showPageByName('clearBrowserData');
432         chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
433       };
434       $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
435       // 'metricsReportingEnabled' element is only present on Chrome branded
436       // builds, and the 'metricsReportingCheckboxAction' message is only
437       // handled on ChromeOS.
438       if ($('metricsReportingEnabled') && cr.isChromeOS) {
439         $('metricsReportingEnabled').onclick = function(event) {
440           chrome.send('metricsReportingCheckboxAction',
441               [String(event.currentTarget.checked)]);
442         };
443       }
444       if ($('metricsReportingEnabled') && !cr.isChromeOS) {
445         // The localized string has the | symbol on each side of the text that
446         // needs to be made into a button to restart Chrome. We parse the text
447         // and build the button from that.
448         var restartTextFragments =
449             loadTimeData.getString('metricsReportingResetRestart').split('|');
450         // Assume structure is something like "starting text |link text| ending
451         // text" where both starting text and ending text may or may not be
452         // present, but the split should always be in three pieces.
453         var restartElements =
454             $('metrics-reporting-reset-restart').querySelectorAll('*');
455         for (var i = 0; i < restartTextFragments.length; i++) {
456           restartElements[i].textContent = restartTextFragments[i];
457         }
458         restartElements[1].onclick = function(event) {
459           chrome.send('restartBrowser');
460         };
461         // Attach the listener for updating the checkbox and restart button.
462         var updateMetricsRestartButton = function() {
463           $('metrics-reporting-reset-restart').hidden =
464               loadTimeData.getBoolean('metricsReportingEnabledAtStart') ==
465                   $('metricsReportingEnabled').checked;
466         };
467         $('metricsReportingEnabled').onclick = function(event) {
468           chrome.send('metricsReportingCheckboxChanged',
469               [Boolean(event.currentTarget.checked)]);
470           updateMetricsRestartButton();
471         };
472         $('metricsReportingEnabled').checked =
473             loadTimeData.getBoolean('metricsReportingEnabledAtStart');
474         updateMetricsRestartButton();
475       }
476       $('networkPredictionOptions').onchange = function(event) {
477         var value = (event.target.checked ?
478             NetworkPredictionOptions.WIFI_ONLY :
479             NetworkPredictionOptions.NEVER);
480         var metric = event.target.metric;
481         Preferences.setIntegerPref(
482             'net.network_prediction_options',
483             value,
484             true,
485             metric);
486       };
487       if (loadTimeData.valueExists('showWakeOnWifi') &&
488           loadTimeData.getBoolean('showWakeOnWifi')) {
489         $('wake-on-wifi').hidden = false;
490       }
492       // Bluetooth (CrOS only).
493       if (cr.isChromeOS) {
494         options.system.bluetooth.BluetoothDeviceList.decorate(
495             $('bluetooth-paired-devices-list'));
497         $('bluetooth-add-device').onclick =
498             this.handleAddBluetoothDevice_.bind(this);
500         $('enable-bluetooth').onchange = function(event) {
501           var state = $('enable-bluetooth').checked;
502           chrome.send('bluetoothEnableChange', [Boolean(state)]);
503         };
505         $('bluetooth-reconnect-device').onclick = function(event) {
506           chrome.send('coreOptionsUserMetricsAction',
507                       ['Options_BluetoothConnectPairedDevice']);
508           var device = $('bluetooth-paired-devices-list').selectedItem;
509           var address = device.address;
510           chrome.send('updateBluetoothDevice', [address, 'connect']);
511           PageManager.closeOverlay();
512         };
514         $('bluetooth-paired-devices-list').addEventListener('change',
515             function() {
516           var item = $('bluetooth-paired-devices-list').selectedItem;
517           var disabled = !item || item.connected || !item.connectable;
518           $('bluetooth-reconnect-device').disabled = disabled;
519         });
520       }
522       // Passwords and Forms section.
523       $('autofill-settings').onclick = function(event) {
524         PageManager.showPageByName('autofill');
525         chrome.send('coreOptionsUserMetricsAction',
526             ['Options_ShowAutofillSettings']);
527       };
528       $('manage-passwords').onclick = function(event) {
529         PageManager.showPageByName('passwords');
530         OptionsPage.showTab($('passwords-nav-tab'));
531         chrome.send('coreOptionsUserMetricsAction',
532             ['Options_ShowPasswordManager']);
533       };
534       if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
535         // Disable and turn off Autofill in guest mode.
536         var autofillEnabled = $('autofill-enabled');
537         autofillEnabled.disabled = true;
538         autofillEnabled.checked = false;
539         cr.dispatchSimpleEvent(autofillEnabled, 'change');
540         $('autofill-settings').disabled = true;
542         // Disable and turn off Password Manager in guest mode.
543         var passwordManagerEnabled = $('password-manager-enabled');
544         passwordManagerEnabled.disabled = true;
545         passwordManagerEnabled.checked = false;
546         cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
547         $('manage-passwords').disabled = true;
548       }
550       if (cr.isMac) {
551         $('mac-passwords-warning').hidden =
552             !loadTimeData.getBoolean('multiple_profiles');
553       }
555       // Network section.
556       if (!cr.isChromeOS) {
557         $('proxiesConfigureButton').onclick = function(event) {
558           chrome.send('showNetworkProxySettings');
559         };
560       }
562       // Device control section.
563       if (cr.isChromeOS &&
564           UIAccountTweaks.currentUserIsOwner() &&
565           loadTimeData.getBoolean('consumerManagementEnabled')) {
566         $('device-control-section').hidden = false;
567         $('consumer-management-button').onclick = function(event) {
568           PageManager.showPageByName('consumer-management-overlay');
569         };
570       }
572       // Easy Unlock section.
573       if (loadTimeData.getBoolean('easyUnlockAllowed')) {
574         $('easy-unlock-section').hidden = false;
575         $('easy-unlock-setup-button').onclick = function(event) {
576           chrome.send('launchEasyUnlockSetup');
577         };
578         $('easy-unlock-turn-off-button').onclick = function(event) {
579           PageManager.showPageByName('easyUnlockTurnOffOverlay');
580         };
581       }
582       $('easy-unlock-enable-proximity-detection').hidden =
583           !loadTimeData.getBoolean('easyUnlockProximityDetectionAllowed');
585       // Website Settings section.
586       if (loadTimeData.getBoolean('websiteSettingsManagerEnabled')) {
587         $('website-settings-section').hidden = false;
588         $('website-management-button').onclick = function(event) {
589           PageManager.showPageByName('websiteSettings');
590         };
591       }
593       // Web Content section.
594       $('fontSettingsCustomizeFontsButton').onclick = function(event) {
595         PageManager.showPageByName('fonts');
596         chrome.send('coreOptionsUserMetricsAction',
597                     ['Options_ShowFontSettings']);
598       };
599       $('defaultFontSize').onchange = function(event) {
600         var value = event.target.options[event.target.selectedIndex].value;
601         Preferences.setIntegerPref(
602              'webkit.webprefs.default_fixed_font_size',
603              value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
604         chrome.send('defaultFontSizeAction', [String(value)]);
605       };
606       $('defaultZoomFactor').onchange = function(event) {
607         chrome.send('defaultZoomFactorAction',
608             [String(event.target.options[event.target.selectedIndex].value)]);
609       };
611       // Languages section.
612       var showLanguageOptions = function(event) {
613         PageManager.showPageByName('languages');
614         chrome.send('coreOptionsUserMetricsAction',
615             ['Options_LanuageAndSpellCheckSettings']);
616       };
617       $('language-button').onclick = showLanguageOptions;
618       $('manage-languages').onclick = showLanguageOptions;
620       // Downloads section.
621       Preferences.getInstance().addEventListener('download.default_directory',
622           this.onDefaultDownloadDirectoryChanged_.bind(this));
623       $('downloadLocationChangeButton').onclick = function(event) {
624         chrome.send('selectDownloadLocation');
625       };
626       if (cr.isChromeOS) {
627         $('disable-drive-row').hidden =
628             UIAccountTweaks.loggedInAsSupervisedUser();
629       }
630       $('autoOpenFileTypesResetToDefault').onclick = function(event) {
631         chrome.send('autoOpenFileTypesAction');
632       };
634       // HTTPS/SSL section.
635       if (cr.isWindows || cr.isMac) {
636         $('certificatesManageButton').onclick = function(event) {
637           chrome.send('showManageSSLCertificates');
638         };
639       } else {
640         $('certificatesManageButton').onclick = function(event) {
641           PageManager.showPageByName('certificates');
642           chrome.send('coreOptionsUserMetricsAction',
643                       ['Options_ManageSSLCertificates']);
644         };
645       }
647       if (loadTimeData.getBoolean('cloudPrintShowMDnsOptions')) {
648         $('cloudprint-options-mdns').hidden = false;
649         $('cloudPrintDevicesPageButton').onclick = function() {
650           chrome.send('showCloudPrintDevicesPage');
651         };
652       }
654       // Accessibility section (CrOS only).
655       if (cr.isChromeOS) {
656         var updateAccessibilitySettingsButton = function() {
657           $('accessibility-settings').hidden =
658               !($('accessibility-spoken-feedback-check').checked);
659         };
660         Preferences.getInstance().addEventListener(
661             'settings.accessibility',
662             updateAccessibilitySettingsButton);
663         $('accessibility-learn-more').onclick = function(unused_event) {
664           window.open(loadTimeData.getString('accessibilityLearnMoreURL'));
665           chrome.send('coreOptionsUserMetricsAction',
666                       ['Options_AccessibilityLearnMore']);
667         };
668         $('accessibility-settings-button').onclick = function(unused_event) {
669           window.open(loadTimeData.getString('accessibilitySettingsURL'));
670         };
671         $('accessibility-spoken-feedback-check').onchange =
672             updateAccessibilitySettingsButton;
673         updateAccessibilitySettingsButton();
675         var updateDelayDropdown = function() {
676           $('accessibility-autoclick-dropdown').disabled =
677               !$('accessibility-autoclick-check').checked;
678         };
679         Preferences.getInstance().addEventListener(
680             $('accessibility-autoclick-check').getAttribute('pref'),
681             updateDelayDropdown);
682       }
684       // Display management section (CrOS only).
685       if (cr.isChromeOS) {
686         $('display-options').onclick = function(event) {
687           PageManager.showPageByName('display');
688           chrome.send('coreOptionsUserMetricsAction',
689                       ['Options_Display']);
690         };
691       }
693       // Factory reset section (CrOS only).
694       if (cr.isChromeOS) {
695         $('factory-reset-restart').onclick = function(event) {
696           PageManager.showPageByName('factoryResetData');
697           chrome.send('onPowerwashDialogShow');
698         };
699       }
701       // System section.
702       if (!cr.isChromeOS) {
703         var updateGpuRestartButton = function() {
704           $('gpu-mode-reset-restart').hidden =
705               loadTimeData.getBoolean('gpuEnabledAtStart') ==
706               $('gpu-mode-checkbox').checked;
707         };
708         Preferences.getInstance().addEventListener(
709             $('gpu-mode-checkbox').getAttribute('pref'),
710             updateGpuRestartButton);
711         $('gpu-mode-reset-restart-button').onclick = function(event) {
712           chrome.send('restartBrowser');
713         };
714         updateGpuRestartButton();
715       }
717       // Reset profile settings section.
718       $('reset-profile-settings').onclick = function(event) {
719         PageManager.showPageByName('resetProfileSettings');
720       };
722       // Extension controlled UI.
723       this.addExtensionControlledBox_('search-section-content',
724                                       'search-engine-controlled',
725                                       true);
726       this.addExtensionControlledBox_('extension-controlled-container',
727                                       'homepage-controlled',
728                                       true);
729       this.addExtensionControlledBox_('startup-section-content',
730                                       'startpage-controlled',
731                                       false);
732       this.addExtensionControlledBox_('newtab-section-content',
733                                       'newtab-controlled',
734                                       false);
735       this.addExtensionControlledBox_('proxy-section-content',
736                                       'proxy-controlled',
737                                       true);
739       document.body.addEventListener('click', function(e) {
740         var target = assertInstanceof(e.target, Node);
741         var button = findAncestor(target, function(el) {
742           return el.tagName == 'BUTTON' &&
743                  el.dataset.extensionId !== undefined &&
744                  el.dataset.extensionId.length;
745         });
746         if (button)
747           chrome.send('disableExtension', [button.dataset.extensionId]);
748       });
749     },
751     /** @override */
752     didShowPage: function() {
753       $('search-field').focus();
754     },
756    /**
757     * Called after all C++ UI handlers have called InitializePage to notify
758     * that initialization is complete.
759     * @private
760     */
761     notifyInitializationComplete_: function() {
762       this.initializationComplete_ = true;
763       cr.dispatchSimpleEvent(document, 'initializationComplete');
764     },
766     /**
767      * Event listener for the 'session.restore_on_startup' pref.
768      * @param {Event} event The preference change event.
769      * @private
770      */
771     onRestoreOnStartupChanged_: function(event) {
772       /** @const */ var showHomePageValue = 0;
774       if (event.value.value == showHomePageValue) {
775         // If the user previously selected "Show the homepage", the
776         // preference will already be migrated to "Open a specific page". So
777         // the only way to reach this code is if the 'restore on startup'
778         // preference is managed.
779         assert(event.value.controlledBy);
781         // Select "open the following pages" and lock down the list of URLs
782         // to reflect the intention of the policy.
783         $('startup-show-pages').checked = true;
784         StartupOverlay.getInstance().setControlsDisabled(true);
785       } else {
786         // Re-enable the controls in the startup overlay if necessary.
787         StartupOverlay.getInstance().updateControlStates();
788       }
789     },
791     /**
792      * Handler for messages sent from the main uber page.
793      * @param {Event} e The 'message' event from the uber page.
794      * @private
795      */
796     handleWindowMessage_: function(e) {
797       if ((/** @type {{method: string}} */(e.data)).method == 'frameSelected')
798         $('search-field').focus();
799     },
801     /**
802      * Animatedly changes height |from| a px number |to| a px number.
803      * @param {HTMLElement} section The section to animate.
804      * @param {HTMLElement} container The container of |section|.
805      * @param {boolean} showing Whether to go from 0 -> container height or
806      *     container height -> 0.
807      * @private
808      */
809     animatedSectionHeightChange_: function(section, container, showing) {
810       // If the section is already animating, dispatch a synthetic transition
811       // end event as the upcoming code will cancel the current one.
812       if (section.classList.contains('sliding'))
813         cr.dispatchSimpleEvent(section, 'webkitTransitionEnd');
815       this.addTransitionEndListener_(section);
817       section.hidden = false;
818       section.style.height = (showing ? 0 : container.offsetHeight) + 'px';
819       section.classList.add('sliding');
821       // Force a style recalc before starting the animation.
822       /** @suppress {suspiciousCode} */
823       section.offsetHeight;
825       section.style.height = (showing ? container.offsetHeight : 0) + 'px';
826     },
828     /**
829      * Shows the given section.
830      * @param {HTMLElement} section The section to be shown.
831      * @param {HTMLElement} container The container for the section. Must be
832      *     inside of |section|.
833      * @param {boolean} animate Indicate if the expansion should be animated.
834      * @private
835      */
836     showSection_: function(section, container, animate) {
837       // Delay starting the transition if animating so that hidden change will
838       // be processed.
839       if (animate) {
840         this.animatedSectionHeightChange_(section, container, true);
841       } else {
842         section.hidden = false;
843         section.style.height = 'auto';
844       }
845     },
847     /**
848      * Shows the given section, with animation.
849      * @param {HTMLElement} section The section to be shown.
850      * @param {HTMLElement} container The container for the section. Must be
851      *     inside of |section|.
852      * @private
853      */
854     showSectionWithAnimation_: function(section, container) {
855       this.showSection_(section, container, /* animate */ true);
856     },
858     /**
859      * Hides the given |section| with animation.
860      * @param {HTMLElement} section The section to be hidden.
861      * @param {HTMLElement} container The container for the section. Must be
862      *     inside of |section|.
863      * @private
864      */
865     hideSectionWithAnimation_: function(section, container) {
866       this.animatedSectionHeightChange_(section, container, false);
867     },
869     /**
870      * Toggles the visibility of |section| in an animated way.
871      * @param {HTMLElement} section The section to be toggled.
872      * @param {HTMLElement} container The container for the section. Must be
873      *     inside of |section|.
874      * @private
875      */
876     toggleSectionWithAnimation_: function(section, container) {
877       if (BrowserOptions.shouldShowSection_(section))
878         this.showSectionWithAnimation_(section, container);
879       else
880         this.hideSectionWithAnimation_(section, container);
881     },
883     /**
884      * Scrolls the settings page to make the section visible auto-expanding
885      * advanced settings if required.  The transition is not animated.  This
886      * method is used to ensure that a section associated with an overlay
887      * is visible when the overlay is closed.
888      * @param {!Element} section  The section to make visible.
889      * @private
890      */
891     scrollToSection_: function(section) {
892       var advancedSettings = $('advanced-settings');
893       var container = $('advanced-settings-container');
894       var expander = $('advanced-settings-expander');
895       if (!expander.hidden &&
896           advancedSettings.hidden &&
897           section.parentNode == container) {
898         this.showSection_($('advanced-settings'),
899                           $('advanced-settings-container'),
900                           /* animate */ false);
901         this.updateAdvancedSettingsExpander_();
902       }
904       if (!this.initializationComplete_) {
905         var self = this;
906         var callback = function() {
907            document.removeEventListener('initializationComplete', callback);
908            self.scrollToSection_(section);
909         };
910         document.addEventListener('initializationComplete', callback);
911         return;
912       }
914       var pageContainer = $('page-container');
915       // pageContainer.offsetTop is relative to the screen.
916       var pageTop = pageContainer.offsetTop;
917       var sectionBottom = section.offsetTop + section.offsetHeight;
918       // section.offsetTop is relative to the 'page-container'.
919       var sectionTop = section.offsetTop;
920       if (pageTop + sectionBottom > document.body.scrollHeight ||
921           pageTop + sectionTop < 0) {
922         // Currently not all layout updates are guaranteed to precede the
923         // initializationComplete event (for example 'set-as-default-browser'
924         // button) leaving some uncertainty in the optimal scroll position.
925         // The section is placed approximately in the middle of the screen.
926         var top = Math.min(0, document.body.scrollHeight / 2 - sectionBottom);
927         pageContainer.style.top = top + 'px';
928         pageContainer.oldScrollTop = -top;
929       }
930     },
932     /**
933      * Adds a |webkitTransitionEnd| listener to the given section so that
934      * it can be animated. The listener will only be added to a given section
935      * once, so this can be called as multiple times.
936      * @param {HTMLElement} section The section to be animated.
937      * @private
938      */
939     addTransitionEndListener_: function(section) {
940       if (section.hasTransitionEndListener_)
941         return;
943       section.addEventListener('webkitTransitionEnd',
944           this.onTransitionEnd_.bind(this));
945       section.hasTransitionEndListener_ = true;
946     },
948     /**
949      * Called after an animation transition has ended.
950      * @param {Event} event The webkitTransitionEnd event. NOTE: May be
951      *     synthetic.
952      * @private
953      */
954     onTransitionEnd_: function(event) {
955       if (event.propertyName && event.propertyName != 'height') {
956         // If not a synthetic event or a real transition we care about, bail.
957         return;
958       }
960       var section = event.target;
961       section.classList.remove('sliding');
963       if (!event.propertyName) {
964         // Only real transitions past this point.
965         return;
966       }
968       if (section.style.height == '0px') {
969         // Hide the content so it can't get tab focus.
970         section.hidden = true;
971         section.style.height = '';
972       } else {
973         // Set the section height to 'auto' to allow for size changes
974         // (due to font change or dynamic content).
975         section.style.height = 'auto';
976       }
977     },
979     /** @private */
980     updateAdvancedSettingsExpander_: function() {
981       var expander = $('advanced-settings-expander');
982       if (BrowserOptions.shouldShowSection_($('advanced-settings')))
983         expander.textContent = loadTimeData.getString('showAdvancedSettings');
984       else
985         expander.textContent = loadTimeData.getString('hideAdvancedSettings');
986     },
988     /**
989      * Updates the sync section with the given state.
990      * @param {options.SyncStatus} syncData A bunch of data records that
991      *     describe the status of the sync system.
992      * @private
993      */
994     updateSyncState_: function(syncData) {
995       if (!syncData.signinAllowed &&
996           (!syncData.supervisedUser || !cr.isChromeOS)) {
997         $('sync-section').hidden = true;
998         this.maybeShowUserSection_();
999         return;
1000       }
1002       $('sync-section').hidden = false;
1003       this.maybeShowUserSection_();
1005       if (cr.isChromeOS && syncData.supervisedUser && !syncData.childUser) {
1006         var subSection = $('sync-section').firstChild;
1007         while (subSection) {
1008           if (subSection.nodeType == Node.ELEMENT_NODE)
1009             subSection.hidden = true;
1010           subSection = subSection.nextSibling;
1011         }
1013         $('account-picture-wrapper').hidden = false;
1014         $('sync-general').hidden = false;
1015         $('sync-status').hidden = true;
1017         return;
1018       }
1020       // If the user gets signed out while the advanced sync settings dialog is
1021       // visible, say, due to a dashboard clear, close the dialog.
1022       // However, if the user gets signed out as a result of abandoning first
1023       // time sync setup, do not call closeOverlay as it will redirect the
1024       // browser to the main settings page and override any in-progress
1025       // user-initiated navigation. See crbug.com/278030.
1026       // Note: SyncSetupOverlay.closeOverlay is a no-op if the overlay is
1027       // already hidden.
1028       if (this.signedIn_ && !syncData.signedIn && !syncData.setupInProgress)
1029         SyncSetupOverlay.closeOverlay();
1031       this.signedIn_ = !!syncData.signedIn;
1033       // Display the "advanced settings" button if we're signed in and sync is
1034       // not managed/disabled. If the user is signed in, but sync is disabled,
1035       // this button is used to re-enable sync.
1036       var customizeSyncButton = $('customize-sync');
1037       customizeSyncButton.hidden = !this.signedIn_ ||
1038                                    syncData.managed ||
1039                                    !syncData.syncSystemEnabled;
1041       // Only modify the customize button's text if the new text is different.
1042       // Otherwise, it can affect search-highlighting in the settings page.
1043       // See http://crbug.com/268265.
1044       var customizeSyncButtonNewText = syncData.setupCompleted ?
1045           loadTimeData.getString('customizeSync') :
1046           loadTimeData.getString('syncButtonTextStart');
1047       if (customizeSyncButton.textContent != customizeSyncButtonNewText)
1048         customizeSyncButton.textContent = customizeSyncButtonNewText;
1050       // Disable the "sign in" button if we're currently signing in, or if we're
1051       // already signed in and signout is not allowed.
1052       var signInButton = $('start-stop-sync');
1053       signInButton.disabled = syncData.setupInProgress;
1054       this.signoutAllowed_ = !!syncData.signoutAllowed;
1055       if (!syncData.signoutAllowed)
1056         $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
1057       else
1058         $('start-stop-sync-indicator').removeAttribute('controlled-by');
1060       // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome
1061       // (except for supervised users, which can't change their signed-in
1062       // status).
1063       signInButton.hidden = cr.isChromeOS || syncData.supervisedUser;
1065       signInButton.textContent =
1066           this.signedIn_ ?
1067               loadTimeData.getString('syncButtonTextStop') :
1068               syncData.setupInProgress ?
1069                   loadTimeData.getString('syncButtonTextInProgress') :
1070                   loadTimeData.getString('syncButtonTextSignIn');
1071       $('start-stop-sync-indicator').hidden = signInButton.hidden;
1073       // TODO(estade): can this just be textContent?
1074       $('sync-status-text').innerHTML = syncData.statusText;
1075       var statusSet = syncData.statusText.length != 0;
1076       $('sync-overview').hidden =
1077           statusSet ||
1078           (cr.isChromeOS && UIAccountTweaks.loggedInAsPublicAccount());
1079       $('sync-status').hidden = !statusSet;
1081       $('sync-action-link').textContent = syncData.actionLinkText;
1082       // Don't show the action link if it is empty or undefined.
1083       $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
1084       $('sync-action-link').disabled = syncData.managed ||
1085                                        !syncData.syncSystemEnabled;
1087       // On Chrome OS, sign out the user and sign in again to get fresh
1088       // credentials on auth errors.
1089       $('sync-action-link').onclick = function(event) {
1090         if (cr.isChromeOS && syncData.hasError)
1091           SyncSetupOverlay.doSignOutOnAuthError();
1092         else
1093           SyncSetupOverlay.showSetupUI();
1094       };
1096       if (syncData.hasError)
1097         $('sync-status').classList.add('sync-error');
1098       else
1099         $('sync-status').classList.remove('sync-error');
1101       // Disable the "customize / set up sync" button if sync has an
1102       // unrecoverable error. Also disable the button if sync has not been set
1103       // up and the user is being presented with a link to re-auth.
1104       // See crbug.com/289791.
1105       customizeSyncButton.disabled =
1106           syncData.hasUnrecoverableError ||
1107           (!syncData.setupCompleted && !$('sync-action-link').hidden);
1108     },
1110     /**
1111      * Update the UI depending on whether Easy Unlock is enabled for the current
1112      * profile.
1113      * @param {boolean} isEnabled True if the feature is enabled for the current
1114      *     profile.
1115      */
1116     updateEasyUnlock_: function(isEnabled) {
1117       $('easy-unlock-disabled').hidden = isEnabled;
1118       $('easy-unlock-enabled').hidden = !isEnabled;
1119       if (!isEnabled && EasyUnlockTurnOffOverlay.getInstance().visible) {
1120         EasyUnlockTurnOffOverlay.dismiss();
1121       }
1122     },
1124     /**
1125      * Update the UI depending on whether the current profile manages any
1126      * supervised users.
1127      * @param {boolean} show True if the current profile manages any supervised
1128      *     users.
1129      */
1130     updateManagesSupervisedUsers_: function(show) {
1131       $('profiles-supervised-dashboard-tip').hidden = !show;
1132       this.maybeShowUserSection_();
1133     },
1135     /**
1136      * Get the start/stop sync button DOM element. Used for testing.
1137      * @return {Element} The start/stop sync button.
1138      * @private
1139      */
1140     getStartStopSyncButton_: function() {
1141       return $('start-stop-sync');
1142     },
1144     /**
1145      * Event listener for the 'show home button' preference. Shows/hides the
1146      * UI for changing the home page with animation, unless this is the first
1147      * time this function is called, in which case there is no animation.
1148      * @param {Event} event The preference change event.
1149      */
1150     onShowHomeButtonChanged_: function(event) {
1151       var section = $('change-home-page-section');
1152       if (this.onShowHomeButtonChangedCalled_) {
1153         var container = $('change-home-page-section-container');
1154         if (event.value.value)
1155           this.showSectionWithAnimation_(section, container);
1156         else
1157           this.hideSectionWithAnimation_(section, container);
1158       } else {
1159         section.hidden = !event.value.value;
1160         this.onShowHomeButtonChangedCalled_ = true;
1161       }
1162     },
1164     /**
1165      * Activates the Hotword section from the System settings page.
1166      * @param {string} sectionId The id of the section to display.
1167      * @param {string} indicatorId The id of the indicator to display.
1168      * @param {string=} opt_error The error message to display.
1169      * @private
1170      */
1171     showHotwordCheckboxAndIndicator_: function(sectionId, indicatorId,
1172                                                opt_error) {
1173       $(sectionId).hidden = false;
1174       $(indicatorId).setError(opt_error);
1175       if (opt_error)
1176         $(indicatorId).updateBasedOnError();
1177     },
1179     /**
1180      * Activates the Hotword section from the System settings page.
1181      * @param {string=} opt_error The error message to display.
1182      * @private
1183      */
1184     showHotwordSection_: function(opt_error) {
1185       this.showHotwordCheckboxAndIndicator_(
1186           'hotword-search',
1187           'hotword-search-setting-indicator',
1188           opt_error);
1189     },
1191     /**
1192      * Activates the Audio History and Always-On Hotword sections from the
1193      * System settings page.
1194      * @param {string=} opt_error The error message to display.
1195      * @private
1196      */
1197     showHotwordAlwaysOnSection_: function(opt_error) {
1198       this.showHotwordCheckboxAndIndicator_(
1199           'hotword-always-on-search',
1200           'hotword-always-on-search-setting-indicator',
1201           opt_error);
1202     },
1204     /**
1205      * Activates the Hotword section on devices with no DSP
1206      * from the System settings page.
1207      * @param {string=} opt_error The error message to display.
1208      * @private
1209      */
1210     showHotwordNoDspSection_: function(opt_error) {
1211       this.showHotwordCheckboxAndIndicator_(
1212           'hotword-no-dsp-search',
1213           'hotword-no-dsp-search-setting-indicator',
1214           opt_error);
1215     },
1217     /**
1218      * Controls the visibility of all the hotword sections.
1219      * @param {boolean} visible Whether to show hotword sections.
1220      * @private
1221      */
1222     setAllHotwordSectionsVisible_: function(visible) {
1223       $('hotword-search').hidden = !visible;
1224       $('hotword-always-on-search').hidden = !visible;
1225       $('hotword-no-dsp-search').hidden = !visible;
1226       $('audio-history').hidden = !visible;
1227     },
1229     /**
1230      * Shows or hides the hotword retrain link
1231      * @param {boolean} visible Whether to show the link.
1232      * @private
1233      */
1234     setHotwordRetrainLinkVisible_: function(visible) {
1235       $('hotword-retrain-link').hidden = !visible;
1236     },
1238     /**
1239      * Event listener for the 'hotword always on search enabled' preference.
1240      * Updates the visibility of the 'retrain' link.
1241      * @param {Event} event The preference change event.
1242      * @private
1243      */
1244     onHotwordAlwaysOnChanged_: function(event) {
1245       this.setHotwordRetrainLinkVisible_(event.value.value);
1246     },
1248     /**
1249      * Activates the Audio History section of the Settings page.
1250      * @param {boolean} visible Whether the audio history section is visible.
1251      * @param {boolean} alwaysOn Whether always-on hotwording is available.
1252      * @param {string} labelText Text describing current audio history state.
1253      * @private
1254      */
1255     setAudioHistorySectionVisible_: function(visible, alwaysOn, labelText) {
1256       $('audio-history').hidden = !visible;
1257       $('audio-history-label').textContent = labelText;
1258       $('audio-history-always-on-description').hidden = !alwaysOn;
1259     },
1261     /**
1262      * Event listener for the 'homepage is NTP' preference. Updates the label
1263      * next to the 'Change' button.
1264      * @param {Event} event The preference change event.
1265      */
1266     onHomePageIsNtpChanged_: function(event) {
1267       if (!event.value.uncommitted) {
1268         $('home-page-url').hidden = event.value.value;
1269         $('home-page-ntp').hidden = !event.value.value;
1270       }
1271     },
1273     /**
1274      * Event listener for changes to the homepage preference. Updates the label
1275      * next to the 'Change' button.
1276      * @param {Event} event The preference change event.
1277      */
1278     onHomePageChanged_: function(event) {
1279       if (!event.value.uncommitted)
1280         $('home-page-url').textContent = this.stripHttp_(event.value.value);
1281     },
1283     /**
1284      * Removes the 'http://' from a URL, like the omnibox does. If the string
1285      * doesn't start with 'http://' it is returned unchanged.
1286      * @param {string} url The url to be processed
1287      * @return {string} The url with the 'http://' removed.
1288      */
1289     stripHttp_: function(url) {
1290       return url.replace(/^http:\/\//, '');
1291     },
1293    /**
1294     * Shows the autoLaunch preference and initializes its checkbox value.
1295     * @param {boolean} enabled Whether autolaunch is enabled or or not.
1296     * @private
1297     */
1298     updateAutoLaunchState_: function(enabled) {
1299       $('auto-launch-option').hidden = false;
1300       $('auto-launch').checked = enabled;
1301     },
1303     /**
1304      * Called when the value of the download.default_directory preference
1305      * changes.
1306      * @param {Event} event Change event.
1307      * @private
1308      */
1309     onDefaultDownloadDirectoryChanged_: function(event) {
1310       $('downloadLocationPath').value = event.value.value;
1311       if (cr.isChromeOS) {
1312         // On ChromeOS, replace /special/drive-<hash>/root with "Google Drive"
1313         // for remote files, /home/chronos/user/Downloads or
1314         // /home/chronos/u-<hash>/Downloads with "Downloads" for local paths,
1315         // and '/' with ' \u203a ' (angled quote sign) everywhere. The modified
1316         // path is used only for display purpose.
1317         var path = $('downloadLocationPath').value;
1318         path = path.replace(/^\/special\/drive[^\/]*\/root/, 'Google Drive');
1319         path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, '');
1320         path = path.replace(/\//g, ' \u203a ');
1321         $('downloadLocationPath').value = path;
1322       }
1323       $('download-location-label').classList.toggle('disabled',
1324                                                     event.value.disabled);
1325       $('downloadLocationChangeButton').disabled = event.value.disabled;
1326     },
1328     /**
1329      * Update the Default Browsers section based on the current state.
1330      * @param {string} statusString Description of the current default state.
1331      * @param {boolean} isDefault Whether or not the browser is currently
1332      *     default.
1333      * @param {boolean} canBeDefault Whether or not the browser can be default.
1334      * @private
1335      */
1336     updateDefaultBrowserState_: function(statusString, isDefault,
1337                                          canBeDefault) {
1338       if (!cr.isChromeOS) {
1339         var label = $('default-browser-state');
1340         label.textContent = statusString;
1342         $('set-as-default-browser').hidden = !canBeDefault || isDefault;
1343       }
1344     },
1346     /**
1347      * Clears the search engine popup.
1348      * @private
1349      */
1350     clearSearchEngines_: function() {
1351       $('default-search-engine').textContent = '';
1352     },
1354     /**
1355      * Updates the search engine popup with the given entries.
1356      * @param {Array} engines List of available search engines.
1357      * @param {number} defaultValue The value of the current default engine.
1358      * @param {boolean} defaultManaged Whether the default search provider is
1359      *     managed. If true, the default search provider can't be changed.
1360      * @private
1361      */
1362     updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
1363       this.clearSearchEngines_();
1364       var engineSelect = $('default-search-engine');
1365       engineSelect.disabled = defaultManaged;
1366       if (defaultManaged && defaultValue == -1)
1367         return;
1368       var engineCount = engines.length;
1369       var defaultIndex = -1;
1370       for (var i = 0; i < engineCount; i++) {
1371         var engine = engines[i];
1372         var option = new Option(engine.name, engine.index);
1373         if (defaultValue == option.value)
1374           defaultIndex = i;
1375         engineSelect.appendChild(option);
1376       }
1377       if (defaultIndex >= 0)
1378         engineSelect.selectedIndex = defaultIndex;
1379     },
1381     /**
1382      * Set the default search engine based on the popup selection.
1383      * @private
1384      */
1385     setDefaultSearchEngine_: function() {
1386       var engineSelect = $('default-search-engine');
1387       var selectedIndex = engineSelect.selectedIndex;
1388       if (selectedIndex >= 0) {
1389         var selection = engineSelect.options[selectedIndex];
1390         chrome.send('setDefaultSearchEngine', [String(selection.value)]);
1391       }
1392     },
1394    /**
1395      * Sets or clear whether Chrome should Auto-launch on computer startup.
1396      * @private
1397      */
1398     handleAutoLaunchChanged_: function() {
1399       chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
1400     },
1402     /**
1403      * Get the selected profile item from the profile list. This also works
1404      * correctly if the list is not displayed.
1405      * @return {?Object} The profile item object, or null if nothing is
1406      *     selected.
1407      * @private
1408      */
1409     getSelectedProfileItem_: function() {
1410       var profilesList = $('profiles-list');
1411       if (profilesList.hidden) {
1412         if (profilesList.dataModel.length > 0)
1413           return profilesList.dataModel.item(0);
1414       } else {
1415         return profilesList.selectedItem;
1416       }
1417       return null;
1418     },
1420     /**
1421      * Helper function to set the status of profile view buttons to disabled or
1422      * enabled, depending on the number of profiles and selection status of the
1423      * profiles list.
1424      * @private
1425      */
1426     setProfileViewButtonsStatus_: function() {
1427       var profilesList = $('profiles-list');
1428       var selectedProfile = profilesList.selectedItem;
1429       var hasSelection = selectedProfile != null;
1430       var hasSingleProfile = profilesList.dataModel.length == 1;
1431       $('profiles-manage').disabled = !hasSelection ||
1432           !selectedProfile.isCurrentProfile;
1433       if (hasSelection && !selectedProfile.isCurrentProfile)
1434         $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1435       else
1436         $('profiles-manage').title = '';
1437       $('profiles-delete').disabled = !profilesList.canDeleteItems ||
1438                                       (!hasSelection && !hasSingleProfile);
1439       if (OptionsPage.isSettingsApp()) {
1440         $('profiles-app-list-switch').disabled = !hasSelection ||
1441             selectedProfile.isCurrentProfile;
1442       }
1443       var importData = $('import-data');
1444       if (importData) {
1445         importData.disabled = $('import-data').disabled = hasSelection &&
1446           !selectedProfile.isCurrentProfile;
1447       }
1448     },
1450     /**
1451      * Display the correct dialog layout, depending on how many profiles are
1452      * available.
1453      * @param {number} numProfiles The number of profiles to display.
1454      * @private
1455      */
1456     setProfileViewSingle_: function(numProfiles) {
1457       // Always show the profiles list when using the new Profiles UI.
1458       var usingNewProfilesUI = loadTimeData.getBoolean('usingNewProfilesUI');
1459       var showSingleProfileView = !usingNewProfilesUI && numProfiles == 1;
1460       $('profiles-list').hidden = showSingleProfileView;
1461       $('profiles-single-message').hidden = !showSingleProfileView;
1462       $('profiles-manage').hidden =
1463           showSingleProfileView || OptionsPage.isSettingsApp();
1464       $('profiles-delete').textContent = showSingleProfileView ?
1465           loadTimeData.getString('profilesDeleteSingle') :
1466           loadTimeData.getString('profilesDelete');
1467       if (OptionsPage.isSettingsApp())
1468         $('profiles-app-list-switch').hidden = showSingleProfileView;
1469     },
1471     /**
1472      * Adds all |profiles| to the list.
1473      * @param {Array.<!options.Profile>} profiles An array of profile info
1474      *     objects.
1475      * @private
1476      */
1477     setProfilesInfo_: function(profiles) {
1478       this.setProfileViewSingle_(profiles.length);
1479       // add it to the list, even if the list is hidden so we can access it
1480       // later.
1481       $('profiles-list').dataModel = new ArrayDataModel(profiles);
1483       // Received new data. If showing the "manage" overlay, keep it up to
1484       // date. If showing the "delete" overlay, close it.
1485       if (ManageProfileOverlay.getInstance().visible &&
1486           !$('manage-profile-overlay-manage').hidden) {
1487         ManageProfileOverlay.showManageDialog(false);
1488       } else {
1489         ManageProfileOverlay.getInstance().visible = false;
1490       }
1492       this.setProfileViewButtonsStatus_();
1493     },
1495     /**
1496      * Reports supervised user import errors to the SupervisedUserImportOverlay.
1497      * @param {string} error The error message to display.
1498      * @private
1499      */
1500     showSupervisedUserImportError_: function(error) {
1501       SupervisedUserImportOverlay.onError(error);
1502     },
1504     /**
1505      * Reports successful importing of a supervised user to
1506      * the SupervisedUserImportOverlay.
1507      * @private
1508      */
1509     showSupervisedUserImportSuccess_: function() {
1510       SupervisedUserImportOverlay.onSuccess();
1511     },
1513     /**
1514      * Reports an error to the "create" overlay during profile creation.
1515      * @param {string} error The error message to display.
1516      * @private
1517      */
1518     showCreateProfileError_: function(error) {
1519       CreateProfileOverlay.onError(error);
1520     },
1522     /**
1523     * Sends a warning message to the "create" overlay during profile creation.
1524     * @param {string} warning The warning message to display.
1525     * @private
1526     */
1527     showCreateProfileWarning_: function(warning) {
1528       CreateProfileOverlay.onWarning(warning);
1529     },
1531     /**
1532     * Reports successful profile creation to the "create" overlay.
1533      * @param {options.Profile} profileInfo An object of the form:
1534      *     profileInfo = {
1535      *       name: "Profile Name",
1536      *       filePath: "/path/to/profile/data/on/disk"
1537      *       isSupervised: (true|false),
1538      *     };
1539     * @private
1540     */
1541     showCreateProfileSuccess_: function(profileInfo) {
1542       CreateProfileOverlay.onSuccess(profileInfo);
1543     },
1545     /**
1546      * Returns the currently active profile for this browser window.
1547      * @return {options.Profile} A profile info object.
1548      * @private
1549      */
1550     getCurrentProfile_: function() {
1551       for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1552         var profile = $('profiles-list').dataModel.item(i);
1553         if (profile.isCurrentProfile)
1554           return profile;
1555       }
1557       assertNotReached('There should always be a current profile.');
1558     },
1560     /**
1561      * Propmpts user to confirm deletion of the profile for this browser
1562      * window.
1563      * @private
1564      */
1565     deleteCurrentProfile_: function() {
1566       ManageProfileOverlay.showDeleteDialog(this.getCurrentProfile_());
1567     },
1569     /**
1570      * @param {boolean} enabled
1571      */
1572     setNativeThemeButtonEnabled_: function(enabled) {
1573       var button = $('themes-native-button');
1574       if (button)
1575         button.disabled = !enabled;
1576     },
1578     /**
1579      * @param {boolean} enabled
1580      */
1581     setThemesResetButtonEnabled_: function(enabled) {
1582       $('themes-reset').disabled = !enabled;
1583     },
1585     /**
1586      * @param {boolean} managed
1587      */
1588     setAccountPictureManaged_: function(managed) {
1589       var picture = $('account-picture');
1590       if (managed || UIAccountTweaks.loggedInAsGuest()) {
1591         picture.disabled = true;
1592         ChangePictureOptions.closeOverlay();
1593       } else {
1594         picture.disabled = false;
1595       }
1597       // Create a synthetic pref change event decorated as
1598       // CoreOptionsHandler::CreateValueForPref() does.
1599       var event = new Event('account-picture');
1600       if (managed)
1601         event.value = { controlledBy: 'policy' };
1602       else
1603         event.value = {};
1604       $('account-picture-indicator').handlePrefChange(event);
1605     },
1607     /**
1608      * (Re)loads IMG element with current user account picture.
1609      * @private
1610      */
1611     updateAccountPicture_: function() {
1612       var picture = $('account-picture');
1613       if (picture) {
1614         picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1615             Date.now();
1616       }
1617     },
1619     /**
1620      * @param {boolean} managed
1621      */
1622     setWallpaperManaged_: function(managed) {
1623       if (managed)
1624         $('set-wallpaper').disabled = true;
1625       else
1626         this.enableElementIfPossible_(getRequiredElement('set-wallpaper'));
1628       // Create a synthetic pref change event decorated as
1629       // CoreOptionsHandler::CreateValueForPref() does.
1630       var event = new Event('wallpaper');
1631       event.value = managed ? { controlledBy: 'policy' } : {};
1632       $('wallpaper-indicator').handlePrefChange(event);
1633     },
1635     /**
1636      * This is called from chromium code when system timezone "managed" state
1637      * is changed. Enables or disables dependent settings.
1638      * @param {boolean} managed Is true when system Timezone is managed by
1639      *     enterprise policy. False otherwize.
1640      */
1641     setSystemTimezoneManaged_: function(managed) {
1642       if (loadTimeData.getBoolean('enableTimeZoneTrackingOption')) {
1643         if (managed) {
1644           $('resolve-timezone-by-geolocation-selection').disabled = true;
1645           $('resolve-timezone-by-geolocation').onclick = function(event) {};
1646         } else {
1647           this.enableElementIfPossible_(
1648               getRequiredElement('resolve-timezone-by-geolocation-selection'));
1649           $('resolve-timezone-by-geolocation').onclick = function(event) {
1650             $('timezone-value-select').disabled = event.currentTarget.checked;
1651           };
1652           $('timezone-value-select').disabled =
1653               $('resolve-timezone-by-geolocation').checked;
1654         }
1655       }
1656     },
1658     /**
1659      * Handle the 'add device' button click.
1660      * @private
1661      */
1662     handleAddBluetoothDevice_: function() {
1663       chrome.send('coreOptionsUserMetricsAction',
1664                   ['Options_BluetoothShowAddDevice']);
1665       chrome.send('findBluetoothDevices');
1666       PageManager.showPageByName('bluetooth', false);
1667     },
1669     /**
1670      * Enables or disables the Manage SSL Certificates button.
1671      * @private
1672      */
1673     enableCertificateButton_: function(enabled) {
1674       $('certificatesManageButton').disabled = !enabled;
1675     },
1677     /**
1678      * Enables or disables the ChromeOS display settings button.
1679      * @private
1680      */
1681     enableDisplayButton_: function(enabled) {
1682       if (cr.isChromeOS)
1683         $('display-options').disabled = !enabled;
1684     },
1686     /**
1687      * Enables factory reset section.
1688      * @private
1689      */
1690     enableFactoryResetSection_: function() {
1691       $('factory-reset-section').hidden = false;
1692     },
1694     /**
1695      * Set the checked state of the metrics reporting checkbox.
1696      * @private
1697      */
1698     setMetricsReportingCheckboxState_: function(checked, disabled) {
1699       $('metricsReportingEnabled').checked = checked;
1700       $('metricsReportingEnabled').disabled = disabled;
1702       // If checkbox gets disabled then add an attribute for displaying the
1703       // special icon. Otherwise remove the indicator attribute.
1704       if (disabled) {
1705         $('metrics-reporting-disabled-icon').setAttribute('controlled-by',
1706                                                           'policy');
1707       } else {
1708         $('metrics-reporting-disabled-icon').removeAttribute('controlled-by');
1709       }
1710     },
1712     /**
1713      * @private
1714      */
1715     setMetricsReportingSettingVisibility_: function(visible) {
1716       if (visible)
1717         $('metricsReportingSetting').style.display = 'block';
1718       else
1719         $('metricsReportingSetting').style.display = 'none';
1720     },
1722     /**
1723      * Set network prediction checkbox value.
1724      *
1725      * @param {{value: number, disabled: boolean}} pref Information about
1726      *     network prediction options. |pref.value| is the value of network
1727      *     prediction options. |pref.disabled| shows if the pref is not user
1728      *     modifiable.
1729      * @private
1730      */
1731     setNetworkPredictionValue_: function(pref) {
1732       var checkbox = $('networkPredictionOptions');
1733       checkbox.disabled = pref.disabled;
1734       if (pref.value == NetworkPredictionOptions.UNSET) {
1735         checkbox.checked = (NetworkPredictionOptions.DEFAULT !=
1736             NetworkPredictionOptions.NEVER);
1737       } else {
1738         checkbox.checked = (pref.value != NetworkPredictionOptions.NEVER);
1739       }
1740     },
1742     /**
1743      * Set the font size selected item. This item actually reflects two
1744      * preferences: the default font size and the default fixed font size.
1745      *
1746      * @param {{value: number, disabled: boolean, controlledBy: string}} pref
1747      *     Information about the font size preferences. |pref.value| is the
1748      *     value of the default font size pref. |pref.disabled| is true if
1749      *     either pref not user modifiable. |pref.controlledBy| is the source of
1750      *     the pref value(s) if either pref is currently not controlled by the
1751      *     user.
1752      * @private
1753      */
1754     setFontSize_: function(pref) {
1755       var selectCtl = $('defaultFontSize');
1756       selectCtl.disabled = pref.disabled;
1757       // Create a synthetic pref change event decorated as
1758       // CoreOptionsHandler::CreateValueForPref() does.
1759       var event = new Event('synthetic-font-size');
1760       event.value = {
1761         value: pref.value,
1762         controlledBy: pref.controlledBy,
1763         disabled: pref.disabled
1764       };
1765       $('font-size-indicator').handlePrefChange(event);
1767       for (var i = 0; i < selectCtl.options.length; i++) {
1768         if (selectCtl.options[i].value == pref.value) {
1769           selectCtl.selectedIndex = i;
1770           if ($('Custom'))
1771             selectCtl.remove($('Custom').index);
1772           return;
1773         }
1774       }
1776       // Add/Select Custom Option in the font size label list.
1777       if (!$('Custom')) {
1778         var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1779                                 -1, false, true);
1780         option.setAttribute('id', 'Custom');
1781         selectCtl.add(option);
1782       }
1783       $('Custom').selected = true;
1784     },
1786     /**
1787      * Populate the page zoom selector with values received from the caller.
1788      * @param {Array} items An array of items to populate the selector.
1789      *     each object is an array with three elements as follows:
1790      *       0: The title of the item (string).
1791      *       1: The value of the item (number).
1792      *       2: Whether the item should be selected (boolean).
1793      * @private
1794      */
1795     setupPageZoomSelector_: function(items) {
1796       var element = $('defaultZoomFactor');
1798       // Remove any existing content.
1799       element.textContent = '';
1801       // Insert new child nodes into select element.
1802       var value, title, selected;
1803       for (var i = 0; i < items.length; i++) {
1804         title = items[i][0];
1805         value = items[i][1];
1806         selected = items[i][2];
1807         element.appendChild(new Option(title, value, false, selected));
1808       }
1809     },
1811     /**
1812      * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1813      * animation.
1814      * @param {boolean} display Whether to show the button and label or not.
1815      * @private
1816      */
1817     setAutoOpenFileTypesDisplayed_: function(display) {
1818       if ($('advanced-settings').hidden) {
1819         // If the Advanced section is hidden, don't animate the transition.
1820         $('auto-open-file-types-section').hidden = !display;
1821       } else {
1822         if (display) {
1823           this.showSectionWithAnimation_(
1824               $('auto-open-file-types-section'),
1825               $('auto-open-file-types-container'));
1826         } else {
1827           this.hideSectionWithAnimation_(
1828               $('auto-open-file-types-section'),
1829               $('auto-open-file-types-container'));
1830         }
1831       }
1832     },
1834     /**
1835      * Set the enabled state for the proxy settings button and its associated
1836      * message when extension controlled.
1837      * @param {boolean} disabled Whether the button should be disabled.
1838      * @param {boolean} extensionControlled Whether the proxy is extension
1839      *     controlled.
1840      * @private
1841      */
1842     setupProxySettingsButton_: function(disabled, extensionControlled) {
1843       if (!cr.isChromeOS) {
1844         $('proxiesConfigureButton').disabled = disabled;
1845         $('proxiesLabel').textContent =
1846             loadTimeData.getString(extensionControlled ?
1847                 'proxiesLabelExtension' : 'proxiesLabelSystem');
1848       }
1849     },
1851     /**
1852      * Set the initial state of the spoken feedback checkbox.
1853      * @private
1854      */
1855     setSpokenFeedbackCheckboxState_: function(checked) {
1856       $('accessibility-spoken-feedback-check').checked = checked;
1857     },
1859     /**
1860      * Set the initial state of the high contrast checkbox.
1861      * @private
1862      */
1863     setHighContrastCheckboxState_: function(checked) {
1864       $('accessibility-high-contrast-check').checked = checked;
1865     },
1867     /**
1868      * Set the initial state of the virtual keyboard checkbox.
1869      * @private
1870      */
1871     setVirtualKeyboardCheckboxState_: function(checked) {
1872       // TODO(zork): Update UI
1873     },
1875     /**
1876      * Show/hide mouse settings slider.
1877      * @private
1878      */
1879     showMouseControls_: function(show) {
1880       $('mouse-settings').hidden = !show;
1881     },
1883     /**
1884      * Adds hidden warning boxes for settings potentially controlled by
1885      * extensions.
1886      * @param {string} parentDiv The div name to append the bubble to.
1887      * @param {string} bubbleId The ID to use for the bubble.
1888      * @param {boolean} first Add as first node if true, otherwise last.
1889      * @private
1890      */
1891     addExtensionControlledBox_: function(parentDiv, bubbleId, first) {
1892       var bubble = $('extension-controlled-warning-template').cloneNode(true);
1893       bubble.id = bubbleId;
1894       var parent = $(parentDiv);
1895       if (first)
1896         parent.insertBefore(bubble, parent.firstChild);
1897       else
1898         parent.appendChild(bubble);
1899     },
1901     /**
1902      * Adds a bubble showing that an extension is controlling a particular
1903      * setting.
1904      * @param {string} parentDiv The div name to append the bubble to.
1905      * @param {string} bubbleId The ID to use for the bubble.
1906      * @param {string} extensionId The ID of the controlling extension.
1907      * @param {string} extensionName The name of the controlling extension.
1908      * @private
1909      */
1910     toggleExtensionControlledBox_: function(
1911         parentDiv, bubbleId, extensionId, extensionName) {
1912       var bubble = $(bubbleId);
1913       assert(bubble);
1914       bubble.hidden = extensionId.length == 0;
1915       if (bubble.hidden)
1916         return;
1918       // Set the extension image.
1919       var div = bubble.firstElementChild;
1920       div.style.backgroundImage =
1921           'url(chrome://extension-icon/' + extensionId + '/24/1)';
1923       // Set the bubble label.
1924       var label = loadTimeData.getStringF('extensionControlled', extensionName);
1925       var docFrag = parseHtmlSubset('<div>' + label + '</div>', ['B', 'DIV']);
1926       div.innerHTML = docFrag.firstChild.innerHTML;
1928       // Wire up the button to disable the right extension.
1929       var button = div.nextElementSibling;
1930       button.dataset.extensionId = extensionId;
1931     },
1933     /**
1934      * Toggles the warning boxes that show which extension is controlling
1935      * various settings of Chrome.
1936      * @param {{searchEngine: options.ExtensionData,
1937      *          homePage: options.ExtensionData,
1938      *          startUpPage: options.ExtensionData,
1939      *          newTabPage: options.ExtensionData,
1940      *          proxy: options.ExtensionData}} details A dictionary of ID+name
1941      *     pairs for each of the settings controlled by an extension.
1942      * @private
1943      */
1944     toggleExtensionIndicators_: function(details) {
1945       this.toggleExtensionControlledBox_('search-section-content',
1946                                          'search-engine-controlled',
1947                                          details.searchEngine.id,
1948                                          details.searchEngine.name);
1949       this.toggleExtensionControlledBox_('extension-controlled-container',
1950                                          'homepage-controlled',
1951                                          details.homePage.id,
1952                                          details.homePage.name);
1953       this.toggleExtensionControlledBox_('startup-section-content',
1954                                          'startpage-controlled',
1955                                          details.startUpPage.id,
1956                                          details.startUpPage.name);
1957       this.toggleExtensionControlledBox_('newtab-section-content',
1958                                          'newtab-controlled',
1959                                          details.newTabPage.id,
1960                                          details.newTabPage.name);
1961       this.toggleExtensionControlledBox_('proxy-section-content',
1962                                          'proxy-controlled',
1963                                          details.proxy.id,
1964                                          details.proxy.name);
1966       // The proxy section contains just the warning box and nothing else, so
1967       // if we're hiding the proxy warning box, we should also hide its header
1968       // section.
1969       $('proxy-section').hidden = details.proxy.id.length == 0;
1970     },
1973     /**
1974      * Show/hide touchpad-related settings.
1975      * @private
1976      */
1977     showTouchpadControls_: function(show) {
1978       $('touchpad-settings').hidden = !show;
1979       $('accessibility-tap-dragging').hidden = !show;
1980     },
1982     /**
1983      * Activate the Bluetooth settings section on the System settings page.
1984      * @private
1985      */
1986     showBluetoothSettings_: function() {
1987       $('bluetooth-devices').hidden = false;
1988     },
1990     /**
1991      * Dectivates the Bluetooth settings section from the System settings page.
1992      * @private
1993      */
1994     hideBluetoothSettings_: function() {
1995       $('bluetooth-devices').hidden = true;
1996     },
1998     /**
1999      * Sets the state of the checkbox indicating if Bluetooth is turned on. The
2000      * state of the "Find devices" button and the list of discovered devices may
2001      * also be affected by a change to the state.
2002      * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
2003      * @private
2004      */
2005     setBluetoothState_: function(checked) {
2006       $('enable-bluetooth').checked = checked;
2007       $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
2008       $('bluetooth-add-device').hidden = !checked;
2009       $('bluetooth-reconnect-device').hidden = !checked;
2010       // Flush list of previously discovered devices if bluetooth is turned off.
2011       if (!checked) {
2012         $('bluetooth-paired-devices-list').clear();
2013         $('bluetooth-unpaired-devices-list').clear();
2014       } else {
2015         chrome.send('getPairedBluetoothDevices');
2016       }
2017     },
2019     /**
2020      * Adds an element to the list of available Bluetooth devices. If an element
2021      * with a matching address is found, the existing element is updated.
2022      * @param {{name: string,
2023      *          address: string,
2024      *          paired: boolean,
2025      *          connected: boolean}} device
2026      *     Decription of the Bluetooth device.
2027      * @private
2028      */
2029     addBluetoothDevice_: function(device) {
2030       var list = $('bluetooth-unpaired-devices-list');
2031       // Display the "connecting" (already paired or not yet paired) and the
2032       // paired devices in the same list.
2033       if (device.paired || device.connecting) {
2034         // Test to see if the device is currently in the unpaired list, in which
2035         // case it should be removed from that list.
2036         var index = $('bluetooth-unpaired-devices-list').find(device.address);
2037         if (index != undefined)
2038           $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
2039         list = $('bluetooth-paired-devices-list');
2040       } else {
2041         // Test to see if the device is currently in the paired list, in which
2042         // case it should be removed from that list.
2043         var index = $('bluetooth-paired-devices-list').find(device.address);
2044         if (index != undefined)
2045           $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
2046       }
2047       list.appendDevice(device);
2049       // One device can be in the process of pairing.  If found, display
2050       // the Bluetooth pairing overlay.
2051       if (device.pairing)
2052         BluetoothPairing.showDialog(device);
2053     },
2055     /**
2056      * Removes an element from the list of available devices.
2057      * @param {string} address Unique address of the device.
2058      * @private
2059      */
2060     removeBluetoothDevice_: function(address) {
2061       var index = $('bluetooth-unpaired-devices-list').find(address);
2062       if (index != undefined) {
2063         $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
2064       } else {
2065         index = $('bluetooth-paired-devices-list').find(address);
2066         if (index != undefined)
2067           $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
2068       }
2069     },
2071     /**
2072      * Shows the overlay dialog for changing the user avatar image.
2073      * @private
2074      */
2075     showImagerPickerOverlay_: function() {
2076       PageManager.showPageByName('changePicture');
2077     },
2079     /**
2080      * Shows (or not) the "User" section of the settings page based on whether
2081      * any of the sub-sections are present (or not).
2082      * @private
2083      */
2084     maybeShowUserSection_: function() {
2085       $('sync-users-section').hidden =
2086           $('profiles-section').hidden &&
2087           $('sync-section').hidden &&
2088           $('profiles-supervised-dashboard-tip').hidden;
2089     },
2091     /**
2092      * Updates the date and time section with time sync information.
2093      * @param {boolean} canSetTime Whether the system time can be set.
2094      * @private
2095      */
2096     setCanSetTime_: function(canSetTime) {
2097       // If the time has been network-synced, it cannot be set manually.
2098       $('time-synced-explanation').hidden = canSetTime;
2099       $('set-time').hidden = !canSetTime;
2100     },
2102     /**
2103      * Handle the 'set date and time' button click.
2104      * @private
2105      */
2106     handleSetTime_: function() {
2107       chrome.send('showSetTime');
2108     },
2110     /**
2111      * Enables the given element if possible; on Chrome OS, it won't enable
2112      * an element that must stay disabled for the session type.
2113      * @param {!Element} element Element to enable.
2114      */
2115     enableElementIfPossible_: function(element) {
2116       if (cr.isChromeOS)
2117         UIAccountTweaks.enableElementIfPossible(element);
2118       else
2119         element.disabled = false;
2120     },
2122     /**
2123      * Sets the icon in the battery section.
2124      * @param {string} iconData The data representing the icon to display.
2125      * @private
2126      */
2127     setBatteryIcon_: function(iconData) {
2128       $('battery-icon').style.backgroundImage = 'url(' + iconData + ')';
2129       $('battery-icon').hidden = false;
2130     },
2132     /**
2133      * Sets the text for the battery section.
2134      * @param {string} statusText The battery status, with a relevant label.
2135      * @private
2136      */
2137     setBatteryStatusText_: function(statusText) {
2138       $('battery').hidden = !statusText.length;
2139       if (statusText.length) {
2140         $('battery-status').textContent = statusText;
2141         chrome.send('requestBatteryIcon');
2142       }
2143     },
2144   };
2146   //Forward public APIs to private implementations.
2147   cr.makePublic(BrowserOptions, [
2148     'addBluetoothDevice',
2149     'deleteCurrentProfile',
2150     'enableCertificateButton',
2151     'enableDisplayButton',
2152     'enableFactoryResetSection',
2153     'getCurrentProfile',
2154     'getStartStopSyncButton',
2155     'hideBluetoothSettings',
2156     'notifyInitializationComplete',
2157     'removeBluetoothDevice',
2158     'scrollToSection',
2159     'setAccountPictureManaged',
2160     'setWallpaperManaged',
2161     'setAutoOpenFileTypesDisplayed',
2162     'setBatteryIcon',
2163     'setBatteryStatusText',
2164     'setBluetoothState',
2165     'setCanSetTime',
2166     'setFontSize',
2167     'setHotwordRetrainLinkVisible',
2168     'setNativeThemeButtonEnabled',
2169     'setNetworkPredictionValue',
2170     'setHighContrastCheckboxState',
2171     'setAllHotwordSectionsVisible',
2172     'setMetricsReportingCheckboxState',
2173     'setMetricsReportingSettingVisibility',
2174     'setProfilesInfo',
2175     'setSpokenFeedbackCheckboxState',
2176     'setThemesResetButtonEnabled',
2177     'setVirtualKeyboardCheckboxState',
2178     'setupPageZoomSelector',
2179     'setupProxySettingsButton',
2180     'setAudioHistorySectionVisible',
2181     'showBluetoothSettings',
2182     'showCreateProfileError',
2183     'showCreateProfileSuccess',
2184     'showCreateProfileWarning',
2185     'showHotwordAlwaysOnSection',
2186     'showHotwordNoDspSection',
2187     'showHotwordSection',
2188     'showMouseControls',
2189     'showSupervisedUserImportError',
2190     'showSupervisedUserImportSuccess',
2191     'showTouchpadControls',
2192     'toggleExtensionIndicators',
2193     'updateAccountPicture',
2194     'updateAutoLaunchState',
2195     'updateDefaultBrowserState',
2196     'updateEasyUnlock',
2197     'updateManagesSupervisedUsers',
2198     'updateSearchEngines',
2199     'updateSyncState',
2200   ]);
2202   if (cr.isChromeOS) {
2203     /**
2204      * Returns username (canonical email) of the user logged in (ChromeOS only).
2205      * @return {string} user email.
2206      */
2207     // TODO(jhawkins): Investigate the use case for this method.
2208     BrowserOptions.getLoggedInUsername = function() {
2209       return BrowserOptions.getInstance().username_;
2210     };
2212     /**
2213      * Shows different button text for each consumer management enrollment
2214      * status.
2215      * @enum {string} status Consumer management service status string.
2216      */
2217     BrowserOptions.setConsumerManagementStatus = function(status) {
2218       var button = $('consumer-management-button');
2219       if (status == 'StatusUnknown') {
2220         button.hidden = true;
2221         return;
2222       }
2224       button.hidden = false;
2225       var strId;
2226       switch (status) {
2227         case ConsumerManagementOverlay.Status.STATUS_UNENROLLED:
2228           strId = 'consumerManagementEnrollButton';
2229           button.disabled = false;
2230           ConsumerManagementOverlay.setStatus(status);
2231           break;
2232         case ConsumerManagementOverlay.Status.STATUS_ENROLLING:
2233           strId = 'consumerManagementEnrollingButton';
2234           button.disabled = true;
2235           break;
2236         case ConsumerManagementOverlay.Status.STATUS_ENROLLED:
2237           strId = 'consumerManagementUnenrollButton';
2238           button.disabled = false;
2239           ConsumerManagementOverlay.setStatus(status);
2240           break;
2241         case ConsumerManagementOverlay.Status.STATUS_UNENROLLING:
2242           strId = 'consumerManagementUnenrollingButton';
2243           button.disabled = true;
2244           break;
2245       }
2246       button.textContent = loadTimeData.getString(strId);
2247     };
2248   }
2250   // Export
2251   return {
2252     BrowserOptions: BrowserOptions
2253   };