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