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