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