1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 cr.define('options', function() {
6 var OptionsPage = options.OptionsPage;
7 var Page = cr.ui.pageManager.Page;
8 var PageManager = cr.ui.pageManager.PageManager;
9 var ArrayDataModel = cr.ui.ArrayDataModel;
10 var RepeatingButton = cr.ui.RepeatingButton;
11 var HotwordSearchSettingIndicator = options.HotwordSearchSettingIndicator;
12 var NetworkPredictionOptions = {
21 // BrowserOptions class
22 // Encapsulated handling of browser options page.
24 function BrowserOptions() {
25 Page.call(this, 'settings', loadTimeData.getString('settingsTitle'),
29 cr.addSingletonGetter(BrowserOptions);
32 * @param {HTMLElement} section The section to show or hide.
33 * @return {boolean} Whether the section should be shown.
36 BrowserOptions.shouldShowSection_ = function(section) {
37 // If the section is hidden or hiding, it should be shown.
38 return section.style.height == '' || section.style.height == '0px';
41 BrowserOptions.prototype = {
42 __proto__: Page.prototype,
45 * Keeps track of whether the user is signed in or not.
52 * Indicates whether signing out is allowed or whether a complete profile
53 * wipe is required to remove the current enterprise account.
57 signoutAllowed_: true,
60 * Keeps track of whether |onShowHomeButtonChanged_| has been called. See
61 * |onShowHomeButtonChanged_|.
65 onShowHomeButtonChangedCalled_: false,
68 * Track if page initialization is complete. All C++ UI handlers have the
69 * chance to manipulate page content within their InitializePage methods.
70 * This flag is set to true after all initializers have been called.
74 initializationComplete_: false,
77 * When a section is waiting to change its height, this will be a number.
78 * Otherwise it'll be null.
82 sectionHeightChangeTimeout_: null,
85 initializePage: function() {
86 Page.prototype.initializePage.call(this);
89 if (window.top != window) {
90 // The options page is not in its own window.
91 document.body.classList.add('uber-frame');
92 PageManager.horizontalOffset = 155;
95 // Ensure that navigation events are unblocked on uber page. A reload of
96 // the settings page while an overlay is open would otherwise leave uber
97 // page in a blocked state, where tab switching is not possible.
98 uber.invokeMethodOnParent('stopInterceptingEvents');
100 window.addEventListener('message', this.handleWindowMessage_.bind(this));
102 if (loadTimeData.getBoolean('allowAdvancedSettings')) {
103 $('advanced-settings-expander').onclick = function() {
105 BrowserOptions.shouldShowSection_($('advanced-settings'));
107 chrome.send('coreOptionsUserMetricsAction',
108 ['Options_ShowAdvancedSettings']);
110 self.toggleSectionWithAnimation_(
111 $('advanced-settings'),
112 $('advanced-settings-container'));
114 // If the link was focused (i.e., it was activated using the keyboard)
115 // and it was used to show the section (rather than hiding it), focus
116 // the first element in the container.
117 if (document.activeElement === $('advanced-settings-expander') &&
119 var focusElement = $('advanced-settings-container').querySelector(
120 'button, input, list, select, a[href]');
122 focusElement.focus();
126 $('advanced-settings-expander').hidden = true;
127 $('advanced-settings').hidden = true;
130 $('advanced-settings').addEventListener('webkitTransitionEnd',
131 this.updateAdvancedSettingsExpander_.bind(this));
133 if (cr.isChromeOS && loadTimeData.getBoolean('showVersion')) {
134 $('version-button').hidden = false;
135 $('version-button').addEventListener('click', function() {
136 PageManager.showPageByName('help');
137 chrome.send('coreOptionsUserMetricsAction',
143 UIAccountTweaks.applyGuestSessionVisibility(document);
144 UIAccountTweaks.applyPublicSessionVisibility(document);
145 if (loadTimeData.getBoolean('secondaryUser'))
146 $('secondary-user-banner').hidden = false;
149 // Sync (Sign in) section.
150 this.updateSyncState_(loadTimeData.getValue('syncData'));
152 $('start-stop-sync').onclick = function(event) {
153 if (self.signedIn_) {
154 if (self.signoutAllowed_)
155 SyncSetupOverlay.showStopSyncingUI();
157 chrome.send('showDisconnectManagedProfileDialog');
158 } else if (cr.isChromeOS) {
159 SyncSetupOverlay.showSetupUI();
161 SyncSetupOverlay.startSignIn();
164 $('customize-sync').onclick = function(event) {
165 SyncSetupOverlay.showSetupUI();
168 // Internet connection section (ChromeOS only).
170 options.network.NetworkList.decorate($('network-list'));
171 // Show that the network settings are shared if this is a secondary user
172 // in a multi-profile session.
173 if (loadTimeData.getBoolean('secondaryUser')) {
174 var networkIndicator = document.querySelector(
175 '#network-section-header > .controlled-setting-indicator');
176 networkIndicator.setAttribute('controlled-by', 'shared');
177 networkIndicator.location = cr.ui.ArrowLocation.TOP_START;
179 options.network.NetworkList.refreshNetworkData(
180 loadTimeData.getValue('networkData'));
183 // On Startup section.
184 Preferences.getInstance().addEventListener('session.restore_on_startup',
185 this.onRestoreOnStartupChanged_.bind(this));
186 Preferences.getInstance().addEventListener(
187 'session.startup_urls',
189 $('startup-set-pages').disabled = event.value.disabled;
192 $('startup-set-pages').onclick = function() {
193 PageManager.showPageByName('startup');
196 // Appearance section.
197 Preferences.getInstance().addEventListener('browser.show_home_button',
198 this.onShowHomeButtonChanged_.bind(this));
200 Preferences.getInstance().addEventListener('homepage',
201 this.onHomePageChanged_.bind(this));
202 Preferences.getInstance().addEventListener('homepage_is_newtabpage',
203 this.onHomePageIsNtpChanged_.bind(this));
205 $('change-home-page').onclick = function(event) {
206 PageManager.showPageByName('homePageOverlay');
207 chrome.send('coreOptionsUserMetricsAction',
208 ['Options_Homepage_ShowSettings']);
211 var hotwordIndicator = $('hotword-search-setting-indicator');
212 HotwordSearchSettingIndicator.decorate(hotwordIndicator);
213 hotwordIndicator.disabledOnErrorSection = $('hotword-search-enable');
214 chrome.send('requestHotwordAvailable');
216 if ($('set-wallpaper')) {
217 $('set-wallpaper').onclick = function(event) {
218 chrome.send('openWallpaperManager');
219 chrome.send('coreOptionsUserMetricsAction',
220 ['Options_OpenWallpaperManager']);
224 $('themes-gallery').onclick = function(event) {
225 window.open(loadTimeData.getString('themesGalleryURL'));
226 chrome.send('coreOptionsUserMetricsAction',
227 ['Options_ThemesGallery']);
229 $('themes-reset').onclick = function(event) {
230 chrome.send('themesReset');
233 if (loadTimeData.getBoolean('profileIsSupervised')) {
234 if ($('themes-native-button')) {
235 $('themes-native-button').disabled = true;
236 $('themes-native-button').hidden = true;
238 // Supervised users have just one default theme, even on Linux. So use
239 // the same button for Linux as for the other platforms.
240 $('themes-reset').textContent = loadTimeData.getString('themesReset');
243 // Device section (ChromeOS only).
245 $('keyboard-settings-button').onclick = function(evt) {
246 PageManager.showPageByName('keyboard-overlay');
247 chrome.send('coreOptionsUserMetricsAction',
248 ['Options_ShowKeyboardSettings']);
250 $('pointer-settings-button').onclick = function(evt) {
251 PageManager.showPageByName('pointer-overlay');
252 chrome.send('coreOptionsUserMetricsAction',
253 ['Options_ShowTouchpadSettings']);
258 $('manage-default-search-engines').onclick = function(event) {
259 PageManager.showPageByName('searchEngines');
260 chrome.send('coreOptionsUserMetricsAction',
261 ['Options_ManageSearchEngines']);
263 $('default-search-engine').addEventListener('change',
264 this.setDefaultSearchEngine_);
267 if (loadTimeData.valueExists('profilesInfo')) {
268 $('profiles-section').hidden = false;
269 this.maybeShowUserSection_();
271 var profilesList = $('profiles-list');
272 options.browser_options.ProfileList.decorate(profilesList);
273 profilesList.autoExpands = true;
275 // The profiles info data in |loadTimeData| might be stale.
276 this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
277 chrome.send('requestProfilesInfo');
279 profilesList.addEventListener('change',
280 this.setProfileViewButtonsStatus_);
281 $('profiles-create').onclick = function(event) {
282 ManageProfileOverlay.showCreateDialog();
284 if (OptionsPage.isSettingsApp()) {
285 $('profiles-app-list-switch').onclick = function(event) {
286 var selectedProfile = self.getSelectedProfileItem_();
287 chrome.send('switchAppListProfile', [selectedProfile.filePath]);
290 $('profiles-manage').onclick = function(event) {
291 ManageProfileOverlay.showManageDialog();
293 $('profiles-delete').onclick = function(event) {
294 var selectedProfile = self.getSelectedProfileItem_();
296 ManageProfileOverlay.showDeleteDialog(selectedProfile);
298 if (loadTimeData.getBoolean('profileIsSupervised')) {
299 $('profiles-create').disabled = true;
300 $('profiles-delete').disabled = true;
301 $('profiles-list').canDeleteItems = false;
306 // Username (canonical email) of the currently logged in user or
307 // |kGuestUser| if a guest session is active.
308 this.username_ = loadTimeData.getString('username');
310 this.updateAccountPicture_();
312 $('account-picture').onclick = this.showImagerPickerOverlay_;
313 $('change-picture-caption').onclick = this.showImagerPickerOverlay_;
315 $('manage-accounts-button').onclick = function(event) {
316 PageManager.showPageByName('accounts');
317 chrome.send('coreOptionsUserMetricsAction',
318 ['Options_ManageAccounts']);
321 document.querySelector(
322 '#enable-screen-lock + span > .controlled-setting-indicator').
323 setAttribute('textshared',
324 loadTimeData.getString('screenLockShared'));
326 $('import-data').onclick = function(event) {
327 ImportDataOverlay.show();
328 chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
331 if ($('themes-native-button')) {
332 $('themes-native-button').onclick = function(event) {
333 chrome.send('themesSetNative');
338 // Date and time section (CrOS only).
339 if ($('set-time-button'))
340 $('set-time-button').onclick = this.handleSetTime_.bind(this);
342 // Default browser section.
343 if (!cr.isChromeOS) {
344 if (!loadTimeData.getBoolean('showSetDefault')) {
345 $('set-default-browser-section').hidden = true;
347 $('set-as-default-browser').onclick = function(event) {
348 chrome.send('becomeDefaultBrowser');
351 $('auto-launch').onclick = this.handleAutoLaunchChanged_;
355 $('privacyContentSettingsButton').onclick = function(event) {
356 PageManager.showPageByName('content');
357 OptionsPage.showTab($('cookies-nav-tab'));
358 chrome.send('coreOptionsUserMetricsAction',
359 ['Options_ContentSettings']);
361 $('privacyClearDataButton').onclick = function(event) {
362 PageManager.showPageByName('clearBrowserData');
363 chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
365 $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
366 // 'metricsReportingEnabled' element is only present on Chrome branded
367 // builds, and the 'metricsReportingCheckboxAction' message is only
368 // handled on ChromeOS.
369 if ($('metricsReportingEnabled') && cr.isChromeOS) {
370 $('metricsReportingEnabled').onclick = function(event) {
371 chrome.send('metricsReportingCheckboxAction',
372 [String(event.currentTarget.checked)]);
375 if ($('metricsReportingEnabled') && !cr.isChromeOS) {
376 // The localized string has the | symbol on each side of the text that
377 // needs to be made into a button to restart Chrome. We parse the text
378 // and build the button from that.
379 var restartTextFragments =
380 loadTimeData.getString('metricsReportingResetRestart').split('|');
381 // Assume structure is something like "starting text |link text| ending
382 // text" where both starting text and ending text may or may not be
383 // present, but the split should always be in three pieces.
384 var restartElements =
385 $('metrics-reporting-reset-restart').querySelectorAll('*');
386 for (var i = 0; i < restartTextFragments.length; i++) {
387 restartElements[i].textContent = restartTextFragments[i];
389 restartElements[1].onclick = function(event) {
390 chrome.send('restartBrowser');
392 var updateMetricsRestartButton = function() {
393 $('metrics-reporting-reset-restart').hidden =
394 loadTimeData.getBoolean('metricsReportingEnabledAtStart') ==
395 $('metricsReportingEnabled').checked;
397 Preferences.getInstance().addEventListener(
398 $('metricsReportingEnabled').getAttribute('pref'),
399 updateMetricsRestartButton);
400 updateMetricsRestartButton();
402 $('networkPredictionOptions').onchange = function(event) {
403 var value = (event.target.checked ?
404 NetworkPredictionOptions.WIFI_ONLY :
405 NetworkPredictionOptions.NEVER);
406 var metric = event.target.metric;
407 Preferences.setIntegerPref(
408 'net.network_prediction_options',
414 // Bluetooth (CrOS only).
416 options.system.bluetooth.BluetoothDeviceList.decorate(
417 $('bluetooth-paired-devices-list'));
419 $('bluetooth-add-device').onclick =
420 this.handleAddBluetoothDevice_.bind(this);
422 $('enable-bluetooth').onchange = function(event) {
423 var state = $('enable-bluetooth').checked;
424 chrome.send('bluetoothEnableChange', [Boolean(state)]);
427 $('bluetooth-reconnect-device').onclick = function(event) {
428 var device = $('bluetooth-paired-devices-list').selectedItem;
429 var address = device.address;
430 chrome.send('updateBluetoothDevice', [address, 'connect']);
431 PageManager.closeOverlay();
434 $('bluetooth-paired-devices-list').addEventListener('change',
436 var item = $('bluetooth-paired-devices-list').selectedItem;
437 var disabled = !item || item.connected || !item.connectable;
438 $('bluetooth-reconnect-device').disabled = disabled;
442 // Passwords and Forms section.
443 $('autofill-settings').onclick = function(event) {
444 PageManager.showPageByName('autofill');
445 chrome.send('coreOptionsUserMetricsAction',
446 ['Options_ShowAutofillSettings']);
448 $('manage-passwords').onclick = function(event) {
449 PageManager.showPageByName('passwords');
450 OptionsPage.showTab($('passwords-nav-tab'));
451 chrome.send('coreOptionsUserMetricsAction',
452 ['Options_ShowPasswordManager']);
454 if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
455 // Disable and turn off Autofill in guest mode.
456 var autofillEnabled = $('autofill-enabled');
457 autofillEnabled.disabled = true;
458 autofillEnabled.checked = false;
459 cr.dispatchSimpleEvent(autofillEnabled, 'change');
460 $('autofill-settings').disabled = true;
462 // Disable and turn off Password Manager in guest mode.
463 var passwordManagerEnabled = $('password-manager-enabled');
464 passwordManagerEnabled.disabled = true;
465 passwordManagerEnabled.checked = false;
466 cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
467 $('manage-passwords').disabled = true;
471 $('mac-passwords-warning').hidden =
472 !loadTimeData.getBoolean('multiple_profiles');
476 if (!cr.isChromeOS) {
477 $('proxiesConfigureButton').onclick = function(event) {
478 chrome.send('showNetworkProxySettings');
482 // Device control section.
484 UIAccountTweaks.currentUserIsOwner() &&
485 loadTimeData.getBoolean('consumerManagementEnabled')) {
486 $('device-control-section').hidden = false;
488 var isEnrolled = loadTimeData.getBoolean('consumerManagementEnrolled');
489 $('consumer-management-enroll').hidden = isEnrolled;
490 $('consumer-management-unenroll').hidden = !isEnrolled;
492 $('consumer-management-section').onclick = function(event) {
493 // If either button is clicked.
494 if (event.target.tagName == 'BUTTON')
495 PageManager.showPageByName('consumer-management-overlay');
499 // Easy Unlock section.
500 if (loadTimeData.getBoolean('easyUnlockAllowed')) {
501 $('easy-unlock-section').hidden = false;
502 $('easy-unlock-setup-button').onclick = function(event) {
503 chrome.send('launchEasyUnlockSetup');
505 $('easy-unlock-turn-off-button').onclick = function(event) {
506 PageManager.showPageByName('easyUnlockTurnOffOverlay');
510 // Website Settings section.
511 if (loadTimeData.getBoolean('websiteSettingsManagerEnabled')) {
512 $('website-settings-section').hidden = false;
513 $('website-management-button').onclick = function(event) {
514 PageManager.showPageByName('websiteSettings');
518 // Web Content section.
519 $('fontSettingsCustomizeFontsButton').onclick = function(event) {
520 PageManager.showPageByName('fonts');
521 chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
523 $('defaultFontSize').onchange = function(event) {
524 var value = event.target.options[event.target.selectedIndex].value;
525 Preferences.setIntegerPref(
526 'webkit.webprefs.default_fixed_font_size',
527 value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
528 chrome.send('defaultFontSizeAction', [String(value)]);
530 $('defaultZoomFactor').onchange = function(event) {
531 chrome.send('defaultZoomFactorAction',
532 [String(event.target.options[event.target.selectedIndex].value)]);
535 // Languages section.
536 var showLanguageOptions = function(event) {
537 PageManager.showPageByName('languages');
538 chrome.send('coreOptionsUserMetricsAction',
539 ['Options_LanuageAndSpellCheckSettings']);
541 $('language-button').onclick = showLanguageOptions;
542 $('manage-languages').onclick = showLanguageOptions;
544 // Downloads section.
545 Preferences.getInstance().addEventListener('download.default_directory',
546 this.onDefaultDownloadDirectoryChanged_.bind(this));
547 $('downloadLocationChangeButton').onclick = function(event) {
548 chrome.send('selectDownloadLocation');
551 $('disable-drive-row').hidden =
552 UIAccountTweaks.loggedInAsSupervisedUser();
554 $('autoOpenFileTypesResetToDefault').onclick = function(event) {
555 chrome.send('autoOpenFileTypesAction');
558 // HTTPS/SSL section.
559 if (cr.isWindows || cr.isMac) {
560 $('certificatesManageButton').onclick = function(event) {
561 chrome.send('showManageSSLCertificates');
564 $('certificatesManageButton').onclick = function(event) {
565 PageManager.showPageByName('certificates');
566 chrome.send('coreOptionsUserMetricsAction',
567 ['Options_ManageSSLCertificates']);
571 if (loadTimeData.getBoolean('cloudPrintShowMDnsOptions')) {
572 $('cloudprint-options-mdns').hidden = false;
573 $('cloudPrintDevicesPageButton').onclick = function() {
574 chrome.send('showCloudPrintDevicesPage');
578 // Accessibility section (CrOS only).
580 var updateAccessibilitySettingsButton = function() {
581 $('accessibility-settings').hidden =
582 !($('accessibility-spoken-feedback-check').checked);
584 Preferences.getInstance().addEventListener(
585 'settings.accessibility',
586 updateAccessibilitySettingsButton);
587 $('accessibility-learn-more').onclick = function(unused_event) {
588 window.open(loadTimeData.getString('accessibilityLearnMoreURL'));
589 chrome.send('coreOptionsUserMetricsAction',
590 ['Options_AccessibilityLearnMore']);
592 $('accessibility-settings-button').onclick = function(unused_event) {
593 window.open(loadTimeData.getString('accessibilitySettingsURL'));
595 $('accessibility-spoken-feedback-check').onchange = function(
597 chrome.send('spokenFeedbackChange',
598 [$('accessibility-spoken-feedback-check').checked]);
599 updateAccessibilitySettingsButton();
601 updateAccessibilitySettingsButton();
603 $('accessibility-high-contrast-check').onchange = function(
605 chrome.send('highContrastChange',
606 [$('accessibility-high-contrast-check').checked]);
609 var updateDelayDropdown = function() {
610 $('accessibility-autoclick-dropdown').disabled =
611 !$('accessibility-autoclick-check').checked;
613 Preferences.getInstance().addEventListener(
614 $('accessibility-autoclick-check').getAttribute('pref'),
615 updateDelayDropdown);
618 // Display management section (CrOS only).
620 $('display-options').onclick = function(event) {
621 PageManager.showPageByName('display');
622 chrome.send('coreOptionsUserMetricsAction',
623 ['Options_Display']);
627 // Factory reset section (CrOS only).
629 $('factory-reset-restart').onclick = function(event) {
630 PageManager.showPageByName('factoryResetData');
631 chrome.send('onPowerwashDialogShow');
636 if (!cr.isChromeOS) {
637 var updateGpuRestartButton = function() {
638 $('gpu-mode-reset-restart').hidden =
639 loadTimeData.getBoolean('gpuEnabledAtStart') ==
640 $('gpu-mode-checkbox').checked;
642 Preferences.getInstance().addEventListener(
643 $('gpu-mode-checkbox').getAttribute('pref'),
644 updateGpuRestartButton);
645 $('gpu-mode-reset-restart-button').onclick = function(event) {
646 chrome.send('restartBrowser');
648 updateGpuRestartButton();
651 // Reset profile settings section.
652 $('reset-profile-settings').onclick = function(event) {
653 PageManager.showPageByName('resetProfileSettings');
656 // Extension controlled UI.
657 this.addExtensionControlledBox_('search-section-content',
658 'search-engine-controlled',
660 this.addExtensionControlledBox_('extension-controlled-container',
661 'homepage-controlled',
663 this.addExtensionControlledBox_('startup-section-content',
664 'startpage-controlled',
666 this.addExtensionControlledBox_('newtab-section-content',
669 this.addExtensionControlledBox_('proxy-section-content',
673 document.body.addEventListener('click', function(e) {
674 var button = findAncestor(e.target, function(el) {
675 return el.tagName == 'BUTTON' &&
676 el.dataset.extensionId !== undefined &&
677 el.dataset.extensionId.length;
680 chrome.send('disableExtension', [button.dataset.extensionId]);
685 didShowPage: function() {
686 $('search-field').focus();
690 * Called after all C++ UI handlers have called InitializePage to notify
691 * that initialization is complete.
694 notifyInitializationComplete_: function() {
695 this.initializationComplete_ = true;
696 cr.dispatchSimpleEvent(document, 'initializationComplete');
700 * Event listener for the 'session.restore_on_startup' pref.
701 * @param {Event} event The preference change event.
704 onRestoreOnStartupChanged_: function(event) {
705 /** @const */ var showHomePageValue = 0;
707 if (event.value.value == showHomePageValue) {
708 // If the user previously selected "Show the homepage", the
709 // preference will already be migrated to "Open a specific page". So
710 // the only way to reach this code is if the 'restore on startup'
711 // preference is managed.
712 assert(event.value.controlledBy);
714 // Select "open the following pages" and lock down the list of URLs
715 // to reflect the intention of the policy.
716 $('startup-show-pages').checked = true;
717 StartupOverlay.getInstance().setControlsDisabled(true);
719 // Re-enable the controls in the startup overlay if necessary.
720 StartupOverlay.getInstance().updateControlStates();
725 * Handler for messages sent from the main uber page.
726 * @param {Event} e The 'message' event from the uber page.
729 handleWindowMessage_: function(e) {
730 if (e.data.method == 'frameSelected')
731 $('search-field').focus();
735 * Animatedly changes height |from| a px number |to| a px number.
736 * @param {HTMLElement} section The section to animate.
737 * @param {HTMLElement} container The container of |section|.
738 * @param {boolean} showing Whether to go from 0 -> container height or
739 * container height -> 0.
742 animatedSectionHeightChange_: function(section, container, showing) {
743 // If the section is already animating, dispatch a synthetic transition
744 // end event as the upcoming code will cancel the current one.
745 if (section.classList.contains('sliding'))
746 cr.dispatchSimpleEvent(section, 'webkitTransitionEnd');
748 this.addTransitionEndListener_(section);
750 section.hidden = false;
751 section.style.height = (showing ? 0 : container.offsetHeight) + 'px';
752 section.classList.add('sliding');
754 if (this.sectionHeightChangeTimeout_ !== null)
755 clearTimeout(this.sectionHeightChangeTimeout_);
757 this.sectionHeightChangeTimeout_ = setTimeout(function() {
758 section.style.height = (showing ? container.offsetHeight : 0) + 'px';
759 this.sectionHeightChangeTimeout_ = null;
764 * Shows the given section.
765 * @param {HTMLElement} section The section to be shown.
766 * @param {HTMLElement} container The container for the section. Must be
767 * inside of |section|.
768 * @param {boolean} animate Indicate if the expansion should be animated.
771 showSection_: function(section, container, animate) {
772 // Delay starting the transition if animating so that hidden change will
775 this.animatedSectionHeightChange_(section, container, true);
777 section.hidden = false;
778 section.style.height = 'auto';
783 * Shows the given section, with animation.
784 * @param {HTMLElement} section The section to be shown.
785 * @param {HTMLElement} container The container for the section. Must be
786 * inside of |section|.
789 showSectionWithAnimation_: function(section, container) {
790 this.showSection_(section, container, /* animate */ true);
794 * Hides the given |section| with animation.
795 * @param {HTMLElement} section The section to be hidden.
796 * @param {HTMLElement} container The container for the section. Must be
797 * inside of |section|.
800 hideSectionWithAnimation_: function(section, container) {
801 this.animatedSectionHeightChange_(section, container, false);
805 * Toggles the visibility of |section| in an animated way.
806 * @param {HTMLElement} section The section to be toggled.
807 * @param {HTMLElement} container The container for the section. Must be
808 * inside of |section|.
811 toggleSectionWithAnimation_: function(section, container) {
812 if (BrowserOptions.shouldShowSection_(section))
813 this.showSectionWithAnimation_(section, container);
815 this.hideSectionWithAnimation_(section, container);
819 * Scrolls the settings page to make the section visible auto-expanding
820 * advanced settings if required. The transition is not animated. This
821 * method is used to ensure that a section associated with an overlay
822 * is visible when the overlay is closed.
823 * @param {!Element} section The section to make visible.
826 scrollToSection_: function(section) {
827 var advancedSettings = $('advanced-settings');
828 var container = $('advanced-settings-container');
829 var expander = $('advanced-settings-expander');
830 if (!expander.hidden &&
831 advancedSettings.hidden &&
832 section.parentNode == container) {
833 this.showSection_($('advanced-settings'),
834 $('advanced-settings-container'),
835 /* animate */ false);
836 this.updateAdvancedSettingsExpander_();
839 if (!this.initializationComplete_) {
841 var callback = function() {
842 document.removeEventListener('initializationComplete', callback);
843 self.scrollToSection_(section);
845 document.addEventListener('initializationComplete', callback);
849 var pageContainer = $('page-container');
850 // pageContainer.offsetTop is relative to the screen.
851 var pageTop = pageContainer.offsetTop;
852 var sectionBottom = section.offsetTop + section.offsetHeight;
853 // section.offsetTop is relative to the 'page-container'.
854 var sectionTop = section.offsetTop;
855 if (pageTop + sectionBottom > document.body.scrollHeight ||
856 pageTop + sectionTop < 0) {
857 // Currently not all layout updates are guaranteed to precede the
858 // initializationComplete event (for example 'set-as-default-browser'
859 // button) leaving some uncertainty in the optimal scroll position.
860 // The section is placed approximately in the middle of the screen.
861 var top = Math.min(0, document.body.scrollHeight / 2 - sectionBottom);
862 pageContainer.style.top = top + 'px';
863 pageContainer.oldScrollTop = -top;
868 * Adds a |webkitTransitionEnd| listener to the given section so that
869 * it can be animated. The listener will only be added to a given section
870 * once, so this can be called as multiple times.
871 * @param {HTMLElement} section The section to be animated.
874 addTransitionEndListener_: function(section) {
875 if (section.hasTransitionEndListener_)
878 section.addEventListener('webkitTransitionEnd',
879 this.onTransitionEnd_.bind(this));
880 section.hasTransitionEndListener_ = true;
884 * Called after an animation transition has ended.
885 * @param {Event} The webkitTransitionEnd event. NOTE: May be synthetic.
888 onTransitionEnd_: function(event) {
889 if (event.propertyName && event.propertyName != 'height') {
890 // If not a synthetic event or a real transition we care about, bail.
894 var section = event.target;
895 section.classList.remove('sliding');
897 if (!event.propertyName) {
898 // Only real transitions past this point.
902 if (section.style.height == '0px') {
903 // Hide the content so it can't get tab focus.
904 section.hidden = true;
905 section.style.height = '';
907 // Set the section height to 'auto' to allow for size changes
908 // (due to font change or dynamic content).
909 section.style.height = 'auto';
914 updateAdvancedSettingsExpander_: function() {
915 var expander = $('advanced-settings-expander');
916 if (BrowserOptions.shouldShowSection_($('advanced-settings')))
917 expander.textContent = loadTimeData.getString('showAdvancedSettings');
919 expander.textContent = loadTimeData.getString('hideAdvancedSettings');
923 * Updates the sync section with the given state.
924 * @param {Object} syncData A bunch of data records that describe the status
925 * of the sync system.
928 updateSyncState_: function(syncData) {
929 if (!syncData.signinAllowed &&
930 (!syncData.supervisedUser || !cr.isChromeOS)) {
931 $('sync-section').hidden = true;
932 this.maybeShowUserSection_();
936 $('sync-section').hidden = false;
937 this.maybeShowUserSection_();
939 if (cr.isChromeOS && syncData.supervisedUser) {
940 var subSection = $('sync-section').firstChild;
942 if (subSection.nodeType == Node.ELEMENT_NODE)
943 subSection.hidden = true;
944 subSection = subSection.nextSibling;
947 $('account-picture-wrapper').hidden = false;
948 $('sync-general').hidden = false;
949 $('sync-status').hidden = true;
954 // If the user gets signed out while the advanced sync settings dialog is
955 // visible, say, due to a dashboard clear, close the dialog.
956 // However, if the user gets signed out as a result of abandoning first
957 // time sync setup, do not call closeOverlay as it will redirect the
958 // browser to the main settings page and override any in-progress
959 // user-initiated navigation. See crbug.com/278030.
960 // Note: SyncSetupOverlay.closeOverlay is a no-op if the overlay is
962 if (this.signedIn_ && !syncData.signedIn && !syncData.setupInProgress)
963 SyncSetupOverlay.closeOverlay();
965 this.signedIn_ = syncData.signedIn;
967 // Display the "advanced settings" button if we're signed in and sync is
968 // not managed/disabled. If the user is signed in, but sync is disabled,
969 // this button is used to re-enable sync.
970 var customizeSyncButton = $('customize-sync');
971 customizeSyncButton.hidden = !this.signedIn_ ||
973 !syncData.syncSystemEnabled;
975 // Only modify the customize button's text if the new text is different.
976 // Otherwise, it can affect search-highlighting in the settings page.
977 // See http://crbug.com/268265.
978 var customizeSyncButtonNewText = syncData.setupCompleted ?
979 loadTimeData.getString('customizeSync') :
980 loadTimeData.getString('syncButtonTextStart');
981 if (customizeSyncButton.textContent != customizeSyncButtonNewText)
982 customizeSyncButton.textContent = customizeSyncButtonNewText;
984 // Disable the "sign in" button if we're currently signing in, or if we're
985 // already signed in and signout is not allowed.
986 var signInButton = $('start-stop-sync');
987 signInButton.disabled = syncData.setupInProgress;
988 this.signoutAllowed_ = syncData.signoutAllowed;
989 if (!syncData.signoutAllowed)
990 $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
992 $('start-stop-sync-indicator').removeAttribute('controlled-by');
994 // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome
995 // (except for supervised users, which can't change their signed-in
997 signInButton.hidden = cr.isChromeOS || syncData.supervisedUser;
999 signInButton.textContent =
1001 loadTimeData.getString('syncButtonTextStop') :
1002 syncData.setupInProgress ?
1003 loadTimeData.getString('syncButtonTextInProgress') :
1004 loadTimeData.getString('syncButtonTextSignIn');
1005 $('start-stop-sync-indicator').hidden = signInButton.hidden;
1007 // TODO(estade): can this just be textContent?
1008 $('sync-status-text').innerHTML = syncData.statusText;
1009 var statusSet = syncData.statusText.length != 0;
1010 $('sync-overview').hidden =
1012 (cr.isChromeOS && UIAccountTweaks.loggedInAsPublicAccount());
1013 $('sync-status').hidden = !statusSet;
1015 $('sync-action-link').textContent = syncData.actionLinkText;
1016 // Don't show the action link if it is empty or undefined.
1017 $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
1018 $('sync-action-link').disabled = syncData.managed ||
1019 !syncData.syncSystemEnabled;
1021 // On Chrome OS, sign out the user and sign in again to get fresh
1022 // credentials on auth errors.
1023 $('sync-action-link').onclick = function(event) {
1024 if (cr.isChromeOS && syncData.hasError)
1025 SyncSetupOverlay.doSignOutOnAuthError();
1027 SyncSetupOverlay.showSetupUI();
1030 if (syncData.hasError)
1031 $('sync-status').classList.add('sync-error');
1033 $('sync-status').classList.remove('sync-error');
1035 // Disable the "customize / set up sync" button if sync has an
1036 // unrecoverable error. Also disable the button if sync has not been set
1037 // up and the user is being presented with a link to re-auth.
1038 // See crbug.com/289791.
1039 customizeSyncButton.disabled =
1040 syncData.hasUnrecoverableError ||
1041 (!syncData.setupCompleted && !$('sync-action-link').hidden);
1045 * Update the UI depending on whether the current profile has a pairing for
1047 * @param {boolean} hasPairing True if the current profile has a pairing.
1049 updateEasyUnlock_: function(hasPairing) {
1050 $('easy-unlock-setup').hidden = hasPairing;
1051 $('easy-unlock-enable').hidden = !hasPairing;
1052 if (!hasPairing && EasyUnlockTurnOffOverlay.getInstance().visible) {
1053 EasyUnlockTurnOffOverlay.dismiss();
1058 * Update the UI depending on whether the current profile manages any
1060 * @param {boolean} show True if the current profile manages any supervised
1063 updateManagesSupervisedUsers_: function(show) {
1064 $('profiles-supervised-dashboard-tip').hidden = !show;
1065 this.maybeShowUserSection_();
1069 * Get the start/stop sync button DOM element. Used for testing.
1070 * @return {DOMElement} The start/stop sync button.
1073 getStartStopSyncButton_: function() {
1074 return $('start-stop-sync');
1078 * Event listener for the 'show home button' preference. Shows/hides the
1079 * UI for changing the home page with animation, unless this is the first
1080 * time this function is called, in which case there is no animation.
1081 * @param {Event} event The preference change event.
1083 onShowHomeButtonChanged_: function(event) {
1084 var section = $('change-home-page-section');
1085 if (this.onShowHomeButtonChangedCalled_) {
1086 var container = $('change-home-page-section-container');
1087 if (event.value.value)
1088 this.showSectionWithAnimation_(section, container);
1090 this.hideSectionWithAnimation_(section, container);
1092 section.hidden = !event.value.value;
1093 this.onShowHomeButtonChangedCalled_ = true;
1098 * Activates the Hotword section from the System settings page.
1099 * @param {boolean} opt_enabled Current preference state for hotwording.
1100 * @param {string} opt_error The error message to display.
1103 showHotwordSection_: function(opt_enabled, opt_error) {
1104 $('hotword-search').hidden = false;
1105 $('hotword-search-setting-indicator').setError(opt_error);
1106 if (opt_enabled && opt_error)
1107 $('hotword-search-setting-indicator').updateBasedOnError();
1111 * Activates the Audio History and Always-On Hotword sections from the
1112 * System settings page.
1115 showHotwordAlwaysOnSection_: function() {
1116 $('voice-section-title').hidden = false;
1117 $('hotword-always-on-search').hidden = false;
1118 $('audio-logging').hidden = false;
1122 * Event listener for the 'homepage is NTP' preference. Updates the label
1123 * next to the 'Change' button.
1124 * @param {Event} event The preference change event.
1126 onHomePageIsNtpChanged_: function(event) {
1127 if (!event.value.uncommitted) {
1128 $('home-page-url').hidden = event.value.value;
1129 $('home-page-ntp').hidden = !event.value.value;
1134 * Event listener for changes to the homepage preference. Updates the label
1135 * next to the 'Change' button.
1136 * @param {Event} event The preference change event.
1138 onHomePageChanged_: function(event) {
1139 if (!event.value.uncommitted)
1140 $('home-page-url').textContent = this.stripHttp_(event.value.value);
1144 * Removes the 'http://' from a URL, like the omnibox does. If the string
1145 * doesn't start with 'http://' it is returned unchanged.
1146 * @param {string} url The url to be processed
1147 * @return {string} The url with the 'http://' removed.
1149 stripHttp_: function(url) {
1150 return url.replace(/^http:\/\//, '');
1154 * Shows the autoLaunch preference and initializes its checkbox value.
1155 * @param {bool} enabled Whether autolaunch is enabled or or not.
1158 updateAutoLaunchState_: function(enabled) {
1159 $('auto-launch-option').hidden = false;
1160 $('auto-launch').checked = enabled;
1164 * Called when the value of the download.default_directory preference
1166 * @param {Event} event Change event.
1169 onDefaultDownloadDirectoryChanged_: function(event) {
1170 $('downloadLocationPath').value = event.value.value;
1171 if (cr.isChromeOS) {
1172 // On ChromeOS, replace /special/drive-<hash>/root with "Google Drive"
1173 // for remote files, /home/chronos/user/Downloads or
1174 // /home/chronos/u-<hash>/Downloads with "Downloads" for local paths,
1175 // and '/' with ' \u203a ' (angled quote sign) everywhere. The modified
1176 // path is used only for display purpose.
1177 var path = $('downloadLocationPath').value;
1178 path = path.replace(/^\/special\/drive[^\/]*\/root/, 'Google Drive');
1179 path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, '');
1180 path = path.replace(/\//g, ' \u203a ');
1181 $('downloadLocationPath').value = path;
1183 $('download-location-label').classList.toggle('disabled',
1184 event.value.disabled);
1185 $('downloadLocationChangeButton').disabled = event.value.disabled;
1189 * Update the Default Browsers section based on the current state.
1190 * @param {string} statusString Description of the current default state.
1191 * @param {boolean} isDefault Whether or not the browser is currently
1193 * @param {boolean} canBeDefault Whether or not the browser can be default.
1196 updateDefaultBrowserState_: function(statusString, isDefault,
1198 if (!cr.isChromeOS) {
1199 var label = $('default-browser-state');
1200 label.textContent = statusString;
1202 $('set-as-default-browser').hidden = !canBeDefault || isDefault;
1207 * Clears the search engine popup.
1210 clearSearchEngines_: function() {
1211 $('default-search-engine').textContent = '';
1215 * Updates the search engine popup with the given entries.
1216 * @param {Array} engines List of available search engines.
1217 * @param {number} defaultValue The value of the current default engine.
1218 * @param {boolean} defaultManaged Whether the default search provider is
1219 * managed. If true, the default search provider can't be changed.
1222 updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
1223 this.clearSearchEngines_();
1224 engineSelect = $('default-search-engine');
1225 engineSelect.disabled = defaultManaged;
1226 if (defaultManaged && defaultValue == -1)
1228 engineCount = engines.length;
1229 var defaultIndex = -1;
1230 for (var i = 0; i < engineCount; i++) {
1231 var engine = engines[i];
1232 var option = new Option(engine.name, engine.index);
1233 if (defaultValue == option.value)
1235 engineSelect.appendChild(option);
1237 if (defaultIndex >= 0)
1238 engineSelect.selectedIndex = defaultIndex;
1242 * Set the default search engine based on the popup selection.
1245 setDefaultSearchEngine_: function() {
1246 var engineSelect = $('default-search-engine');
1247 var selectedIndex = engineSelect.selectedIndex;
1248 if (selectedIndex >= 0) {
1249 var selection = engineSelect.options[selectedIndex];
1250 chrome.send('setDefaultSearchEngine', [String(selection.value)]);
1255 * Sets or clear whether Chrome should Auto-launch on computer startup.
1258 handleAutoLaunchChanged_: function() {
1259 chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
1263 * Get the selected profile item from the profile list. This also works
1264 * correctly if the list is not displayed.
1265 * @return {Object} the profile item object, or null if nothing is selected.
1268 getSelectedProfileItem_: function() {
1269 var profilesList = $('profiles-list');
1270 if (profilesList.hidden) {
1271 if (profilesList.dataModel.length > 0)
1272 return profilesList.dataModel.item(0);
1274 return profilesList.selectedItem;
1280 * Helper function to set the status of profile view buttons to disabled or
1281 * enabled, depending on the number of profiles and selection status of the
1285 setProfileViewButtonsStatus_: function() {
1286 var profilesList = $('profiles-list');
1287 var selectedProfile = profilesList.selectedItem;
1288 var hasSelection = selectedProfile != null;
1289 var hasSingleProfile = profilesList.dataModel.length == 1;
1290 var isSupervised = loadTimeData.getBoolean('profileIsSupervised');
1291 $('profiles-manage').disabled = !hasSelection ||
1292 !selectedProfile.isCurrentProfile;
1293 if (hasSelection && !selectedProfile.isCurrentProfile)
1294 $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1296 $('profiles-manage').title = '';
1297 $('profiles-delete').disabled = isSupervised ||
1298 (!hasSelection && !hasSingleProfile);
1299 if (OptionsPage.isSettingsApp()) {
1300 $('profiles-app-list-switch').disabled = !hasSelection ||
1301 selectedProfile.isCurrentProfile;
1303 var importData = $('import-data');
1305 importData.disabled = $('import-data').disabled = hasSelection &&
1306 !selectedProfile.isCurrentProfile;
1311 * Display the correct dialog layout, depending on how many profiles are
1313 * @param {number} numProfiles The number of profiles to display.
1316 setProfileViewSingle_: function(numProfiles) {
1317 // Always show the profiles list when using the new Profiles UI.
1318 var usingNewProfilesUI = loadTimeData.getBoolean('usingNewProfilesUI');
1319 var showSingleProfileView = !usingNewProfilesUI && numProfiles == 1;
1320 $('profiles-list').hidden = showSingleProfileView;
1321 $('profiles-single-message').hidden = !showSingleProfileView;
1322 $('profiles-manage').hidden =
1323 showSingleProfileView || OptionsPage.isSettingsApp();
1324 $('profiles-delete').textContent = showSingleProfileView ?
1325 loadTimeData.getString('profilesDeleteSingle') :
1326 loadTimeData.getString('profilesDelete');
1327 if (OptionsPage.isSettingsApp())
1328 $('profiles-app-list-switch').hidden = showSingleProfileView;
1332 * Adds all |profiles| to the list.
1333 * @param {Array.<Object>} profiles An array of profile info objects.
1334 * each object is of the form:
1336 * name: "Profile Name",
1337 * iconURL: "chrome://path/to/icon/image",
1338 * filePath: "/path/to/profile/data/on/disk",
1339 * isCurrentProfile: false,
1340 * isSupervised: false
1344 setProfilesInfo_: function(profiles) {
1345 this.setProfileViewSingle_(profiles.length);
1346 // add it to the list, even if the list is hidden so we can access it
1348 $('profiles-list').dataModel = new ArrayDataModel(profiles);
1350 // Received new data. If showing the "manage" overlay, keep it up to
1351 // date. If showing the "delete" overlay, close it.
1352 if (ManageProfileOverlay.getInstance().visible &&
1353 !$('manage-profile-overlay-manage').hidden) {
1354 ManageProfileOverlay.showManageDialog();
1356 ManageProfileOverlay.getInstance().visible = false;
1359 this.setProfileViewButtonsStatus_();
1363 * Reports supervised user import errors to the SupervisedUserImportOverlay.
1364 * @param {string} error The error message to display.
1367 showSupervisedUserImportError_: function(error) {
1368 SupervisedUserImportOverlay.onError(error);
1372 * Reports successful importing of a supervised user to
1373 * the SupervisedUserImportOverlay.
1376 showSupervisedUserImportSuccess_: function() {
1377 SupervisedUserImportOverlay.onSuccess();
1381 * Reports an error to the "create" overlay during profile creation.
1382 * @param {string} error The error message to display.
1385 showCreateProfileError_: function(error) {
1386 CreateProfileOverlay.onError(error);
1390 * Sends a warning message to the "create" overlay during profile creation.
1391 * @param {string} warning The warning message to display.
1394 showCreateProfileWarning_: function(warning) {
1395 CreateProfileOverlay.onWarning(warning);
1399 * Reports successful profile creation to the "create" overlay.
1400 * @param {Object} profileInfo An object of the form:
1402 * name: "Profile Name",
1403 * filePath: "/path/to/profile/data/on/disk"
1404 * isSupervised: (true|false),
1408 showCreateProfileSuccess_: function(profileInfo) {
1409 CreateProfileOverlay.onSuccess(profileInfo);
1413 * Returns the currently active profile for this browser window.
1414 * @return {Object} A profile info object.
1417 getCurrentProfile_: function() {
1418 for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1419 var profile = $('profiles-list').dataModel.item(i);
1420 if (profile.isCurrentProfile)
1424 assertNotReached('There should always be a current profile.');
1428 * Propmpts user to confirm deletion of the profile for this browser
1432 deleteCurrentProfile_: function() {
1433 ManageProfileOverlay.showDeleteDialog(this.getCurrentProfile_());
1436 setNativeThemeButtonEnabled_: function(enabled) {
1437 var button = $('themes-native-button');
1439 button.disabled = !enabled;
1442 setThemesResetButtonEnabled_: function(enabled) {
1443 $('themes-reset').disabled = !enabled;
1446 setAccountPictureManaged_: function(managed) {
1447 var picture = $('account-picture');
1448 if (managed || UIAccountTweaks.loggedInAsGuest()) {
1449 picture.disabled = true;
1450 ChangePictureOptions.closeOverlay();
1452 picture.disabled = false;
1455 // Create a synthetic pref change event decorated as
1456 // CoreOptionsHandler::CreateValueForPref() does.
1457 var event = new Event('account-picture');
1459 event.value = { controlledBy: 'policy' };
1462 $('account-picture-indicator').handlePrefChange(event);
1466 * (Re)loads IMG element with current user account picture.
1469 updateAccountPicture_: function() {
1470 var picture = $('account-picture');
1472 picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1477 setWallpaperManaged_: function(managed) {
1478 var button = $('set-wallpaper');
1479 button.disabled = !!managed;
1481 // Create a synthetic pref change event decorated as
1482 // CoreOptionsHandler::CreateValueForPref() does.
1483 var event = new Event('wallpaper');
1485 event.value = { controlledBy: 'policy' };
1488 $('wallpaper-indicator').handlePrefChange(event);
1492 * Handle the 'add device' button click.
1495 handleAddBluetoothDevice_: function() {
1496 chrome.send('findBluetoothDevices');
1497 PageManager.showPageByName('bluetooth', false);
1501 * Enables or disables the Manage SSL Certificates button.
1504 enableCertificateButton_: function(enabled) {
1505 $('certificatesManageButton').disabled = !enabled;
1509 * Enables factory reset section.
1512 enableFactoryResetSection_: function() {
1513 $('factory-reset-section').hidden = false;
1517 * Set the checked state of the metrics reporting checkbox.
1520 setMetricsReportingCheckboxState_: function(checked, disabled) {
1521 $('metricsReportingEnabled').checked = checked;
1522 $('metricsReportingEnabled').disabled = disabled;
1528 setMetricsReportingSettingVisibility_: function(visible) {
1530 $('metricsReportingSetting').style.display = 'block';
1532 $('metricsReportingSetting').style.display = 'none';
1536 * Set network prediction checkbox value.
1538 * @param {Object} pref Information about network prediction options.
1539 * @param {number} pref.value The value of network prediction options.
1540 * @param {boolean} pref.disabled If the pref is not user modifiable.
1543 setNetworkPredictionValue_: function(pref) {
1544 var checkbox = $('networkPredictionOptions');
1545 checkbox.disabled = pref.disabled;
1546 if (pref.value == NetworkPredictionOptions.UNSET) {
1547 checkbox.checked = (NetworkPredictionOptions.DEFAULT !=
1548 NetworkPredictionOptions.NEVER);
1550 checkbox.checked = (pref.value != NetworkPredictionOptions.NEVER);
1555 * Set the font size selected item. This item actually reflects two
1556 * preferences: the default font size and the default fixed font size.
1558 * @param {Object} pref Information about the font size preferences.
1559 * @param {number} pref.value The value of the default font size pref.
1560 * @param {boolean} pref.disabled True if either pref not user modifiable.
1561 * @param {string} pref.controlledBy The source of the pref value(s) if
1562 * either pref is currently not controlled by the user.
1565 setFontSize_: function(pref) {
1566 var selectCtl = $('defaultFontSize');
1567 selectCtl.disabled = pref.disabled;
1568 // Create a synthetic pref change event decorated as
1569 // CoreOptionsHandler::CreateValueForPref() does.
1570 var event = new Event('synthetic-font-size');
1573 controlledBy: pref.controlledBy,
1574 disabled: pref.disabled
1576 $('font-size-indicator').handlePrefChange(event);
1578 for (var i = 0; i < selectCtl.options.length; i++) {
1579 if (selectCtl.options[i].value == pref.value) {
1580 selectCtl.selectedIndex = i;
1582 selectCtl.remove($('Custom').index);
1587 // Add/Select Custom Option in the font size label list.
1589 var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1591 option.setAttribute('id', 'Custom');
1592 selectCtl.add(option);
1594 $('Custom').selected = true;
1598 * Populate the page zoom selector with values received from the caller.
1599 * @param {Array} items An array of items to populate the selector.
1600 * each object is an array with three elements as follows:
1601 * 0: The title of the item (string).
1602 * 1: The value of the item (number).
1603 * 2: Whether the item should be selected (boolean).
1606 setupPageZoomSelector_: function(items) {
1607 var element = $('defaultZoomFactor');
1609 // Remove any existing content.
1610 element.textContent = '';
1612 // Insert new child nodes into select element.
1613 var value, title, selected;
1614 for (var i = 0; i < items.length; i++) {
1615 title = items[i][0];
1616 value = items[i][1];
1617 selected = items[i][2];
1618 element.appendChild(new Option(title, value, false, selected));
1623 * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1625 * @param {boolean} display Whether to show the button and label or not.
1628 setAutoOpenFileTypesDisplayed_: function(display) {
1629 if ($('advanced-settings').hidden) {
1630 // If the Advanced section is hidden, don't animate the transition.
1631 $('auto-open-file-types-section').hidden = !display;
1634 this.showSectionWithAnimation_(
1635 $('auto-open-file-types-section'),
1636 $('auto-open-file-types-container'));
1638 this.hideSectionWithAnimation_(
1639 $('auto-open-file-types-section'),
1640 $('auto-open-file-types-container'));
1646 * Set the enabled state for the proxy settings button and its associated
1647 * message when extension controlled.
1648 * @param {boolean} disabled Whether the button should be disabled.
1649 * @param {boolean} extensionControlled Whether the proxy is extension
1653 setupProxySettingsButton_: function(disabled, extensionControlled) {
1654 if (!cr.isChromeOS) {
1655 $('proxiesConfigureButton').disabled = disabled;
1656 $('proxiesLabel').textContent =
1657 loadTimeData.getString(extensionControlled ?
1658 'proxiesLabelExtension' : 'proxiesLabelSystem');
1663 * Set the initial state of the spoken feedback checkbox.
1666 setSpokenFeedbackCheckboxState_: function(checked) {
1667 $('accessibility-spoken-feedback-check').checked = checked;
1671 * Set the initial state of the high contrast checkbox.
1674 setHighContrastCheckboxState_: function(checked) {
1675 $('accessibility-high-contrast-check').checked = checked;
1679 * Set the initial state of the virtual keyboard checkbox.
1682 setVirtualKeyboardCheckboxState_: function(checked) {
1683 // TODO(zork): Update UI
1687 * Show/hide mouse settings slider.
1690 showMouseControls_: function(show) {
1691 $('mouse-settings').hidden = !show;
1695 * Adds hidden warning boxes for settings potentially controlled by
1697 * @param {string} parentDiv The div name to append the bubble to.
1698 * @param {string} bubbleId The ID to use for the bubble.
1699 * @param {boolean} first Add as first node if true, otherwise last.
1702 addExtensionControlledBox_: function(parentDiv, bubbleId, first) {
1703 var bubble = $('extension-controlled-warning-template').cloneNode(true);
1704 bubble.id = bubbleId;
1705 var parent = $(parentDiv);
1707 parent.insertBefore(bubble, parent.firstChild);
1709 parent.appendChild(bubble);
1713 * Adds a bubble showing that an extension is controlling a particular
1715 * @param {string} parentDiv The div name to append the bubble to.
1716 * @param {string} bubbleId The ID to use for the bubble.
1717 * @param {string} extensionId The ID of the controlling extension.
1718 * @param {string} extensionName The name of the controlling extension.
1721 toggleExtensionControlledBox_: function(
1722 parentDiv, bubbleId, extensionId, extensionName) {
1723 var bubble = $(bubbleId);
1725 bubble.hidden = extensionId.length == 0;
1729 // Set the extension image.
1730 var div = bubble.firstElementChild;
1731 div.style.backgroundImage =
1732 'url(chrome://extension-icon/' + extensionId + '/24/1)';
1734 // Set the bubble label.
1735 var label = loadTimeData.getStringF('extensionControlled', extensionName);
1736 var docFrag = parseHtmlSubset('<div>' + label + '</div>', ['B', 'DIV']);
1737 div.innerHTML = docFrag.firstChild.innerHTML;
1739 // Wire up the button to disable the right extension.
1740 var button = div.nextElementSibling;
1741 button.dataset.extensionId = extensionId;
1745 * Toggles the warning boxes that show which extension is controlling
1746 * various settings of Chrome.
1747 * @param {object} details A dictionary of ID+name pairs for each of the
1748 * settings controlled by an extension.
1751 toggleExtensionIndicators_: function(details) {
1752 this.toggleExtensionControlledBox_('search-section-content',
1753 'search-engine-controlled',
1754 details.searchEngine.id,
1755 details.searchEngine.name);
1756 this.toggleExtensionControlledBox_('extension-controlled-container',
1757 'homepage-controlled',
1758 details.homePage.id,
1759 details.homePage.name);
1760 this.toggleExtensionControlledBox_('startup-section-content',
1761 'startpage-controlled',
1762 details.startUpPage.id,
1763 details.startUpPage.name);
1764 this.toggleExtensionControlledBox_('newtab-section-content',
1765 'newtab-controlled',
1766 details.newTabPage.id,
1767 details.newTabPage.name);
1768 this.toggleExtensionControlledBox_('proxy-section-content',
1771 details.proxy.name);
1773 // The proxy section contains just the warning box and nothing else, so
1774 // if we're hiding the proxy warning box, we should also hide its header
1776 $('proxy-section').hidden = details.proxy.id.length == 0;
1781 * Show/hide touchpad-related settings.
1784 showTouchpadControls_: function(show) {
1785 $('touchpad-settings').hidden = !show;
1786 $('accessibility-tap-dragging').hidden = !show;
1790 * Activate the Bluetooth settings section on the System settings page.
1793 showBluetoothSettings_: function() {
1794 $('bluetooth-devices').hidden = false;
1798 * Dectivates the Bluetooth settings section from the System settings page.
1801 hideBluetoothSettings_: function() {
1802 $('bluetooth-devices').hidden = true;
1806 * Sets the state of the checkbox indicating if Bluetooth is turned on. The
1807 * state of the "Find devices" button and the list of discovered devices may
1808 * also be affected by a change to the state.
1809 * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
1812 setBluetoothState_: function(checked) {
1813 $('enable-bluetooth').checked = checked;
1814 $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
1815 $('bluetooth-add-device').hidden = !checked;
1816 $('bluetooth-reconnect-device').hidden = !checked;
1817 // Flush list of previously discovered devices if bluetooth is turned off.
1819 $('bluetooth-paired-devices-list').clear();
1820 $('bluetooth-unpaired-devices-list').clear();
1822 chrome.send('getPairedBluetoothDevices');
1827 * Adds an element to the list of available Bluetooth devices. If an element
1828 * with a matching address is found, the existing element is updated.
1829 * @param {{name: string,
1832 * connected: boolean}} device
1833 * Decription of the Bluetooth device.
1836 addBluetoothDevice_: function(device) {
1837 var list = $('bluetooth-unpaired-devices-list');
1838 // Display the "connecting" (already paired or not yet paired) and the
1839 // paired devices in the same list.
1840 if (device.paired || device.connecting) {
1841 // Test to see if the device is currently in the unpaired list, in which
1842 // case it should be removed from that list.
1843 var index = $('bluetooth-unpaired-devices-list').find(device.address);
1844 if (index != undefined)
1845 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1846 list = $('bluetooth-paired-devices-list');
1848 // Test to see if the device is currently in the paired list, in which
1849 // case it should be removed from that list.
1850 var index = $('bluetooth-paired-devices-list').find(device.address);
1851 if (index != undefined)
1852 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1854 list.appendDevice(device);
1856 // One device can be in the process of pairing. If found, display
1857 // the Bluetooth pairing overlay.
1859 BluetoothPairing.showDialog(device);
1863 * Removes an element from the list of available devices.
1864 * @param {string} address Unique address of the device.
1867 removeBluetoothDevice_: function(address) {
1868 var index = $('bluetooth-unpaired-devices-list').find(address);
1869 if (index != undefined) {
1870 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1872 index = $('bluetooth-paired-devices-list').find(address);
1873 if (index != undefined)
1874 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1879 * Shows the overlay dialog for changing the user avatar image.
1882 showImagerPickerOverlay_: function() {
1883 PageManager.showPageByName('changePicture');
1887 * Shows (or not) the "User" section of the settings page based on whether
1888 * any of the sub-sections are present (or not).
1891 maybeShowUserSection_: function() {
1892 $('sync-users-section').hidden =
1893 $('profiles-section').hidden &&
1894 $('sync-section').hidden &&
1895 $('profiles-supervised-dashboard-tip').hidden;
1899 * Updates the date and time section with time sync information.
1900 * @param {boolean} canSetTime Whether the system time can be set.
1903 setCanSetTime_: function(canSetTime) {
1904 // If the time has been network-synced, it cannot be set manually.
1905 $('time-synced-explanation').hidden = canSetTime;
1906 $('set-time').hidden = !canSetTime;
1910 * Handle the 'set date and time' button click.
1913 handleSetTime_: function() {
1914 chrome.send('showSetTime');
1918 //Forward public APIs to private implementations.
1920 'addBluetoothDevice',
1921 'deleteCurrentProfile',
1922 'enableCertificateButton',
1923 'enableFactoryResetSection',
1924 'getCurrentProfile',
1925 'getStartStopSyncButton',
1926 'hideBluetoothSettings',
1927 'notifyInitializationComplete',
1928 'removeBluetoothDevice',
1930 'setAccountPictureManaged',
1931 'setWallpaperManaged',
1932 'setAutoOpenFileTypesDisplayed',
1933 'setBluetoothState',
1936 'setNativeThemeButtonEnabled',
1937 'setNetworkPredictionValue',
1938 'setHighContrastCheckboxState',
1939 'setMetricsReportingCheckboxState',
1940 'setMetricsReportingSettingVisibility',
1942 'setSpokenFeedbackCheckboxState',
1943 'setThemesResetButtonEnabled',
1944 'setVirtualKeyboardCheckboxState',
1945 'setupPageZoomSelector',
1946 'setupProxySettingsButton',
1947 'showBluetoothSettings',
1948 'showCreateProfileError',
1949 'showCreateProfileSuccess',
1950 'showCreateProfileWarning',
1951 'showHotwordAlwaysOnSection',
1952 'showHotwordSection',
1953 'showMouseControls',
1954 'showSupervisedUserImportError',
1955 'showSupervisedUserImportSuccess',
1956 'showTouchpadControls',
1957 'toggleExtensionIndicators',
1958 'updateAccountPicture',
1959 'updateAutoLaunchState',
1960 'updateDefaultBrowserState',
1962 'updateManagesSupervisedUsers',
1963 'updateSearchEngines',
1964 'updateStartupPages',
1966 ].forEach(function(name) {
1967 BrowserOptions[name] = function() {
1968 var instance = BrowserOptions.getInstance();
1969 return instance[name + '_'].apply(instance, arguments);
1973 if (cr.isChromeOS) {
1975 * Returns username (canonical email) of the user logged in (ChromeOS only).
1976 * @return {string} user email.
1978 // TODO(jhawkins): Investigate the use case for this method.
1979 BrowserOptions.getLoggedInUsername = function() {
1980 return BrowserOptions.getInstance().username_;
1986 BrowserOptions: BrowserOptions