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;
11 // BrowserOptions class
12 // Encapsulated handling of browser options page.
14 function BrowserOptions() {
15 OptionsPage.call(this, 'settings', loadTimeData.getString('settingsTitle'),
19 cr.addSingletonGetter(BrowserOptions);
21 BrowserOptions.prototype = {
22 __proto__: options.OptionsPage.prototype,
25 * Keeps track of whether the user is signed in or not.
32 * Keeps track of whether the user has completed sync setup or not.
36 setupCompleted_: false,
39 * Keeps track of whether |onShowHomeButtonChanged_| has been called. See
40 * |onShowHomeButtonChanged_|.
44 onShowHomeButtonChangedCalled_: false,
47 * Track if page initialization is complete. All C++ UI handlers have the
48 * chance to manipulate page content within their InitializePage methods.
49 * This flag is set to true after all initializers have been called.
53 initializationComplete_: false,
56 initializePage: function() {
57 OptionsPage.prototype.initializePage.call(this);
60 // Ensure that navigation events are unblocked on uber page. A reload of
61 // the settings page while an overlay is open would otherwise leave uber
62 // page in a blocked state, where tab switching is not possible.
63 uber.invokeMethodOnParent('stopInterceptingEvents');
65 window.addEventListener('message', this.handleWindowMessage_.bind(this));
67 $('advanced-settings-expander').onclick = function() {
68 self.toggleSectionWithAnimation_(
69 $('advanced-settings'),
70 $('advanced-settings-container'));
72 // If the link was focused (i.e., it was activated using the keyboard)
73 // and it was used to show the section (rather than hiding it), focus
74 // the first element in the container.
75 if (document.activeElement === $('advanced-settings-expander') &&
76 $('advanced-settings').style.height === '') {
77 var focusElement = $('advanced-settings-container').querySelector(
78 'button, input, list, select, a[href]');
84 $('advanced-settings').addEventListener('webkitTransitionEnd',
85 this.updateAdvancedSettingsExpander_.bind(this));
88 UIAccountTweaks.applyGuestModeVisibility(document);
90 // Sync (Sign in) section.
91 this.updateSyncState_(loadTimeData.getValue('syncData'));
93 $('start-stop-sync').onclick = function(event) {
95 SyncSetupOverlay.showStopSyncingUI();
96 else if (cr.isChromeOS)
97 SyncSetupOverlay.showSetupUI();
99 SyncSetupOverlay.startSignIn();
101 $('customize-sync').onclick = function(event) {
102 SyncSetupOverlay.showSetupUI();
105 // Internet connection section (ChromeOS only).
107 options.network.NetworkList.decorate($('network-list'));
108 options.network.NetworkList.refreshNetworkData(
109 loadTimeData.getValue('networkData'));
112 // On Startup section.
113 Preferences.getInstance().addEventListener('session.restore_on_startup',
114 this.onRestoreOnStartupChanged_.bind(this));
115 Preferences.getInstance().addEventListener(
116 'session.urls_to_restore_on_startup',
118 $('startup-set-pages').disabled = event.value.disabled;
121 $('startup-set-pages').onclick = function() {
122 OptionsPage.navigateToPage('startup');
125 // Appearance section.
126 Preferences.getInstance().addEventListener('browser.show_home_button',
127 this.onShowHomeButtonChanged_.bind(this));
129 Preferences.getInstance().addEventListener('homepage',
130 this.onHomePageChanged_.bind(this));
131 Preferences.getInstance().addEventListener('homepage_is_newtabpage',
132 this.onHomePageIsNtpChanged_.bind(this));
134 $('change-home-page').onclick = function(event) {
135 OptionsPage.navigateToPage('homePageOverlay');
138 if ($('set-wallpaper')) {
139 $('set-wallpaper').onclick = function(event) {
140 chrome.send('openWallpaperManager');
144 $('themes-gallery').onclick = function(event) {
145 window.open(loadTimeData.getString('themesGalleryURL'));
147 $('themes-reset').onclick = function(event) {
148 chrome.send('themesReset');
151 if (loadTimeData.getBoolean('profileIsManaged')) {
152 if ($('themes-GTK-button'))
153 $('themes-GTK-button').disabled = true;
154 $('themes-reset').disabled = true;
155 $('themes-gallery').disabled = true;
158 // Device section (ChromeOS only).
160 $('keyboard-settings-button').onclick = function(evt) {
161 OptionsPage.navigateToPage('keyboard-overlay');
163 $('pointer-settings-button').onclick = function(evt) {
164 OptionsPage.navigateToPage('pointer-overlay');
169 $('manage-default-search-engines').onclick = function(event) {
170 OptionsPage.navigateToPage('searchEngines');
171 chrome.send('coreOptionsUserMetricsAction',
172 ['Options_ManageSearchEngines']);
174 $('default-search-engine').addEventListener('change',
175 this.setDefaultSearchEngine_);
178 if (loadTimeData.valueExists('profilesInfo')) {
179 $('profiles-section').hidden = false;
181 var profilesList = $('profiles-list');
182 options.browser_options.ProfileList.decorate(profilesList);
183 profilesList.autoExpands = true;
185 this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
187 profilesList.addEventListener('change',
188 this.setProfileViewButtonsStatus_);
189 $('profiles-create').onclick = function(event) {
190 ManageProfileOverlay.showCreateDialog();
192 if (OptionsPage.isSettingsApp()) {
193 $('profiles-app-list-switch').onclick = function(event) {
194 var selectedProfile = self.getSelectedProfileItem_();
195 chrome.send('switchAppListProfile', [selectedProfile.filePath]);
198 $('profiles-manage').onclick = function(event) {
199 ManageProfileOverlay.showManageDialog();
201 $('profiles-delete').onclick = function(event) {
202 var selectedProfile = self.getSelectedProfileItem_();
204 ManageProfileOverlay.showDeleteDialog(selectedProfile);
206 if (loadTimeData.getBoolean('profileIsManaged')) {
207 $('profiles-create').disabled = true;
208 $('profiles-delete').disabled = true;
209 $('profiles-list').canDeleteItems = false;
214 if (!UIAccountTweaks.loggedInAsGuest()) {
215 $('account-picture-wrapper').onclick = function(event) {
216 OptionsPage.navigateToPage('changePicture');
220 // Username (canonical email) of the currently logged in user or
221 // |kGuestUser| if a guest session is active.
222 this.username_ = loadTimeData.getString('username');
224 this.updateAccountPicture_();
226 $('account-picture-wrapper').oncontextmenu = function(e) {
230 $('manage-accounts-button').onclick = function(event) {
231 OptionsPage.navigateToPage('accounts');
232 chrome.send('coreOptionsUserMetricsAction',
233 ['Options_ManageAccounts']);
236 $('import-data').onclick = function(event) {
237 ImportDataOverlay.show();
238 chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
241 if ($('themes-native-button')) {
242 $('themes-native-button').onclick = function(event) {
243 chrome.send('themesSetNative');
248 // Default browser section.
249 if (!cr.isChromeOS) {
250 $('set-as-default-browser').onclick = function(event) {
251 chrome.send('becomeDefaultBrowser');
254 $('auto-launch').onclick = this.handleAutoLaunchChanged_;
258 $('privacyContentSettingsButton').onclick = function(event) {
259 OptionsPage.navigateToPage('content');
260 OptionsPage.showTab($('cookies-nav-tab'));
261 chrome.send('coreOptionsUserMetricsAction',
262 ['Options_ContentSettings']);
264 $('privacyClearDataButton').onclick = function(event) {
265 OptionsPage.navigateToPage('clearBrowserData');
266 chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
268 $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
269 // 'metricsReportingEnabled' element is only present on Chrome branded
271 if ($('metricsReportingEnabled')) {
272 $('metricsReportingEnabled').onclick = function(event) {
273 chrome.send('metricsReportingCheckboxAction',
274 [String(event.currentTarget.checked)]);
278 // Bluetooth (CrOS only).
280 options.system.bluetooth.BluetoothDeviceList.decorate(
281 $('bluetooth-paired-devices-list'));
283 $('bluetooth-add-device').onclick =
284 this.handleAddBluetoothDevice_.bind(this);
286 $('enable-bluetooth').onchange = function(event) {
287 var state = $('enable-bluetooth').checked;
288 chrome.send('bluetoothEnableChange', [Boolean(state)]);
291 $('bluetooth-reconnect-device').onclick = function(event) {
292 var device = $('bluetooth-paired-devices-list').selectedItem;
293 var address = device.address;
294 chrome.send('updateBluetoothDevice', [address, 'connect']);
295 OptionsPage.closeOverlay();
298 $('bluetooth-reconnect-device').onmousedown = function(event) {
299 // Prevent 'blur' event, which would reset the list selection,
300 // thereby disabling the apply button.
301 event.preventDefault();
304 $('bluetooth-paired-devices-list').addEventListener('change',
306 var item = $('bluetooth-paired-devices-list').selectedItem;
307 var disabled = !item || item.connected || !item.connectable;
308 $('bluetooth-reconnect-device').disabled = disabled;
312 // Passwords and Forms section.
313 $('autofill-settings').onclick = function(event) {
314 OptionsPage.navigateToPage('autofill');
315 chrome.send('coreOptionsUserMetricsAction',
316 ['Options_ShowAutofillSettings']);
318 $('manage-passwords').onclick = function(event) {
319 OptionsPage.navigateToPage('passwords');
320 OptionsPage.showTab($('passwords-nav-tab'));
321 chrome.send('coreOptionsUserMetricsAction',
322 ['Options_ShowPasswordManager']);
324 if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
325 // Disable and turn off Autofill in guest mode.
326 var autofillEnabled = $('autofill-enabled');
327 autofillEnabled.disabled = true;
328 autofillEnabled.checked = false;
329 cr.dispatchSimpleEvent(autofillEnabled, 'change');
330 $('autofill-settings').disabled = true;
332 // Disable and turn off Password Manager in guest mode.
333 var passwordManagerEnabled = $('password-manager-enabled');
334 passwordManagerEnabled.disabled = true;
335 passwordManagerEnabled.checked = false;
336 cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
337 $('manage-passwords').disabled = true;
341 $('mac-passwords-warning').hidden =
342 !loadTimeData.getBoolean('multiple_profiles');
346 if (!cr.isChromeOS) {
347 $('proxiesConfigureButton').onclick = function(event) {
348 chrome.send('showNetworkProxySettings');
352 // Web Content section.
353 $('fontSettingsCustomizeFontsButton').onclick = function(event) {
354 OptionsPage.navigateToPage('fonts');
355 chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
357 $('defaultFontSize').onchange = function(event) {
358 var value = event.target.options[event.target.selectedIndex].value;
359 Preferences.setIntegerPref(
360 'webkit.webprefs.default_fixed_font_size',
361 value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
362 chrome.send('defaultFontSizeAction', [String(value)]);
364 $('defaultZoomFactor').onchange = function(event) {
365 chrome.send('defaultZoomFactorAction',
366 [String(event.target.options[event.target.selectedIndex].value)]);
369 // Languages section.
370 var showLanguageOptions = function(event) {
371 OptionsPage.navigateToPage('languages');
372 chrome.send('coreOptionsUserMetricsAction',
373 ['Options_LanuageAndSpellCheckSettings']);
375 $('language-button').onclick = showLanguageOptions;
376 $('manage-languages').onclick = showLanguageOptions;
378 // Downloads section.
379 Preferences.getInstance().addEventListener('download.default_directory',
380 this.onDefaultDownloadDirectoryChanged_.bind(this));
381 $('downloadLocationChangeButton').onclick = function(event) {
382 chrome.send('selectDownloadLocation');
384 if (!cr.isChromeOS) {
385 $('autoOpenFileTypesResetToDefault').onclick = function(event) {
386 chrome.send('autoOpenFileTypesAction');
389 $('disable-drive-row').hidden =
390 UIAccountTweaks.loggedInAsLocallyManagedUser();
393 // HTTPS/SSL section.
394 if (cr.isWindows || cr.isMac) {
395 $('certificatesManageButton').onclick = function(event) {
396 chrome.send('showManageSSLCertificates');
399 $('certificatesManageButton').onclick = function(event) {
400 OptionsPage.navigateToPage('certificates');
401 chrome.send('coreOptionsUserMetricsAction',
402 ['Options_ManageSSLCertificates']);
406 // Cloud Print section.
407 // 'cloudPrintProxyEnabled' is true for Chrome branded builds on
408 // certain platforms, or could be enabled by a lab.
409 if (!cr.isChromeOS) {
410 $('cloudPrintConnectorSetupButton').onclick = function(event) {
411 if ($('cloudPrintManageButton').style.display == 'none') {
412 // Disable the button, set its text to the intermediate state.
413 $('cloudPrintConnectorSetupButton').textContent =
414 loadTimeData.getString('cloudPrintConnectorEnablingButton');
415 $('cloudPrintConnectorSetupButton').disabled = true;
416 chrome.send('showCloudPrintSetupDialog');
418 chrome.send('disableCloudPrintConnector');
422 $('cloudPrintManageButton').onclick = function(event) {
423 chrome.send('showCloudPrintManagePage');
426 // Accessibility section (CrOS only).
428 $('accessibility-spoken-feedback-check').onchange = function(event) {
429 chrome.send('spokenFeedbackChange',
430 [$('accessibility-spoken-feedback-check').checked]);
433 $('accessibility-high-contrast-check').onchange = function(event) {
434 chrome.send('highContrastChange',
435 [$('accessibility-high-contrast-check').checked]);
439 // Display management section (CrOS only).
441 $('display-options').onclick = function(event) {
442 OptionsPage.navigateToPage('display');
443 chrome.send('coreOptionsUserMetricsAction',
444 ['Options_Display']);
448 // Factory reset section (CrOS only).
450 $('factory-reset-restart').onclick = function(event) {
451 OptionsPage.navigateToPage('factoryResetData');
456 if (!cr.isChromeOS) {
457 var updateGpuRestartButton = function() {
458 $('gpu-mode-reset-restart').hidden =
459 loadTimeData.getBoolean('gpuEnabledAtStart') ==
460 $('gpu-mode-checkbox').checked;
462 Preferences.getInstance().addEventListener(
463 $('gpu-mode-checkbox').getAttribute('pref'),
464 updateGpuRestartButton);
465 $('gpu-mode-reset-restart-button').onclick = function(event) {
466 chrome.send('restartBrowser');
468 updateGpuRestartButton();
471 // Reset profile settings section.
472 $('reset-profile-settings-section').hidden =
473 !loadTimeData.getValue('enableResetProfileSettingsSection');
474 $('reset-profile-settings').onclick = function(event) {
475 OptionsPage.navigateToPage('resetProfileSettings');
480 didShowPage: function() {
481 $('search-field').focus();
485 * Called after all C++ UI handlers have called InitializePage to notify
486 * that initialization is complete.
489 notifyInitializationComplete_: function() {
490 this.initializationComplete_ = true;
491 cr.dispatchSimpleEvent(document, 'initializationComplete');
495 * Event listener for the 'session.restore_on_startup' pref.
496 * @param {Event} event The preference change event.
499 onRestoreOnStartupChanged_: function(event) {
500 /** @const */ var showHomePageValue = 0;
502 if (event.value.value == showHomePageValue) {
503 // If the user previously selected "Show the homepage", the
504 // preference will already be migrated to "Open a specific page". So
505 // the only way to reach this code is if the 'restore on startup'
506 // preference is managed.
507 assert(event.value.controlledBy);
509 // Select "open the following pages" and lock down the list of URLs
510 // to reflect the intention of the policy.
511 $('startup-show-pages').checked = true;
512 StartupOverlay.getInstance().setControlsDisabled(true);
514 // Re-enable the controls in the startup overlay if necessary.
515 StartupOverlay.getInstance().updateControlStates();
520 * Handler for messages sent from the main uber page.
521 * @param {Event} e The 'message' event from the uber page.
524 handleWindowMessage_: function(e) {
525 if (e.data.method == 'frameSelected')
526 $('search-field').focus();
530 * Shows the given section.
531 * @param {HTMLElement} section The section to be shown.
532 * @param {HTMLElement} container The container for the section. Must be
533 * inside of |section|.
534 * @param {boolean} animate Indicate if the expansion should be animated.
537 showSection_: function(section, container, animate) {
539 this.addTransitionEndListener_(section);
542 section.hidden = false;
544 var expander = function() {
545 // Reveal the section using a WebKit transition if animating.
547 section.classList.add('sliding');
548 section.style.height = container.offsetHeight + 'px';
550 section.style.height = 'auto';
554 // Delay starting the transition if animating so that hidden change will
557 setTimeout(expander, 0);
563 * Shows the given section, with animation.
564 * @param {HTMLElement} section The section to be shown.
565 * @param {HTMLElement} container The container for the section. Must be
566 * inside of |section|.
569 showSectionWithAnimation_: function(section, container) {
570 this.showSection_(section, container, /*animate */ true);
574 * See showSectionWithAnimation_.
576 hideSectionWithAnimation_: function(section, container) {
577 this.addTransitionEndListener_(section);
579 // Before we start hiding the section, we need to set
580 // the height to a pixel value.
581 section.style.height = container.offsetHeight + 'px';
583 // Delay starting the transition so that the height change will be
585 setTimeout(function() {
586 // Hide the section using a WebKit transition.
587 section.classList.add('sliding');
588 section.style.height = '';
593 * See showSectionWithAnimation_.
595 toggleSectionWithAnimation_: function(section, container) {
596 if (section.style.height == '')
597 this.showSectionWithAnimation_(section, container);
599 this.hideSectionWithAnimation_(section, container);
603 * Scrolls the settings page to make the section visible auto-expanding
604 * advanced settings if required. The transition is not animated. This
605 * method is used to ensure that a section associated with an overlay
606 * is visible when the overlay is closed.
607 * @param {!Element} section The section to make visible.
610 scrollToSection_: function(section) {
611 var advancedSettings = $('advanced-settings');
612 var container = $('advanced-settings-container');
613 if (advancedSettings.hidden && section.parentNode == container) {
614 this.showSection_($('advanced-settings'),
615 $('advanced-settings-container'),
616 /* animate */ false);
617 this.updateAdvancedSettingsExpander_();
620 if (!this.initializationComplete_) {
622 var callback = function() {
623 document.removeEventListener('initializationComplete', callback);
624 self.scrollToSection_(section);
626 document.addEventListener('initializationComplete', callback);
630 var pageContainer = $('page-container');
631 var pageTop = parseFloat(pageContainer.style.top);
632 var topSection = document.querySelector('#page-container section');
633 var pageHeight = document.body.scrollHeight - topSection.offsetTop;
634 var sectionTop = section.offsetTop;
635 var sectionHeight = section.offsetHeight;
636 var marginBottom = window.getComputedStyle(section).marginBottom;
638 sectionHeight += parseFloat(marginBottom);
639 if (pageHeight - pageTop < sectionTop + sectionHeight) {
640 pageContainer.oldScrollTop = sectionTop + sectionHeight - pageHeight;
641 var verticalPosition = pageContainer.getBoundingClientRect().top -
642 pageContainer.oldScrollTop;
643 pageContainer.style.top = verticalPosition + 'px';
648 * Adds a |webkitTransitionEnd| listener to the given section so that
649 * it can be animated. The listener will only be added to a given section
650 * once, so this can be called as multiple times.
651 * @param {HTMLElement} section The section to be animated.
654 addTransitionEndListener_: function(section) {
655 if (section.hasTransitionEndListener_)
658 section.addEventListener('webkitTransitionEnd',
659 this.onTransitionEnd_.bind(this));
660 section.hasTransitionEndListener_ = true;
664 * Called after an animation transition has ended.
667 onTransitionEnd_: function(event) {
668 if (event.propertyName != 'height')
671 var section = event.target;
673 // Disable WebKit transitions.
674 section.classList.remove('sliding');
676 if (section.style.height == '') {
677 // Hide the content so it can't get tab focus.
678 section.hidden = true;
680 // Set the section height to 'auto' to allow for size changes
681 // (due to font change or dynamic content).
682 section.style.height = 'auto';
686 updateAdvancedSettingsExpander_: function() {
687 var expander = $('advanced-settings-expander');
688 if ($('advanced-settings').style.height == '')
689 expander.textContent = loadTimeData.getString('showAdvancedSettings');
691 expander.textContent = loadTimeData.getString('hideAdvancedSettings');
695 * Updates the sync section with the given state.
696 * @param {Object} syncData A bunch of data records that describe the status
697 * of the sync system.
700 updateSyncState_: function(syncData) {
701 if (!syncData.signinAllowed && !syncData.supervisedUser) {
702 $('sync-section').hidden = true;
706 $('sync-section').hidden = false;
708 var subSection = $('sync-section').firstChild;
710 if (subSection.nodeType == Node.ELEMENT_NODE)
711 subSection.hidden = syncData.supervisedUser;
712 subSection = subSection.nextSibling;
715 if (syncData.supervisedUser) {
716 $('account-picture-wrapper').hidden = false;
717 $('sync-general').hidden = false;
718 $('sync-status').hidden = true;
721 // If the user gets signed out or if sync gets disabled while the advanced
722 // sync settings dialog is visible, say, due to a dashboard clear, close
724 if ((this.signedIn_ && !syncData.signedIn) ||
725 (this.setupCompleted_ && !syncData.setupCompleted)) {
726 SyncSetupOverlay.closeOverlay();
729 this.signedIn_ = syncData.signedIn;
730 this.setupCompleted_ = syncData.setupCompleted;
732 // Display the "advanced settings" button if we're signed in and sync is
733 // not managed/disabled. If the user is signed in, but sync is disabled,
734 // this button is used to re-enable sync.
735 var customizeSyncButton = $('customize-sync');
736 customizeSyncButton.hidden = !this.signedIn_ ||
738 !syncData.syncSystemEnabled;
739 customizeSyncButton.textContent = syncData.setupCompleted ?
740 loadTimeData.getString('customizeSync') :
741 loadTimeData.getString('syncButtonTextStart');
743 // Disable the "sign in" button if we're currently signing in, or if we're
744 // already signed in and signout is not allowed.
745 var signInButton = $('start-stop-sync');
746 signInButton.disabled = syncData.setupInProgress ||
747 !syncData.signoutAllowed;
748 if (!syncData.signoutAllowed)
749 $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
751 $('start-stop-sync-indicator').removeAttribute('controlled-by');
753 // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome.
754 signInButton.hidden = cr.isChromeOS;
756 signInButton.textContent =
758 loadTimeData.getString('syncButtonTextStop') :
759 syncData.setupInProgress ?
760 loadTimeData.getString('syncButtonTextInProgress') :
761 loadTimeData.getString('syncButtonTextSignIn');
762 $('start-stop-sync-indicator').hidden = signInButton.hidden;
764 // TODO(estade): can this just be textContent?
765 $('sync-status-text').innerHTML = syncData.statusText;
766 var statusSet = syncData.statusText.length != 0;
767 $('sync-overview').hidden = statusSet;
768 $('sync-status').hidden = !statusSet;
770 $('sync-action-link').textContent = syncData.actionLinkText;
771 // Don't show the action link if it is empty or undefined.
772 $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
773 $('sync-action-link').disabled = syncData.managed ||
774 !syncData.syncSystemEnabled;
776 // On Chrome OS, sign out the user and sign in again to get fresh
777 // credentials on auth errors.
778 $('sync-action-link').onclick = function(event) {
779 if (cr.isChromeOS && syncData.hasError)
780 SyncSetupOverlay.doSignOutOnAuthError();
782 SyncSetupOverlay.showErrorUI();
785 if (syncData.hasError)
786 $('sync-status').classList.add('sync-error');
788 $('sync-status').classList.remove('sync-error');
790 customizeSyncButton.disabled = syncData.hasUnrecoverableError;
791 // Move #enable-auto-login-checkbox to a different location on CrOS.
793 $('sync-general').insertBefore($('sync-status').nextSibling,
794 $('enable-auto-login-checkbox'));
796 $('enable-auto-login-checkbox').hidden = !syncData.autoLoginVisible;
800 * Get the start/stop sync button DOM element. Used for testing.
801 * @return {DOMElement} The start/stop sync button.
804 getStartStopSyncButton_: function() {
805 return $('start-stop-sync');
809 * Event listener for the 'show home button' preference. Shows/hides the
810 * UI for changing the home page with animation, unless this is the first
811 * time this function is called, in which case there is no animation.
812 * @param {Event} event The preference change event.
814 onShowHomeButtonChanged_: function(event) {
815 var section = $('change-home-page-section');
816 if (this.onShowHomeButtonChangedCalled_) {
817 var container = $('change-home-page-section-container');
818 if (event.value.value)
819 this.showSectionWithAnimation_(section, container);
821 this.hideSectionWithAnimation_(section, container);
823 section.hidden = !event.value.value;
824 this.onShowHomeButtonChangedCalled_ = true;
829 * Event listener for the 'homepage is NTP' preference. Updates the label
830 * next to the 'Change' button.
831 * @param {Event} event The preference change event.
833 onHomePageIsNtpChanged_: function(event) {
834 if (!event.value.uncommitted) {
835 $('home-page-url').hidden = event.value.value;
836 $('home-page-ntp').hidden = !event.value.value;
841 * Event listener for changes to the homepage preference. Updates the label
842 * next to the 'Change' button.
843 * @param {Event} event The preference change event.
845 onHomePageChanged_: function(event) {
846 if (!event.value.uncommitted)
847 $('home-page-url').textContent = this.stripHttp_(event.value.value);
851 * Removes the 'http://' from a URL, like the omnibox does. If the string
852 * doesn't start with 'http://' it is returned unchanged.
853 * @param {string} url The url to be processed
854 * @return {string} The url with the 'http://' removed.
856 stripHttp_: function(url) {
857 return url.replace(/^http:\/\//, '');
861 * Shows the autoLaunch preference and initializes its checkbox value.
862 * @param {bool} enabled Whether autolaunch is enabled or or not.
865 updateAutoLaunchState_: function(enabled) {
866 $('auto-launch-option').hidden = false;
867 $('auto-launch').checked = enabled;
871 * Called when the value of the download.default_directory preference
873 * @param {Event} event Change event.
876 onDefaultDownloadDirectoryChanged_: function(event) {
877 $('downloadLocationPath').value = event.value.value;
879 // On ChromeOS, replace /special/drive with Drive for drive paths, and
880 // /home/chronos/user/Downloads with Downloads for local files.
881 // Also replace '/' with ' \u203a ' (angled quote sign) everywhere.
882 var path = $('downloadLocationPath').value;
883 path = path.replace(/^\/special\/drive\/root/, 'Google Drive');
884 path = path.replace(/^\/home\/chronos\/user\//, '');
885 path = path.replace(/\//g, ' \u203a ');
886 $('downloadLocationPath').value = path;
888 if (event.value.disabled)
889 $('download-location-label').classList.add('disabled');
891 $('download-location-label').classList.remove('disabled');
892 $('downloadLocationChangeButton').disabled = event.value.disabled;
896 * Update the Default Browsers section based on the current state.
897 * @param {string} statusString Description of the current default state.
898 * @param {boolean} isDefault Whether or not the browser is currently
900 * @param {boolean} canBeDefault Whether or not the browser can be default.
903 updateDefaultBrowserState_: function(statusString, isDefault,
905 if (!cr.isChromeOS) {
906 var label = $('default-browser-state');
907 label.textContent = statusString;
909 $('set-as-default-browser').hidden = !canBeDefault || isDefault;
914 * Clears the search engine popup.
917 clearSearchEngines_: function() {
918 $('default-search-engine').textContent = '';
922 * Updates the search engine popup with the given entries.
923 * @param {Array} engines List of available search engines.
924 * @param {number} defaultValue The value of the current default engine.
925 * @param {boolean} defaultManaged Whether the default search provider is
926 * managed. If true, the default search provider can't be changed.
929 updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
930 this.clearSearchEngines_();
931 engineSelect = $('default-search-engine');
932 engineSelect.disabled = defaultManaged;
933 if (defaultManaged && defaultValue == -1)
935 engineCount = engines.length;
936 var defaultIndex = -1;
937 for (var i = 0; i < engineCount; i++) {
938 var engine = engines[i];
939 var option = new Option(engine.name, engine.index);
940 if (defaultValue == option.value)
942 engineSelect.appendChild(option);
944 if (defaultIndex >= 0)
945 engineSelect.selectedIndex = defaultIndex;
949 * Set the default search engine based on the popup selection.
952 setDefaultSearchEngine_: function() {
953 var engineSelect = $('default-search-engine');
954 var selectedIndex = engineSelect.selectedIndex;
955 if (selectedIndex >= 0) {
956 var selection = engineSelect.options[selectedIndex];
957 chrome.send('setDefaultSearchEngine', [String(selection.value)]);
962 * Sets or clear whether Chrome should Auto-launch on computer startup.
965 handleAutoLaunchChanged_: function() {
966 chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
970 * Get the selected profile item from the profile list. This also works
971 * correctly if the list is not displayed.
972 * @return {Object} the profile item object, or null if nothing is selected.
975 getSelectedProfileItem_: function() {
976 var profilesList = $('profiles-list');
977 if (profilesList.hidden) {
978 if (profilesList.dataModel.length > 0)
979 return profilesList.dataModel.item(0);
981 return profilesList.selectedItem;
987 * Helper function to set the status of profile view buttons to disabled or
988 * enabled, depending on the number of profiles and selection status of the
992 setProfileViewButtonsStatus_: function() {
993 var profilesList = $('profiles-list');
994 var selectedProfile = profilesList.selectedItem;
995 var hasSelection = selectedProfile != null;
996 var hasSingleProfile = profilesList.dataModel.length == 1;
997 var isManaged = loadTimeData.getBoolean('profileIsManaged');
998 $('profiles-manage').disabled = !hasSelection ||
999 !selectedProfile.isCurrentProfile;
1000 if (hasSelection && !selectedProfile.isCurrentProfile)
1001 $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1003 $('profiles-manage').title = '';
1004 $('profiles-delete').disabled = isManaged ||
1005 (!hasSelection && !hasSingleProfile);
1006 if (OptionsPage.isSettingsApp()) {
1007 $('profiles-app-list-switch').disabled = !hasSelection ||
1008 selectedProfile.isCurrentProfile;
1010 var importData = $('import-data');
1012 importData.disabled = $('import-data').disabled = hasSelection &&
1013 !selectedProfile.isCurrentProfile;
1018 * Display the correct dialog layout, depending on how many profiles are
1020 * @param {number} numProfiles The number of profiles to display.
1023 setProfileViewSingle_: function(numProfiles) {
1024 var hasSingleProfile = numProfiles == 1;
1025 $('profiles-list').hidden = hasSingleProfile;
1026 $('profiles-single-message').hidden = !hasSingleProfile;
1027 $('profiles-manage').hidden =
1028 hasSingleProfile || OptionsPage.isSettingsApp();
1029 $('profiles-delete').textContent = hasSingleProfile ?
1030 loadTimeData.getString('profilesDeleteSingle') :
1031 loadTimeData.getString('profilesDelete');
1032 if (OptionsPage.isSettingsApp())
1033 $('profiles-app-list-switch').hidden = hasSingleProfile;
1037 * Adds all |profiles| to the list.
1038 * @param {Array.<Object>} profiles An array of profile info objects.
1039 * each object is of the form:
1041 * name: "Profile Name",
1042 * iconURL: "chrome://path/to/icon/image",
1043 * filePath: "/path/to/profile/data/on/disk",
1044 * isCurrentProfile: false
1048 setProfilesInfo_: function(profiles) {
1049 this.setProfileViewSingle_(profiles.length);
1050 // add it to the list, even if the list is hidden so we can access it
1052 $('profiles-list').dataModel = new ArrayDataModel(profiles);
1054 // Received new data. If showing the "manage" overlay, keep it up to
1055 // date. If showing the "delete" overlay, close it.
1056 if (ManageProfileOverlay.getInstance().visible &&
1057 !$('manage-profile-overlay-manage').hidden) {
1058 ManageProfileOverlay.showManageDialog();
1060 ManageProfileOverlay.getInstance().visible = false;
1063 this.setProfileViewButtonsStatus_();
1067 * Reports a local error (e.g., disk full) to the "create" overlay during
1071 showCreateProfileLocalError_: function() {
1072 CreateProfileOverlay.onLocalError();
1076 * Reports a remote error (e.g., a network error during managed-user
1077 * registration) to the "create" overlay during profile creation.
1080 showCreateProfileRemoteError_: function() {
1081 CreateProfileOverlay.onRemoteError();
1085 * Reports successful profile creation to the "create" overlay.
1086 * @param {Object} profileInfo An object of the form:
1088 * name: "Profile Name",
1089 * filePath: "/path/to/profile/data/on/disk"
1090 * isManaged: (true|false),
1094 showCreateProfileSuccess_: function(profileInfo) {
1095 CreateProfileOverlay.onSuccess(profileInfo);
1099 * Returns the currently active profile for this browser window.
1100 * @return {Object} A profile info object.
1103 getCurrentProfile_: function() {
1104 for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1105 var profile = $('profiles-list').dataModel.item(i);
1106 if (profile.isCurrentProfile)
1111 'There should always be a current profile, but none found.');
1114 setNativeThemeButtonEnabled_: function(enabled) {
1115 var button = $('themes-native-button');
1117 button.disabled = !enabled;
1120 setThemesResetButtonEnabled_: function(enabled) {
1121 $('themes-reset').disabled = !enabled;
1125 * (Re)loads IMG element with current user account picture.
1128 updateAccountPicture_: function() {
1129 var picture = $('account-picture');
1131 picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1137 * Handle the 'add device' button click.
1140 handleAddBluetoothDevice_: function() {
1141 chrome.send('findBluetoothDevices');
1142 OptionsPage.showPageByName('bluetooth', false);
1146 * Enables factory reset section.
1149 enableFactoryResetSection_: function() {
1150 $('factory-reset-section').hidden = false;
1154 * Set the checked state of the metrics reporting checkbox.
1157 setMetricsReportingCheckboxState_: function(checked, disabled) {
1158 $('metricsReportingEnabled').checked = checked;
1159 $('metricsReportingEnabled').disabled = disabled;
1165 setMetricsReportingSettingVisibility_: function(visible) {
1167 $('metricsReportingSetting').style.display = 'block';
1169 $('metricsReportingSetting').style.display = 'none';
1173 * Set the visibility of the password generation checkbox.
1176 setPasswordGenerationSettingVisibility_: function(visible) {
1178 $('password-generation-checkbox').style.display = 'block';
1180 $('password-generation-checkbox').style.display = 'none';
1184 * Set the font size selected item. This item actually reflects two
1185 * preferences: the default font size and the default fixed font size.
1187 * @param {Object} pref Information about the font size preferences.
1188 * @param {number} pref.value The value of the default font size pref.
1189 * @param {boolean} pref.disabled True if either pref not user modifiable.
1190 * @param {string} pref.controlledBy The source of the pref value(s) if
1191 * either pref is currently not controlled by the user.
1194 setFontSize_: function(pref) {
1195 var selectCtl = $('defaultFontSize');
1196 selectCtl.disabled = pref.disabled;
1197 // Create a synthetic pref change event decorated as
1198 // CoreOptionsHandler::CreateValueForPref() does.
1199 var event = new cr.Event('synthetic-font-size');
1202 controlledBy: pref.controlledBy,
1203 disabled: pref.disabled
1205 $('font-size-indicator').handlePrefChange(event);
1207 for (var i = 0; i < selectCtl.options.length; i++) {
1208 if (selectCtl.options[i].value == pref.value) {
1209 selectCtl.selectedIndex = i;
1211 selectCtl.remove($('Custom').index);
1216 // Add/Select Custom Option in the font size label list.
1218 var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1220 option.setAttribute('id', 'Custom');
1221 selectCtl.add(option);
1223 $('Custom').selected = true;
1227 * Populate the page zoom selector with values received from the caller.
1228 * @param {Array} items An array of items to populate the selector.
1229 * each object is an array with three elements as follows:
1230 * 0: The title of the item (string).
1231 * 1: The value of the item (number).
1232 * 2: Whether the item should be selected (boolean).
1235 setupPageZoomSelector_: function(items) {
1236 var element = $('defaultZoomFactor');
1238 // Remove any existing content.
1239 element.textContent = '';
1241 // Insert new child nodes into select element.
1242 var value, title, selected;
1243 for (var i = 0; i < items.length; i++) {
1244 title = items[i][0];
1245 value = items[i][1];
1246 selected = items[i][2];
1247 element.appendChild(new Option(title, value, false, selected));
1252 * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1254 * @param {boolean} display Whether to show the button and label or not.
1257 setAutoOpenFileTypesDisplayed_: function(display) {
1261 if ($('advanced-settings').hidden) {
1262 // If the Advanced section is hidden, don't animate the transition.
1263 $('auto-open-file-types-section').hidden = !display;
1266 this.showSectionWithAnimation_(
1267 $('auto-open-file-types-section'),
1268 $('auto-open-file-types-container'));
1270 this.hideSectionWithAnimation_(
1271 $('auto-open-file-types-section'),
1272 $('auto-open-file-types-container'));
1278 * Set the enabled state for the proxy settings button.
1281 setupProxySettingsSection_: function(disabled, extensionControlled) {
1282 if (!cr.isChromeOS) {
1283 $('proxiesConfigureButton').disabled = disabled;
1284 $('proxiesLabel').textContent =
1285 loadTimeData.getString(extensionControlled ?
1286 'proxiesLabelExtension' : 'proxiesLabelSystem');
1291 * Set the Cloud Print proxy UI to enabled, disabled, or processing.
1294 setupCloudPrintConnectorSection_: function(disabled, label, allowed) {
1295 if (!cr.isChromeOS) {
1296 $('cloudPrintConnectorLabel').textContent = label;
1297 if (disabled || !allowed) {
1298 $('cloudPrintConnectorSetupButton').textContent =
1299 loadTimeData.getString('cloudPrintConnectorDisabledButton');
1300 $('cloudPrintManageButton').style.display = 'none';
1302 $('cloudPrintConnectorSetupButton').textContent =
1303 loadTimeData.getString('cloudPrintConnectorEnabledButton');
1304 $('cloudPrintManageButton').style.display = 'inline';
1306 $('cloudPrintConnectorSetupButton').disabled = !allowed;
1313 removeCloudPrintConnectorSection_: function() {
1314 if (!cr.isChromeOS) {
1315 var connectorSectionElm = $('cloud-print-connector-section');
1316 if (connectorSectionElm)
1317 connectorSectionElm.parentNode.removeChild(connectorSectionElm);
1322 * Set the initial state of the spoken feedback checkbox.
1325 setSpokenFeedbackCheckboxState_: function(checked) {
1326 $('accessibility-spoken-feedback-check').checked = checked;
1330 * Set the initial state of the high contrast checkbox.
1333 setHighContrastCheckboxState_: function(checked) {
1334 $('accessibility-high-contrast-check').checked = checked;
1338 * Set the initial state of the virtual keyboard checkbox.
1341 setVirtualKeyboardCheckboxState_: function(checked) {
1342 // TODO(zork): Update UI
1346 * Show/hide mouse settings slider.
1349 showMouseControls_: function(show) {
1350 $('mouse-settings').hidden = !show;
1354 * Show/hide touchpad-related settings.
1357 showTouchpadControls_: function(show) {
1358 $('touchpad-settings').hidden = !show;
1359 $('accessibility-tap-dragging').hidden = !show;
1363 * Activate the Bluetooth settings section on the System settings page.
1366 showBluetoothSettings_: function() {
1367 $('bluetooth-devices').hidden = false;
1371 * Dectivates the Bluetooth settings section from the System settings page.
1374 hideBluetoothSettings_: function() {
1375 $('bluetooth-devices').hidden = true;
1379 * Sets the state of the checkbox indicating if Bluetooth is turned on. The
1380 * state of the "Find devices" button and the list of discovered devices may
1381 * also be affected by a change to the state.
1382 * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
1385 setBluetoothState_: function(checked) {
1386 $('enable-bluetooth').checked = checked;
1387 $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
1388 $('bluetooth-add-device').hidden = !checked;
1389 $('bluetooth-reconnect-device').hidden = !checked;
1390 // Flush list of previously discovered devices if bluetooth is turned off.
1392 $('bluetooth-paired-devices-list').clear();
1393 $('bluetooth-unpaired-devices-list').clear();
1395 chrome.send('getPairedBluetoothDevices');
1400 * Adds an element to the list of available Bluetooth devices. If an element
1401 * with a matching address is found, the existing element is updated.
1402 * @param {{name: string,
1405 * connected: boolean}} device
1406 * Decription of the Bluetooth device.
1409 addBluetoothDevice_: function(device) {
1410 var list = $('bluetooth-unpaired-devices-list');
1411 // Display the "connecting" (already paired or not yet paired) and the
1412 // paired devices in the same list.
1413 if (device.paired || device.connecting) {
1414 // Test to see if the device is currently in the unpaired list, in which
1415 // case it should be removed from that list.
1416 var index = $('bluetooth-unpaired-devices-list').find(device.address);
1417 if (index != undefined)
1418 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1419 list = $('bluetooth-paired-devices-list');
1421 // Test to see if the device is currently in the paired list, in which
1422 // case it should be removed from that list.
1423 var index = $('bluetooth-paired-devices-list').find(device.address);
1424 if (index != undefined)
1425 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1427 list.appendDevice(device);
1429 // One device can be in the process of pairing. If found, display
1430 // the Bluetooth pairing overlay.
1432 BluetoothPairing.showDialog(device);
1436 * Removes an element from the list of available devices.
1437 * @param {string} address Unique address of the device.
1440 removeBluetoothDevice_: function(address) {
1441 var index = $('bluetooth-unpaired-devices-list').find(address);
1442 if (index != undefined) {
1443 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1445 index = $('bluetooth-paired-devices-list').find(address);
1446 if (index != undefined)
1447 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1452 //Forward public APIs to private implementations.
1454 'addBluetoothDevice',
1455 'enableFactoryResetSection',
1456 'getCurrentProfile',
1457 'getStartStopSyncButton',
1458 'hideBluetoothSettings',
1459 'notifyInitializationComplete',
1460 'removeBluetoothDevice',
1461 'removeCloudPrintConnectorSection',
1463 'setAutoOpenFileTypesDisplayed',
1464 'setBluetoothState',
1466 'setNativeThemeButtonEnabled',
1467 'setHighContrastCheckboxState',
1468 'setMetricsReportingCheckboxState',
1469 'setMetricsReportingSettingVisibility',
1470 'setPasswordGenerationSettingVisibility',
1472 'setSpokenFeedbackCheckboxState',
1473 'setThemesResetButtonEnabled',
1474 'setVirtualKeyboardCheckboxState',
1475 'setupCloudPrintConnectorSection',
1476 'setupPageZoomSelector',
1477 'setupProxySettingsSection',
1478 'showBluetoothSettings',
1479 'showCreateProfileLocalError',
1480 'showCreateProfileRemoteError',
1481 'showCreateProfileSuccess',
1482 'showMouseControls',
1483 'showTouchpadControls',
1484 'updateAccountPicture',
1485 'updateAutoLaunchState',
1486 'updateDefaultBrowserState',
1487 'updateSearchEngines',
1488 'updateStartupPages',
1490 ].forEach(function(name) {
1491 BrowserOptions[name] = function() {
1492 var instance = BrowserOptions.getInstance();
1493 return instance[name + '_'].apply(instance, arguments);
1497 if (cr.isChromeOS) {
1499 * Returns username (canonical email) of the user logged in (ChromeOS only).
1500 * @return {string} user email.
1502 // TODO(jhawkins): Investigate the use case for this method.
1503 BrowserOptions.getLoggedInUsername = function() {
1504 return BrowserOptions.getInstance().username_;
1510 BrowserOptions: BrowserOptions