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 ArrayDataModel = cr.ui.ArrayDataModel;
8 var RepeatingButton = cr.ui.RepeatingButton;
9 var HotwordSearchSettingIndicator = options.HotwordSearchSettingIndicator;
12 // BrowserOptions class
13 // Encapsulated handling of browser options page.
15 function BrowserOptions() {
16 OptionsPage.call(this, 'settings', loadTimeData.getString('settingsTitle'),
20 cr.addSingletonGetter(BrowserOptions);
23 * @param {HTMLElement} section The section to show or hide.
24 * @return {boolean} Whether the section should be shown.
27 BrowserOptions.shouldShowSection_ = function(section) {
28 // If the section is hidden or hiding, it should be shown.
29 return section.style.height == '' || section.style.height == '0px';
32 BrowserOptions.prototype = {
33 __proto__: options.OptionsPage.prototype,
36 * Keeps track of whether the user is signed in or not.
43 * Indicates whether signing out is allowed or whether a complete profile
44 * wipe is required to remove the current enterprise account.
48 signoutAllowed_: true,
51 * Keeps track of whether |onShowHomeButtonChanged_| has been called. See
52 * |onShowHomeButtonChanged_|.
56 onShowHomeButtonChangedCalled_: false,
59 * Track if page initialization is complete. All C++ UI handlers have the
60 * chance to manipulate page content within their InitializePage methods.
61 * This flag is set to true after all initializers have been called.
65 initializationComplete_: false,
68 * When a section is waiting to change its height, this will be a number.
69 * Otherwise it'll be null.
73 sectionHeightChangeTimeout_: null,
76 initializePage: function() {
77 OptionsPage.prototype.initializePage.call(this);
80 // Ensure that navigation events are unblocked on uber page. A reload of
81 // the settings page while an overlay is open would otherwise leave uber
82 // page in a blocked state, where tab switching is not possible.
83 uber.invokeMethodOnParent('stopInterceptingEvents');
85 window.addEventListener('message', this.handleWindowMessage_.bind(this));
87 if (loadTimeData.getBoolean('allowAdvancedSettings')) {
88 $('advanced-settings-expander').onclick = function() {
89 self.toggleSectionWithAnimation_(
90 $('advanced-settings'),
91 $('advanced-settings-container'));
93 // If the link was focused (i.e., it was activated using the keyboard)
94 // and it was used to show the section (rather than hiding it), focus
95 // the first element in the container.
96 if (document.activeElement === $('advanced-settings-expander') &&
97 $('advanced-settings').style.height === '') {
98 var focusElement = $('advanced-settings-container').querySelector(
99 'button, input, list, select, a[href]');
101 focusElement.focus();
105 $('advanced-settings-expander').hidden = true;
106 $('advanced-settings').hidden = true;
109 $('advanced-settings').addEventListener('webkitTransitionEnd',
110 this.updateAdvancedSettingsExpander_.bind(this));
113 UIAccountTweaks.applyGuestModeVisibility(document);
114 if (loadTimeData.getBoolean('secondaryUser'))
115 $('secondary-user-banner').hidden = false;
118 // Sync (Sign in) section.
119 this.updateSyncState_(loadTimeData.getValue('syncData'));
121 $('start-stop-sync').onclick = function(event) {
122 if (self.signedIn_) {
123 if (self.signoutAllowed_)
124 SyncSetupOverlay.showStopSyncingUI();
126 ManageProfileOverlay.showDisconnectManagedProfileDialog();
127 } else if (cr.isChromeOS) {
128 SyncSetupOverlay.showSetupUI();
130 SyncSetupOverlay.startSignIn();
133 $('customize-sync').onclick = function(event) {
134 SyncSetupOverlay.showSetupUI();
137 // Internet connection section (ChromeOS only).
139 options.network.NetworkList.decorate($('network-list'));
140 // Show that the network settings are shared if this is a secondary user
141 // in a multi-profile session.
142 if (loadTimeData.getBoolean('secondaryUser')) {
143 var networkIndicator = document.querySelector(
144 '#network-section-header > .controlled-setting-indicator');
145 networkIndicator.setAttribute('controlled-by', 'shared');
146 networkIndicator.location = cr.ui.ArrowLocation.TOP_START;
148 options.network.NetworkList.refreshNetworkData(
149 loadTimeData.getValue('networkData'));
152 // On Startup section.
153 Preferences.getInstance().addEventListener('session.restore_on_startup',
154 this.onRestoreOnStartupChanged_.bind(this));
155 Preferences.getInstance().addEventListener(
156 'session.startup_urls',
158 $('startup-set-pages').disabled = event.value.disabled;
161 $('startup-set-pages').onclick = function() {
162 OptionsPage.navigateToPage('startup');
165 // Appearance section.
166 Preferences.getInstance().addEventListener('browser.show_home_button',
167 this.onShowHomeButtonChanged_.bind(this));
169 Preferences.getInstance().addEventListener('homepage',
170 this.onHomePageChanged_.bind(this));
171 Preferences.getInstance().addEventListener('homepage_is_newtabpage',
172 this.onHomePageIsNtpChanged_.bind(this));
174 $('change-home-page').onclick = function(event) {
175 OptionsPage.navigateToPage('homePageOverlay');
176 chrome.send('coreOptionsUserMetricsAction',
177 ['Options_Homepage_ShowSettings']);
180 chrome.send('requestHotwordAvailable');
182 if ($('set-wallpaper')) {
183 $('set-wallpaper').onclick = function(event) {
184 chrome.send('openWallpaperManager');
185 chrome.send('coreOptionsUserMetricsAction',
186 ['Options_OpenWallpaperManager']);
190 $('themes-gallery').onclick = function(event) {
191 window.open(loadTimeData.getString('themesGalleryURL'));
192 chrome.send('coreOptionsUserMetricsAction',
193 ['Options_ThemesGallery']);
195 $('themes-reset').onclick = function(event) {
196 chrome.send('themesReset');
199 if (loadTimeData.getBoolean('profileIsManaged')) {
200 if ($('themes-native-button')) {
201 $('themes-native-button').disabled = true;
202 $('themes-native-button').hidden = true;
204 // Supervised users have just one default theme, even on Linux. So use
205 // the same button for Linux as for the other platforms.
206 $('themes-reset').textContent = loadTimeData.getString('themesReset');
209 // Device section (ChromeOS only).
211 $('keyboard-settings-button').onclick = function(evt) {
212 OptionsPage.navigateToPage('keyboard-overlay');
213 chrome.send('coreOptionsUserMetricsAction',
214 ['Options_ShowKeyboardSettings']);
216 $('pointer-settings-button').onclick = function(evt) {
217 OptionsPage.navigateToPage('pointer-overlay');
218 chrome.send('coreOptionsUserMetricsAction',
219 ['Options_ShowTouchpadSettings']);
224 $('manage-default-search-engines').onclick = function(event) {
225 OptionsPage.navigateToPage('searchEngines');
226 chrome.send('coreOptionsUserMetricsAction',
227 ['Options_ManageSearchEngines']);
229 $('default-search-engine').addEventListener('change',
230 this.setDefaultSearchEngine_);
233 if (loadTimeData.valueExists('profilesInfo')) {
234 $('profiles-section').hidden = false;
235 this.maybeShowUserSection_();
237 var profilesList = $('profiles-list');
238 options.browser_options.ProfileList.decorate(profilesList);
239 profilesList.autoExpands = true;
241 // The profiles info data in |loadTimeData| might be stale.
242 this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
243 chrome.send('requestProfilesInfo');
245 profilesList.addEventListener('change',
246 this.setProfileViewButtonsStatus_);
247 $('profiles-create').onclick = function(event) {
248 ManageProfileOverlay.showCreateDialog();
250 if (OptionsPage.isSettingsApp()) {
251 $('profiles-app-list-switch').onclick = function(event) {
252 var selectedProfile = self.getSelectedProfileItem_();
253 chrome.send('switchAppListProfile', [selectedProfile.filePath]);
256 $('profiles-manage').onclick = function(event) {
257 ManageProfileOverlay.showManageDialog();
259 $('profiles-delete').onclick = function(event) {
260 var selectedProfile = self.getSelectedProfileItem_();
262 ManageProfileOverlay.showDeleteDialog(selectedProfile);
264 if (loadTimeData.getBoolean('profileIsManaged')) {
265 $('profiles-create').disabled = true;
266 $('profiles-delete').disabled = true;
267 $('profiles-list').canDeleteItems = false;
272 // Username (canonical email) of the currently logged in user or
273 // |kGuestUser| if a guest session is active.
274 this.username_ = loadTimeData.getString('username');
276 this.updateAccountPicture_();
278 $('account-picture').onclick = this.showImagerPickerOverlay_;
279 $('change-picture-caption').onclick = this.showImagerPickerOverlay_;
281 $('manage-accounts-button').onclick = function(event) {
282 OptionsPage.navigateToPage('accounts');
283 chrome.send('coreOptionsUserMetricsAction',
284 ['Options_ManageAccounts']);
287 document.querySelector(
288 '#enable-screen-lock + span > .controlled-setting-indicator').
289 setAttribute('textshared',
290 loadTimeData.getString('screenLockShared'));
292 $('import-data').onclick = function(event) {
293 ImportDataOverlay.show();
294 chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
297 if ($('themes-native-button')) {
298 $('themes-native-button').onclick = function(event) {
299 chrome.send('themesSetNative');
304 // Date and time section (CrOS only).
305 if ($('set-time-button'))
306 $('set-time-button').onclick = this.handleSetTime_.bind(this);
308 // Default browser section.
309 if (!cr.isChromeOS) {
310 if (!loadTimeData.getBoolean('showSetDefault')) {
311 $('set-default-browser-section').hidden = true;
313 $('set-as-default-browser').onclick = function(event) {
314 chrome.send('becomeDefaultBrowser');
317 $('auto-launch').onclick = this.handleAutoLaunchChanged_;
321 $('privacyContentSettingsButton').onclick = function(event) {
322 OptionsPage.navigateToPage('content');
323 OptionsPage.showTab($('cookies-nav-tab'));
324 chrome.send('coreOptionsUserMetricsAction',
325 ['Options_ContentSettings']);
327 $('privacyClearDataButton').onclick = function(event) {
328 OptionsPage.navigateToPage('clearBrowserData');
329 chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
331 $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
332 // 'metricsReportingEnabled' element is only present on Chrome branded
333 // builds, and the 'metricsReportingCheckboxAction' message is only
334 // handled on ChromeOS.
335 if ($('metricsReportingEnabled') && cr.isChromeOS) {
336 $('metricsReportingEnabled').onclick = function(event) {
337 chrome.send('metricsReportingCheckboxAction',
338 [String(event.currentTarget.checked)]);
342 // Bluetooth (CrOS only).
344 options.system.bluetooth.BluetoothDeviceList.decorate(
345 $('bluetooth-paired-devices-list'));
347 $('bluetooth-add-device').onclick =
348 this.handleAddBluetoothDevice_.bind(this);
350 $('enable-bluetooth').onchange = function(event) {
351 var state = $('enable-bluetooth').checked;
352 chrome.send('bluetoothEnableChange', [Boolean(state)]);
355 $('bluetooth-reconnect-device').onclick = function(event) {
356 var device = $('bluetooth-paired-devices-list').selectedItem;
357 var address = device.address;
358 chrome.send('updateBluetoothDevice', [address, 'connect']);
359 OptionsPage.closeOverlay();
362 $('bluetooth-paired-devices-list').addEventListener('change',
364 var item = $('bluetooth-paired-devices-list').selectedItem;
365 var disabled = !item || item.connected || !item.connectable;
366 $('bluetooth-reconnect-device').disabled = disabled;
370 // Passwords and Forms section.
371 $('autofill-settings').onclick = function(event) {
372 OptionsPage.navigateToPage('autofill');
373 chrome.send('coreOptionsUserMetricsAction',
374 ['Options_ShowAutofillSettings']);
376 $('manage-passwords').onclick = function(event) {
377 OptionsPage.navigateToPage('passwords');
378 OptionsPage.showTab($('passwords-nav-tab'));
379 chrome.send('coreOptionsUserMetricsAction',
380 ['Options_ShowPasswordManager']);
382 if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
383 // Disable and turn off Autofill in guest mode.
384 var autofillEnabled = $('autofill-enabled');
385 autofillEnabled.disabled = true;
386 autofillEnabled.checked = false;
387 cr.dispatchSimpleEvent(autofillEnabled, 'change');
388 $('autofill-settings').disabled = true;
390 // Disable and turn off Password Manager in guest mode.
391 var passwordManagerEnabled = $('password-manager-enabled');
392 passwordManagerEnabled.disabled = true;
393 passwordManagerEnabled.checked = false;
394 cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
395 $('manage-passwords').disabled = true;
399 $('mac-passwords-warning').hidden =
400 !loadTimeData.getBoolean('multiple_profiles');
404 if (!cr.isChromeOS) {
405 $('proxiesConfigureButton').onclick = function(event) {
406 chrome.send('showNetworkProxySettings');
412 loadTimeData.getBoolean('consumerManagementEnabled')) {
413 $('security-section').hidden = false;
414 $('consumer-management-enroll-button').onclick = function(event) {
415 chrome.send('enrollConsumerManagement');
419 // Easy Unlock section.
420 if (loadTimeData.getBoolean('easyUnlockEnabled')) {
421 $('easy-unlock-section').hidden = false;
422 $('easy-unlock-setup-button').onclick = function(event) {
423 chrome.send('launchEasyUnlockSetup');
427 // Web Content section.
428 $('fontSettingsCustomizeFontsButton').onclick = function(event) {
429 OptionsPage.navigateToPage('fonts');
430 chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
432 $('defaultFontSize').onchange = function(event) {
433 var value = event.target.options[event.target.selectedIndex].value;
434 Preferences.setIntegerPref(
435 'webkit.webprefs.default_fixed_font_size',
436 value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
437 chrome.send('defaultFontSizeAction', [String(value)]);
439 $('defaultZoomFactor').onchange = function(event) {
440 chrome.send('defaultZoomFactorAction',
441 [String(event.target.options[event.target.selectedIndex].value)]);
444 // Languages section.
445 var showLanguageOptions = function(event) {
446 OptionsPage.navigateToPage('languages');
447 chrome.send('coreOptionsUserMetricsAction',
448 ['Options_LanuageAndSpellCheckSettings']);
450 $('language-button').onclick = showLanguageOptions;
451 $('manage-languages').onclick = showLanguageOptions;
453 // Downloads section.
454 Preferences.getInstance().addEventListener('download.default_directory',
455 this.onDefaultDownloadDirectoryChanged_.bind(this));
456 $('downloadLocationChangeButton').onclick = function(event) {
457 chrome.send('selectDownloadLocation');
460 $('disable-drive-row').hidden =
461 UIAccountTweaks.loggedInAsLocallyManagedUser();
463 $('autoOpenFileTypesResetToDefault').onclick = function(event) {
464 chrome.send('autoOpenFileTypesAction');
467 // HTTPS/SSL section.
468 if (cr.isWindows || cr.isMac) {
469 $('certificatesManageButton').onclick = function(event) {
470 chrome.send('showManageSSLCertificates');
473 $('certificatesManageButton').onclick = function(event) {
474 OptionsPage.navigateToPage('certificates');
475 chrome.send('coreOptionsUserMetricsAction',
476 ['Options_ManageSSLCertificates']);
480 if (loadTimeData.getBoolean('cloudPrintShowMDnsOptions')) {
481 $('cloudprint-options-mdns').hidden = false;
482 $('cloudPrintDevicesPageButton').onclick = function() {
483 chrome.send('showCloudPrintDevicesPage');
487 // Accessibility section (CrOS only).
489 var updateAccessibilitySettingsButton = function() {
490 $('accessibility-settings').hidden =
491 !($('accessibility-spoken-feedback-check').checked);
493 Preferences.getInstance().addEventListener(
494 'settings.accessibility',
495 updateAccessibilitySettingsButton);
496 $('accessibility-settings-button').onclick = function(event) {
497 window.open(loadTimeData.getString('accessibilitySettingsURL'));
499 $('accessibility-spoken-feedback-check').onchange = function(event) {
500 chrome.send('spokenFeedbackChange',
501 [$('accessibility-spoken-feedback-check').checked]);
502 updateAccessibilitySettingsButton();
504 updateAccessibilitySettingsButton();
506 $('accessibility-high-contrast-check').onchange = function(event) {
507 chrome.send('highContrastChange',
508 [$('accessibility-high-contrast-check').checked]);
511 var updateDelayDropdown = function() {
512 $('accessibility-autoclick-dropdown').disabled =
513 !$('accessibility-autoclick-check').checked;
515 Preferences.getInstance().addEventListener(
516 $('accessibility-autoclick-check').getAttribute('pref'),
517 updateDelayDropdown);
520 // Display management section (CrOS only).
522 $('display-options').onclick = function(event) {
523 OptionsPage.navigateToPage('display');
524 chrome.send('coreOptionsUserMetricsAction',
525 ['Options_Display']);
529 // Factory reset section (CrOS only).
531 $('factory-reset-restart').onclick = function(event) {
532 OptionsPage.navigateToPage('factoryResetData');
533 chrome.send('onPowerwashDialogShow');
538 if (!cr.isChromeOS) {
539 var updateGpuRestartButton = function() {
540 $('gpu-mode-reset-restart').hidden =
541 loadTimeData.getBoolean('gpuEnabledAtStart') ==
542 $('gpu-mode-checkbox').checked;
544 Preferences.getInstance().addEventListener(
545 $('gpu-mode-checkbox').getAttribute('pref'),
546 updateGpuRestartButton);
547 $('gpu-mode-reset-restart-button').onclick = function(event) {
548 chrome.send('restartBrowser');
550 updateGpuRestartButton();
553 // Reset profile settings section.
554 $('reset-profile-settings').onclick = function(event) {
555 OptionsPage.navigateToPage('resetProfileSettings');
557 $('reset-profile-settings-section').hidden =
558 !loadTimeData.getBoolean('enableResetProfileSettings');
560 // Extension controlled UI.
561 this.addExtensionControlledBox_('search-section-content',
562 'search-engine-controlled',
564 this.addExtensionControlledBox_('extension-controlled-container',
565 'homepage-controlled',
567 this.addExtensionControlledBox_('startup-section-content',
568 'startpage-controlled',
570 this.addExtensionControlledBox_('newtab-section-content',
574 document.body.addEventListener('click', function(e) {
575 var button = findAncestor(e.target, function(el) {
576 return el.tagName == 'BUTTON' &&
577 el.dataset.extensionId !== undefined &&
578 el.dataset.extensionId.length;
581 chrome.send('disableExtension', [button.dataset.extensionId]);
586 didShowPage: function() {
587 $('search-field').focus();
591 * Called after all C++ UI handlers have called InitializePage to notify
592 * that initialization is complete.
595 notifyInitializationComplete_: function() {
596 this.initializationComplete_ = true;
597 cr.dispatchSimpleEvent(document, 'initializationComplete');
601 * Event listener for the 'session.restore_on_startup' pref.
602 * @param {Event} event The preference change event.
605 onRestoreOnStartupChanged_: function(event) {
606 /** @const */ var showHomePageValue = 0;
608 if (event.value.value == showHomePageValue) {
609 // If the user previously selected "Show the homepage", the
610 // preference will already be migrated to "Open a specific page". So
611 // the only way to reach this code is if the 'restore on startup'
612 // preference is managed.
613 assert(event.value.controlledBy);
615 // Select "open the following pages" and lock down the list of URLs
616 // to reflect the intention of the policy.
617 $('startup-show-pages').checked = true;
618 StartupOverlay.getInstance().setControlsDisabled(true);
620 // Re-enable the controls in the startup overlay if necessary.
621 StartupOverlay.getInstance().updateControlStates();
626 * Handler for messages sent from the main uber page.
627 * @param {Event} e The 'message' event from the uber page.
630 handleWindowMessage_: function(e) {
631 if (e.data.method == 'frameSelected')
632 $('search-field').focus();
636 * Animatedly changes height |from| a px number |to| a px number.
637 * @param {HTMLElement} section The section to animate.
638 * @param {HTMLElement} container The container of |section|.
639 * @param {boolean} showing Whether to go from 0 -> container height or
640 * container height -> 0.
643 animatedSectionHeightChange_: function(section, container, showing) {
644 // If the section is already animating, dispatch a synthetic transition
645 // end event as the upcoming code will cancel the current one.
646 if (section.classList.contains('sliding'))
647 cr.dispatchSimpleEvent(section, 'webkitTransitionEnd');
649 this.addTransitionEndListener_(section);
651 section.hidden = false;
652 section.style.height = (showing ? 0 : container.offsetHeight) + 'px';
653 section.classList.add('sliding');
655 if (this.sectionHeightChangeTimeout_ !== null)
656 clearTimeout(this.sectionHeightChangeTimeout_);
658 this.sectionHeightChangeTimeout_ = setTimeout(function() {
659 section.style.height = (showing ? container.offsetHeight : 0) + 'px';
660 this.sectionHeightChangeTimeout_ = null;
665 * Shows the given section.
666 * @param {HTMLElement} section The section to be shown.
667 * @param {HTMLElement} container The container for the section. Must be
668 * inside of |section|.
669 * @param {boolean} animate Indicate if the expansion should be animated.
672 showSection_: function(section, container, animate) {
673 // Delay starting the transition if animating so that hidden change will
676 this.animatedSectionHeightChange_(section, container, true);
678 section.hidden = false;
679 section.style.height = 'auto';
684 * Shows the given section, with animation.
685 * @param {HTMLElement} section The section to be shown.
686 * @param {HTMLElement} container The container for the section. Must be
687 * inside of |section|.
690 showSectionWithAnimation_: function(section, container) {
691 this.showSection_(section, container, /* animate */ true);
695 * Hides the given |section| with animation.
696 * @param {HTMLElement} section The section to be hidden.
697 * @param {HTMLElement} container The container for the section. Must be
698 * inside of |section|.
701 hideSectionWithAnimation_: function(section, container) {
702 this.animatedSectionHeightChange_(section, container, false);
706 * Toggles the visibility of |section| in an animated way.
707 * @param {HTMLElement} section The section to be toggled.
708 * @param {HTMLElement} container The container for the section. Must be
709 * inside of |section|.
712 toggleSectionWithAnimation_: function(section, container) {
713 if (BrowserOptions.shouldShowSection_(section))
714 this.showSectionWithAnimation_(section, container);
716 this.hideSectionWithAnimation_(section, container);
720 * Scrolls the settings page to make the section visible auto-expanding
721 * advanced settings if required. The transition is not animated. This
722 * method is used to ensure that a section associated with an overlay
723 * is visible when the overlay is closed.
724 * @param {!Element} section The section to make visible.
727 scrollToSection_: function(section) {
728 var advancedSettings = $('advanced-settings');
729 var container = $('advanced-settings-container');
730 var expander = $('advanced-settings-expander');
731 if (!expander.hidden &&
732 advancedSettings.hidden &&
733 section.parentNode == container) {
734 this.showSection_($('advanced-settings'),
735 $('advanced-settings-container'),
736 /* animate */ false);
737 this.updateAdvancedSettingsExpander_();
740 if (!this.initializationComplete_) {
742 var callback = function() {
743 document.removeEventListener('initializationComplete', callback);
744 self.scrollToSection_(section);
746 document.addEventListener('initializationComplete', callback);
750 var pageContainer = $('page-container');
751 // pageContainer.offsetTop is relative to the screen.
752 var pageTop = pageContainer.offsetTop;
753 var sectionBottom = section.offsetTop + section.offsetHeight;
754 // section.offsetTop is relative to the 'page-container'.
755 var sectionTop = section.offsetTop;
756 if (pageTop + sectionBottom > document.body.scrollHeight ||
757 pageTop + sectionTop < 0) {
758 // Currently not all layout updates are guaranteed to precede the
759 // initializationComplete event (for example 'set-as-default-browser'
760 // button) leaving some uncertainty in the optimal scroll position.
761 // The section is placed approximately in the middle of the screen.
762 var top = Math.min(0, document.body.scrollHeight / 2 - sectionBottom);
763 pageContainer.style.top = top + 'px';
764 pageContainer.oldScrollTop = -top;
769 * Adds a |webkitTransitionEnd| listener to the given section so that
770 * it can be animated. The listener will only be added to a given section
771 * once, so this can be called as multiple times.
772 * @param {HTMLElement} section The section to be animated.
775 addTransitionEndListener_: function(section) {
776 if (section.hasTransitionEndListener_)
779 section.addEventListener('webkitTransitionEnd',
780 this.onTransitionEnd_.bind(this));
781 section.hasTransitionEndListener_ = true;
785 * Called after an animation transition has ended.
786 * @param {Event} The webkitTransitionEnd event. NOTE: May be synthetic.
789 onTransitionEnd_: function(event) {
790 if (event.propertyName && event.propertyName != 'height') {
791 // If not a synthetic event or a real transition we care about, bail.
795 var section = event.target;
796 section.classList.remove('sliding');
798 if (!event.propertyName) {
799 // Only real transitions past this point.
803 if (section.style.height == '0px') {
804 // Hide the content so it can't get tab focus.
805 section.hidden = true;
806 section.style.height = '';
808 // Set the section height to 'auto' to allow for size changes
809 // (due to font change or dynamic content).
810 section.style.height = 'auto';
815 updateAdvancedSettingsExpander_: function() {
816 var expander = $('advanced-settings-expander');
817 if (BrowserOptions.shouldShowSection_($('advanced-settings')))
818 expander.textContent = loadTimeData.getString('showAdvancedSettings');
820 expander.textContent = loadTimeData.getString('hideAdvancedSettings');
824 * Updates the sync section with the given state.
825 * @param {Object} syncData A bunch of data records that describe the status
826 * of the sync system.
829 updateSyncState_: function(syncData) {
830 if (!syncData.signinAllowed &&
831 (!syncData.supervisedUser || !cr.isChromeOS)) {
832 $('sync-section').hidden = true;
833 this.maybeShowUserSection_();
837 $('sync-section').hidden = false;
838 this.maybeShowUserSection_();
840 var subSection = $('sync-section').firstChild;
842 if (subSection.nodeType == Node.ELEMENT_NODE)
843 subSection.hidden = syncData.supervisedUser;
844 subSection = subSection.nextSibling;
847 if (syncData.supervisedUser) {
848 $('account-picture-wrapper').hidden = false;
849 $('sync-general').hidden = false;
850 $('sync-status').hidden = true;
854 // If the user gets signed out while the advanced sync settings dialog is
855 // visible, say, due to a dashboard clear, close the dialog.
856 // However, if the user gets signed out as a result of abandoning first
857 // time sync setup, do not call closeOverlay as it will redirect the
858 // browser to the main settings page and override any in-progress
859 // user-initiated navigation. See crbug.com/278030.
860 // Note: SyncSetupOverlay.closeOverlay is a no-op if the overlay is
862 if (this.signedIn_ && !syncData.signedIn && !syncData.setupInProgress)
863 SyncSetupOverlay.closeOverlay();
865 this.signedIn_ = syncData.signedIn;
867 // Display the "advanced settings" button if we're signed in and sync is
868 // not managed/disabled. If the user is signed in, but sync is disabled,
869 // this button is used to re-enable sync.
870 var customizeSyncButton = $('customize-sync');
871 customizeSyncButton.hidden = !this.signedIn_ ||
873 !syncData.syncSystemEnabled;
875 // Only modify the customize button's text if the new text is different.
876 // Otherwise, it can affect search-highlighting in the settings page.
877 // See http://crbug.com/268265.
878 var customizeSyncButtonNewText = syncData.setupCompleted ?
879 loadTimeData.getString('customizeSync') :
880 loadTimeData.getString('syncButtonTextStart');
881 if (customizeSyncButton.textContent != customizeSyncButtonNewText)
882 customizeSyncButton.textContent = customizeSyncButtonNewText;
884 // Disable the "sign in" button if we're currently signing in, or if we're
885 // already signed in and signout is not allowed.
886 var signInButton = $('start-stop-sync');
887 signInButton.disabled = syncData.setupInProgress;
888 this.signoutAllowed_ = syncData.signoutAllowed;
889 if (!syncData.signoutAllowed)
890 $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
892 $('start-stop-sync-indicator').removeAttribute('controlled-by');
894 // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome.
895 signInButton.hidden = cr.isChromeOS;
897 signInButton.textContent =
899 loadTimeData.getString('syncButtonTextStop') :
900 syncData.setupInProgress ?
901 loadTimeData.getString('syncButtonTextInProgress') :
902 loadTimeData.getString('syncButtonTextSignIn');
903 $('start-stop-sync-indicator').hidden = signInButton.hidden;
905 // TODO(estade): can this just be textContent?
906 $('sync-status-text').innerHTML = syncData.statusText;
907 var statusSet = syncData.statusText.length != 0;
908 $('sync-overview').hidden = statusSet;
909 $('sync-status').hidden = !statusSet;
911 $('sync-action-link').textContent = syncData.actionLinkText;
912 // Don't show the action link if it is empty or undefined.
913 $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
914 $('sync-action-link').disabled = syncData.managed ||
915 !syncData.syncSystemEnabled;
917 // On Chrome OS, sign out the user and sign in again to get fresh
918 // credentials on auth errors.
919 $('sync-action-link').onclick = function(event) {
920 if (cr.isChromeOS && syncData.hasError)
921 SyncSetupOverlay.doSignOutOnAuthError();
923 SyncSetupOverlay.showSetupUI();
926 if (syncData.hasError)
927 $('sync-status').classList.add('sync-error');
929 $('sync-status').classList.remove('sync-error');
931 // Disable the "customize / set up sync" button if sync has an
932 // unrecoverable error. Also disable the button if sync has not been set
933 // up and the user is being presented with a link to re-auth.
934 // See crbug.com/289791.
935 customizeSyncButton.disabled =
936 syncData.hasUnrecoverableError ||
937 (!syncData.setupCompleted && !$('sync-action-link').hidden);
941 * Update the UI depending on whether the current profile has a pairing for
943 * @param {boolean} hasPairing True if the current profile has a pairing.
945 updateEasyUnlock_: function(hasPairing) {
946 $('easy-unlock-setup').hidden = hasPairing;
947 $('easy-unlock-enable').hidden = !hasPairing;
951 * Update the UI depending on whether the current profile manages any
953 * @param {boolean} show True if the current profile manages any supervised
956 updateManagesSupervisedUsers_: function(show) {
957 $('profiles-supervised-dashboard-tip').hidden = !show;
958 this.maybeShowUserSection_();
962 * Get the start/stop sync button DOM element. Used for testing.
963 * @return {DOMElement} The start/stop sync button.
966 getStartStopSyncButton_: function() {
967 return $('start-stop-sync');
971 * Event listener for the 'show home button' preference. Shows/hides the
972 * UI for changing the home page with animation, unless this is the first
973 * time this function is called, in which case there is no animation.
974 * @param {Event} event The preference change event.
976 onShowHomeButtonChanged_: function(event) {
977 var section = $('change-home-page-section');
978 if (this.onShowHomeButtonChangedCalled_) {
979 var container = $('change-home-page-section-container');
980 if (event.value.value)
981 this.showSectionWithAnimation_(section, container);
983 this.hideSectionWithAnimation_(section, container);
985 section.hidden = !event.value.value;
986 this.onShowHomeButtonChangedCalled_ = true;
991 * Activates the Hotword section from the System settings page.
994 showHotwordSection_: function(opt_error) {
995 $('hotword-search').hidden = false;
999 * Event listener for the 'homepage is NTP' preference. Updates the label
1000 * next to the 'Change' button.
1001 * @param {Event} event The preference change event.
1003 onHomePageIsNtpChanged_: function(event) {
1004 if (!event.value.uncommitted) {
1005 $('home-page-url').hidden = event.value.value;
1006 $('home-page-ntp').hidden = !event.value.value;
1011 * Event listener for changes to the homepage preference. Updates the label
1012 * next to the 'Change' button.
1013 * @param {Event} event The preference change event.
1015 onHomePageChanged_: function(event) {
1016 if (!event.value.uncommitted)
1017 $('home-page-url').textContent = this.stripHttp_(event.value.value);
1021 * Removes the 'http://' from a URL, like the omnibox does. If the string
1022 * doesn't start with 'http://' it is returned unchanged.
1023 * @param {string} url The url to be processed
1024 * @return {string} The url with the 'http://' removed.
1026 stripHttp_: function(url) {
1027 return url.replace(/^http:\/\//, '');
1031 * Shows the autoLaunch preference and initializes its checkbox value.
1032 * @param {bool} enabled Whether autolaunch is enabled or or not.
1035 updateAutoLaunchState_: function(enabled) {
1036 $('auto-launch-option').hidden = false;
1037 $('auto-launch').checked = enabled;
1041 * Called when the value of the download.default_directory preference
1043 * @param {Event} event Change event.
1046 onDefaultDownloadDirectoryChanged_: function(event) {
1047 $('downloadLocationPath').value = event.value.value;
1048 if (cr.isChromeOS) {
1049 // On ChromeOS, replace /special/drive-<hash>/root with "Google Drive"
1050 // for remote files, /home/chronos/user/Downloads or
1051 // /home/chronos/u-<hash>/Downloads with "Downloads" for local paths,
1052 // and '/' with ' \u203a ' (angled quote sign) everywhere. The modified
1053 // path is used only for display purpose.
1054 var path = $('downloadLocationPath').value;
1055 path = path.replace(/^\/special\/drive[^\/]*\/root/, 'Google Drive');
1056 path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, '');
1057 path = path.replace(/\//g, ' \u203a ');
1058 $('downloadLocationPath').value = path;
1060 $('download-location-label').classList.toggle('disabled',
1061 event.value.disabled);
1062 $('downloadLocationChangeButton').disabled = event.value.disabled;
1066 * Update the Default Browsers section based on the current state.
1067 * @param {string} statusString Description of the current default state.
1068 * @param {boolean} isDefault Whether or not the browser is currently
1070 * @param {boolean} canBeDefault Whether or not the browser can be default.
1073 updateDefaultBrowserState_: function(statusString, isDefault,
1075 if (!cr.isChromeOS) {
1076 var label = $('default-browser-state');
1077 label.textContent = statusString;
1079 $('set-as-default-browser').hidden = !canBeDefault || isDefault;
1084 * Clears the search engine popup.
1087 clearSearchEngines_: function() {
1088 $('default-search-engine').textContent = '';
1092 * Updates the search engine popup with the given entries.
1093 * @param {Array} engines List of available search engines.
1094 * @param {number} defaultValue The value of the current default engine.
1095 * @param {boolean} defaultManaged Whether the default search provider is
1096 * managed. If true, the default search provider can't be changed.
1099 updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
1100 this.clearSearchEngines_();
1101 engineSelect = $('default-search-engine');
1102 engineSelect.disabled = defaultManaged;
1103 if (defaultManaged && defaultValue == -1)
1105 engineCount = engines.length;
1106 var defaultIndex = -1;
1107 for (var i = 0; i < engineCount; i++) {
1108 var engine = engines[i];
1109 var option = new Option(engine.name, engine.index);
1110 if (defaultValue == option.value)
1112 engineSelect.appendChild(option);
1114 if (defaultIndex >= 0)
1115 engineSelect.selectedIndex = defaultIndex;
1119 * Set the default search engine based on the popup selection.
1122 setDefaultSearchEngine_: function() {
1123 var engineSelect = $('default-search-engine');
1124 var selectedIndex = engineSelect.selectedIndex;
1125 if (selectedIndex >= 0) {
1126 var selection = engineSelect.options[selectedIndex];
1127 chrome.send('setDefaultSearchEngine', [String(selection.value)]);
1132 * Sets or clear whether Chrome should Auto-launch on computer startup.
1135 handleAutoLaunchChanged_: function() {
1136 chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
1140 * Get the selected profile item from the profile list. This also works
1141 * correctly if the list is not displayed.
1142 * @return {Object} the profile item object, or null if nothing is selected.
1145 getSelectedProfileItem_: function() {
1146 var profilesList = $('profiles-list');
1147 if (profilesList.hidden) {
1148 if (profilesList.dataModel.length > 0)
1149 return profilesList.dataModel.item(0);
1151 return profilesList.selectedItem;
1157 * Helper function to set the status of profile view buttons to disabled or
1158 * enabled, depending on the number of profiles and selection status of the
1162 setProfileViewButtonsStatus_: function() {
1163 var profilesList = $('profiles-list');
1164 var selectedProfile = profilesList.selectedItem;
1165 var hasSelection = selectedProfile != null;
1166 var hasSingleProfile = profilesList.dataModel.length == 1;
1167 var isManaged = loadTimeData.getBoolean('profileIsManaged');
1168 $('profiles-manage').disabled = !hasSelection ||
1169 !selectedProfile.isCurrentProfile;
1170 if (hasSelection && !selectedProfile.isCurrentProfile)
1171 $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1173 $('profiles-manage').title = '';
1174 $('profiles-delete').disabled = isManaged ||
1175 (!hasSelection && !hasSingleProfile);
1176 if (OptionsPage.isSettingsApp()) {
1177 $('profiles-app-list-switch').disabled = !hasSelection ||
1178 selectedProfile.isCurrentProfile;
1180 var importData = $('import-data');
1182 importData.disabled = $('import-data').disabled = hasSelection &&
1183 !selectedProfile.isCurrentProfile;
1188 * Display the correct dialog layout, depending on how many profiles are
1190 * @param {number} numProfiles The number of profiles to display.
1193 setProfileViewSingle_: function(numProfiles) {
1194 var hasSingleProfile = numProfiles == 1;
1195 $('profiles-list').hidden = hasSingleProfile;
1196 $('profiles-single-message').hidden = !hasSingleProfile;
1197 $('profiles-manage').hidden =
1198 hasSingleProfile || OptionsPage.isSettingsApp();
1199 $('profiles-delete').textContent = hasSingleProfile ?
1200 loadTimeData.getString('profilesDeleteSingle') :
1201 loadTimeData.getString('profilesDelete');
1202 if (OptionsPage.isSettingsApp())
1203 $('profiles-app-list-switch').hidden = hasSingleProfile;
1207 * Adds all |profiles| to the list.
1208 * @param {Array.<Object>} profiles An array of profile info objects.
1209 * each object is of the form:
1211 * name: "Profile Name",
1212 * iconURL: "chrome://path/to/icon/image",
1213 * filePath: "/path/to/profile/data/on/disk",
1214 * isCurrentProfile: false,
1219 setProfilesInfo_: function(profiles) {
1220 this.setProfileViewSingle_(profiles.length);
1221 // add it to the list, even if the list is hidden so we can access it
1223 $('profiles-list').dataModel = new ArrayDataModel(profiles);
1225 // Received new data. If showing the "manage" overlay, keep it up to
1226 // date. If showing the "delete" overlay, close it.
1227 if (ManageProfileOverlay.getInstance().visible &&
1228 !$('manage-profile-overlay-manage').hidden) {
1229 ManageProfileOverlay.showManageDialog();
1231 ManageProfileOverlay.getInstance().visible = false;
1234 this.setProfileViewButtonsStatus_();
1238 * Reports managed user import errors to the ManagedUserImportOverlay.
1239 * @param {string} error The error message to display.
1242 showManagedUserImportError_: function(error) {
1243 ManagedUserImportOverlay.onError(error);
1247 * Reports successful importing of a managed user to
1248 * the ManagedUserImportOverlay.
1251 showManagedUserImportSuccess_: function() {
1252 ManagedUserImportOverlay.onSuccess();
1256 * Reports an error to the "create" overlay during profile creation.
1257 * @param {string} error The error message to display.
1260 showCreateProfileError_: function(error) {
1261 CreateProfileOverlay.onError(error);
1265 * Sends a warning message to the "create" overlay during profile creation.
1266 * @param {string} warning The warning message to display.
1269 showCreateProfileWarning_: function(warning) {
1270 CreateProfileOverlay.onWarning(warning);
1274 * Reports successful profile creation to the "create" overlay.
1275 * @param {Object} profileInfo An object of the form:
1277 * name: "Profile Name",
1278 * filePath: "/path/to/profile/data/on/disk"
1279 * isManaged: (true|false),
1283 showCreateProfileSuccess_: function(profileInfo) {
1284 CreateProfileOverlay.onSuccess(profileInfo);
1288 * Returns the currently active profile for this browser window.
1289 * @return {Object} A profile info object.
1292 getCurrentProfile_: function() {
1293 for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1294 var profile = $('profiles-list').dataModel.item(i);
1295 if (profile.isCurrentProfile)
1300 'There should always be a current profile, but none found.');
1304 * Propmpts user to confirm deletion of the profile for this browser
1308 deleteCurrentProfile_: function() {
1309 ManageProfileOverlay.showDeleteDialog(this.getCurrentProfile_());
1312 setNativeThemeButtonEnabled_: function(enabled) {
1313 var button = $('themes-native-button');
1315 button.disabled = !enabled;
1318 setThemesResetButtonEnabled_: function(enabled) {
1319 $('themes-reset').disabled = !enabled;
1322 setAccountPictureManaged_: function(managed) {
1323 var picture = $('account-picture');
1324 if (managed || UIAccountTweaks.loggedInAsGuest()) {
1325 picture.disabled = true;
1326 ChangePictureOptions.closeOverlay();
1328 picture.disabled = false;
1331 // Create a synthetic pref change event decorated as
1332 // CoreOptionsHandler::CreateValueForPref() does.
1333 var event = new Event('account-picture');
1335 event.value = { controlledBy: 'policy' };
1338 $('account-picture-indicator').handlePrefChange(event);
1342 * (Re)loads IMG element with current user account picture.
1345 updateAccountPicture_: function() {
1346 var picture = $('account-picture');
1348 picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1353 setWallpaperManaged_: function(managed) {
1354 var button = $('set-wallpaper');
1355 button.disabled = !!managed;
1357 // Create a synthetic pref change event decorated as
1358 // CoreOptionsHandler::CreateValueForPref() does.
1359 var event = new Event('wallpaper');
1361 event.value = { controlledBy: 'policy' };
1364 $('wallpaper-indicator').handlePrefChange(event);
1368 * Handle the 'add device' button click.
1371 handleAddBluetoothDevice_: function() {
1372 chrome.send('findBluetoothDevices');
1373 OptionsPage.showPageByName('bluetooth', false);
1377 * Enables or disables the Manage SSL Certificates button.
1380 enableCertificateButton_: function(enabled) {
1381 $('certificatesManageButton').disabled = !enabled;
1385 * Enables factory reset section.
1388 enableFactoryResetSection_: function() {
1389 $('factory-reset-section').hidden = false;
1393 * Set the checked state of the metrics reporting checkbox.
1396 setMetricsReportingCheckboxState_: function(checked, disabled) {
1397 $('metricsReportingEnabled').checked = checked;
1398 $('metricsReportingEnabled').disabled = disabled;
1404 setMetricsReportingSettingVisibility_: function(visible) {
1406 $('metricsReportingSetting').style.display = 'block';
1408 $('metricsReportingSetting').style.display = 'none';
1412 * Set the font size selected item. This item actually reflects two
1413 * preferences: the default font size and the default fixed font size.
1415 * @param {Object} pref Information about the font size preferences.
1416 * @param {number} pref.value The value of the default font size pref.
1417 * @param {boolean} pref.disabled True if either pref not user modifiable.
1418 * @param {string} pref.controlledBy The source of the pref value(s) if
1419 * either pref is currently not controlled by the user.
1422 setFontSize_: function(pref) {
1423 var selectCtl = $('defaultFontSize');
1424 selectCtl.disabled = pref.disabled;
1425 // Create a synthetic pref change event decorated as
1426 // CoreOptionsHandler::CreateValueForPref() does.
1427 var event = new Event('synthetic-font-size');
1430 controlledBy: pref.controlledBy,
1431 disabled: pref.disabled
1433 $('font-size-indicator').handlePrefChange(event);
1435 for (var i = 0; i < selectCtl.options.length; i++) {
1436 if (selectCtl.options[i].value == pref.value) {
1437 selectCtl.selectedIndex = i;
1439 selectCtl.remove($('Custom').index);
1444 // Add/Select Custom Option in the font size label list.
1446 var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1448 option.setAttribute('id', 'Custom');
1449 selectCtl.add(option);
1451 $('Custom').selected = true;
1455 * Populate the page zoom selector with values received from the caller.
1456 * @param {Array} items An array of items to populate the selector.
1457 * each object is an array with three elements as follows:
1458 * 0: The title of the item (string).
1459 * 1: The value of the item (number).
1460 * 2: Whether the item should be selected (boolean).
1463 setupPageZoomSelector_: function(items) {
1464 var element = $('defaultZoomFactor');
1466 // Remove any existing content.
1467 element.textContent = '';
1469 // Insert new child nodes into select element.
1470 var value, title, selected;
1471 for (var i = 0; i < items.length; i++) {
1472 title = items[i][0];
1473 value = items[i][1];
1474 selected = items[i][2];
1475 element.appendChild(new Option(title, value, false, selected));
1480 * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1482 * @param {boolean} display Whether to show the button and label or not.
1485 setAutoOpenFileTypesDisplayed_: function(display) {
1486 if ($('advanced-settings').hidden) {
1487 // If the Advanced section is hidden, don't animate the transition.
1488 $('auto-open-file-types-section').hidden = !display;
1491 this.showSectionWithAnimation_(
1492 $('auto-open-file-types-section'),
1493 $('auto-open-file-types-container'));
1495 this.hideSectionWithAnimation_(
1496 $('auto-open-file-types-section'),
1497 $('auto-open-file-types-container'));
1503 * Set the enabled state for the proxy settings button.
1506 setupProxySettingsSection_: function(disabled, extensionControlled) {
1507 if (!cr.isChromeOS) {
1508 $('proxiesConfigureButton').disabled = disabled;
1509 $('proxiesLabel').textContent =
1510 loadTimeData.getString(extensionControlled ?
1511 'proxiesLabelExtension' : 'proxiesLabelSystem');
1516 * Set the initial state of the spoken feedback checkbox.
1519 setSpokenFeedbackCheckboxState_: function(checked) {
1520 $('accessibility-spoken-feedback-check').checked = checked;
1524 * Set the initial state of the high contrast checkbox.
1527 setHighContrastCheckboxState_: function(checked) {
1528 $('accessibility-high-contrast-check').checked = checked;
1532 * Set the initial state of the virtual keyboard checkbox.
1535 setVirtualKeyboardCheckboxState_: function(checked) {
1536 // TODO(zork): Update UI
1540 * Show/hide mouse settings slider.
1543 showMouseControls_: function(show) {
1544 $('mouse-settings').hidden = !show;
1548 * Adds hidden warning boxes for settings potentially controlled by
1550 * @param {string} parentDiv The div name to append the bubble to.
1551 * @param {string} bubbleId The ID to use for the bubble.
1552 * @param {boolean} first Add as first node if true, otherwise last.
1555 addExtensionControlledBox_: function(parentDiv, bubbleId, first) {
1556 var bubble = $('extension-controlled-warning-template').cloneNode(true);
1557 bubble.id = bubbleId;
1558 var parent = $(parentDiv);
1560 parent.insertBefore(bubble, parent.firstChild);
1562 parent.appendChild(bubble);
1566 * Adds a bubble showing that an extension is controlling a particular
1568 * @param {string} parentDiv The div name to append the bubble to.
1569 * @param {string} bubbleId The ID to use for the bubble.
1570 * @param {string} extensionId The ID of the controlling extension.
1571 * @param {string} extensionName The name of the controlling extension.
1574 toggleExtensionControlledBox_: function(
1575 parentDiv, bubbleId, extensionId, extensionName) {
1576 var bubble = $(bubbleId);
1578 bubble.hidden = extensionId.length == 0;
1582 // Set the extension image.
1583 var div = bubble.firstElementChild;
1584 div.style.backgroundImage =
1585 'url(chrome://extension-icon/' + extensionId + '/24/1)';
1587 // Set the bubble label.
1588 var label = loadTimeData.getStringF('extensionControlled', extensionName);
1589 var docFrag = parseHtmlSubset('<div>' + label + '</div>', ['B', 'DIV']);
1590 div.innerHTML = docFrag.firstChild.innerHTML;
1592 // Wire up the button to disable the right extension.
1593 var button = div.nextElementSibling;
1594 button.dataset.extensionId = extensionId;
1598 * Toggles the warning boxes that show which extension is controlling
1599 * various settings of Chrome.
1600 * @param {object} details A dictionary of ID+name pairs for each of the
1601 * settings controlled by an extension.
1604 toggleExtensionIndicators_: function(details) {
1605 this.toggleExtensionControlledBox_('search-section-content',
1606 'search-engine-controlled',
1607 details.searchEngine.id,
1608 details.searchEngine.name);
1609 this.toggleExtensionControlledBox_('extension-controlled-container',
1610 'homepage-controlled',
1611 details.homePage.id,
1612 details.homePage.name);
1613 this.toggleExtensionControlledBox_('startup-section-content',
1614 'startpage-controlled',
1615 details.startUpPage.id,
1616 details.startUpPage.name);
1617 this.toggleExtensionControlledBox_('newtab-section-content',
1618 'newtab-controlled',
1619 details.newTabPage.id,
1620 details.newTabPage.name);
1625 * Show/hide touchpad-related settings.
1628 showTouchpadControls_: function(show) {
1629 $('touchpad-settings').hidden = !show;
1630 $('accessibility-tap-dragging').hidden = !show;
1634 * Activate the Bluetooth settings section on the System settings page.
1637 showBluetoothSettings_: function() {
1638 $('bluetooth-devices').hidden = false;
1642 * Dectivates the Bluetooth settings section from the System settings page.
1645 hideBluetoothSettings_: function() {
1646 $('bluetooth-devices').hidden = true;
1650 * Sets the state of the checkbox indicating if Bluetooth is turned on. The
1651 * state of the "Find devices" button and the list of discovered devices may
1652 * also be affected by a change to the state.
1653 * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
1656 setBluetoothState_: function(checked) {
1657 $('enable-bluetooth').checked = checked;
1658 $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
1659 $('bluetooth-add-device').hidden = !checked;
1660 $('bluetooth-reconnect-device').hidden = !checked;
1661 // Flush list of previously discovered devices if bluetooth is turned off.
1663 $('bluetooth-paired-devices-list').clear();
1664 $('bluetooth-unpaired-devices-list').clear();
1666 chrome.send('getPairedBluetoothDevices');
1671 * Adds an element to the list of available Bluetooth devices. If an element
1672 * with a matching address is found, the existing element is updated.
1673 * @param {{name: string,
1676 * connected: boolean}} device
1677 * Decription of the Bluetooth device.
1680 addBluetoothDevice_: function(device) {
1681 var list = $('bluetooth-unpaired-devices-list');
1682 // Display the "connecting" (already paired or not yet paired) and the
1683 // paired devices in the same list.
1684 if (device.paired || device.connecting) {
1685 // Test to see if the device is currently in the unpaired list, in which
1686 // case it should be removed from that list.
1687 var index = $('bluetooth-unpaired-devices-list').find(device.address);
1688 if (index != undefined)
1689 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1690 list = $('bluetooth-paired-devices-list');
1692 // Test to see if the device is currently in the paired list, in which
1693 // case it should be removed from that list.
1694 var index = $('bluetooth-paired-devices-list').find(device.address);
1695 if (index != undefined)
1696 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1698 list.appendDevice(device);
1700 // One device can be in the process of pairing. If found, display
1701 // the Bluetooth pairing overlay.
1703 BluetoothPairing.showDialog(device);
1707 * Removes an element from the list of available devices.
1708 * @param {string} address Unique address of the device.
1711 removeBluetoothDevice_: function(address) {
1712 var index = $('bluetooth-unpaired-devices-list').find(address);
1713 if (index != undefined) {
1714 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1716 index = $('bluetooth-paired-devices-list').find(address);
1717 if (index != undefined)
1718 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1723 * Shows the overlay dialog for changing the user avatar image.
1726 showImagerPickerOverlay_: function() {
1727 OptionsPage.navigateToPage('changePicture');
1731 * Shows (or not) the "User" section of the settings page based on whether
1732 * any of the sub-sections are present (or not).
1735 maybeShowUserSection_: function() {
1736 $('sync-users-section').hidden =
1737 $('profiles-section').hidden &&
1738 $('sync-section').hidden &&
1739 $('profiles-supervised-dashboard-tip').hidden;
1743 * Updates the date and time section with time sync information.
1744 * @param {boolean} canSetTime Whether the system time can be set.
1747 setCanSetTime_: function(canSetTime) {
1748 // If the time has been network-synced, it cannot be set manually.
1749 $('time-synced-explanation').hidden = canSetTime;
1750 $('set-time').hidden = !canSetTime;
1754 * Handle the 'set date and time' button click.
1757 handleSetTime_: function() {
1758 chrome.send('showSetTime');
1762 //Forward public APIs to private implementations.
1764 'addBluetoothDevice',
1765 'deleteCurrentProfile',
1766 'enableCertificateButton',
1767 'enableFactoryResetSection',
1768 'getCurrentProfile',
1769 'getStartStopSyncButton',
1770 'hideBluetoothSettings',
1771 'notifyInitializationComplete',
1772 'removeBluetoothDevice',
1774 'setAccountPictureManaged',
1775 'setWallpaperManaged',
1776 'setAutoOpenFileTypesDisplayed',
1777 'setBluetoothState',
1780 'setNativeThemeButtonEnabled',
1781 'setHighContrastCheckboxState',
1782 'setMetricsReportingCheckboxState',
1783 'setMetricsReportingSettingVisibility',
1785 'setSpokenFeedbackCheckboxState',
1786 'setThemesResetButtonEnabled',
1787 'setVirtualKeyboardCheckboxState',
1788 'setupPageZoomSelector',
1789 'setupProxySettingsSection',
1790 'showBluetoothSettings',
1791 'showCreateProfileError',
1792 'showCreateProfileSuccess',
1793 'showCreateProfileWarning',
1794 'showHotwordSection',
1795 'showManagedUserImportError',
1796 'showManagedUserImportSuccess',
1797 'showMouseControls',
1798 'showTouchpadControls',
1799 'toggleExtensionIndicators',
1800 'updateAccountPicture',
1801 'updateAutoLaunchState',
1802 'updateDefaultBrowserState',
1804 'updateManagesSupervisedUsers',
1805 'updateSearchEngines',
1806 'updateStartupPages',
1808 ].forEach(function(name) {
1809 BrowserOptions[name] = function() {
1810 var instance = BrowserOptions.getInstance();
1811 return instance[name + '_'].apply(instance, arguments);
1815 if (cr.isChromeOS) {
1817 * Returns username (canonical email) of the user logged in (ChromeOS only).
1818 * @return {string} user email.
1820 // TODO(jhawkins): Investigate the use case for this method.
1821 BrowserOptions.getLoggedInUsername = function() {
1822 return BrowserOptions.getInstance().username_;
1828 BrowserOptions: BrowserOptions