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