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 |onShowHomeButtonChanged_| has been called. See
33 * |onShowHomeButtonChanged_|.
37 onShowHomeButtonChangedCalled_: false,
40 * Track if page initialization is complete. All C++ UI handlers have the
41 * chance to manipulate page content within their InitializePage methods.
42 * This flag is set to true after all initializers have been called.
46 initializationComplete_: false,
49 initializePage: function() {
50 OptionsPage.prototype.initializePage.call(this);
53 // Ensure that navigation events are unblocked on uber page. A reload of
54 // the settings page while an overlay is open would otherwise leave uber
55 // page in a blocked state, where tab switching is not possible.
56 uber.invokeMethodOnParent('stopInterceptingEvents');
58 window.addEventListener('message', this.handleWindowMessage_.bind(this));
60 $('advanced-settings-expander').onclick = function() {
61 self.toggleSectionWithAnimation_(
62 $('advanced-settings'),
63 $('advanced-settings-container'));
65 // If the link was focused (i.e., it was activated using the keyboard)
66 // and it was used to show the section (rather than hiding it), focus
67 // the first element in the container.
68 if (document.activeElement === $('advanced-settings-expander') &&
69 $('advanced-settings').style.height === '') {
70 var focusElement = $('advanced-settings-container').querySelector(
71 'button, input, list, select, a[href]');
77 $('advanced-settings').addEventListener('webkitTransitionEnd',
78 this.updateAdvancedSettingsExpander_.bind(this));
81 UIAccountTweaks.applyGuestModeVisibility(document);
83 // Sync (Sign in) section.
84 this.updateSyncState_(loadTimeData.getValue('syncData'));
86 $('start-stop-sync').onclick = function(event) {
88 SyncSetupOverlay.showStopSyncingUI();
89 else if (cr.isChromeOS)
90 SyncSetupOverlay.showSetupUI();
92 SyncSetupOverlay.startSignIn();
94 $('customize-sync').onclick = function(event) {
95 SyncSetupOverlay.showSetupUI();
98 // Internet connection section (ChromeOS only).
100 options.network.NetworkList.decorate($('network-list'));
101 options.network.NetworkList.refreshNetworkData(
102 loadTimeData.getValue('networkData'));
105 // On Startup section.
106 Preferences.getInstance().addEventListener('session.restore_on_startup',
107 this.onRestoreOnStartupChanged_.bind(this));
108 Preferences.getInstance().addEventListener(
109 'session.startup_urls',
111 $('startup-set-pages').disabled = event.value.disabled;
114 $('startup-set-pages').onclick = function() {
115 OptionsPage.navigateToPage('startup');
118 // Appearance section.
119 Preferences.getInstance().addEventListener('browser.show_home_button',
120 this.onShowHomeButtonChanged_.bind(this));
122 Preferences.getInstance().addEventListener('homepage',
123 this.onHomePageChanged_.bind(this));
124 Preferences.getInstance().addEventListener('homepage_is_newtabpage',
125 this.onHomePageIsNtpChanged_.bind(this));
127 $('change-home-page').onclick = function(event) {
128 OptionsPage.navigateToPage('homePageOverlay');
131 if ($('set-wallpaper')) {
132 $('set-wallpaper').onclick = function(event) {
133 chrome.send('openWallpaperManager');
137 $('themes-gallery').onclick = function(event) {
138 window.open(loadTimeData.getString('themesGalleryURL'));
140 $('themes-reset').onclick = function(event) {
141 chrome.send('themesReset');
144 if (loadTimeData.getBoolean('profileIsManaged')) {
145 if ($('themes-native-button')) {
146 $('themes-native-button').disabled = true;
147 $('themes-native-button').hidden = true;
149 // Supervised users have just one default theme, even on Linux. So use
150 // the same button for Linux as for the other platforms.
151 $('themes-reset').textContent = loadTimeData.getString('themesReset');
154 // Device section (ChromeOS only).
156 $('keyboard-settings-button').onclick = function(evt) {
157 OptionsPage.navigateToPage('keyboard-overlay');
159 $('pointer-settings-button').onclick = function(evt) {
160 OptionsPage.navigateToPage('pointer-overlay');
165 $('manage-default-search-engines').onclick = function(event) {
166 OptionsPage.navigateToPage('searchEngines');
167 chrome.send('coreOptionsUserMetricsAction',
168 ['Options_ManageSearchEngines']);
170 $('default-search-engine').addEventListener('change',
171 this.setDefaultSearchEngine_);
172 // Without this, the bubble would overlap the uber frame navigation pane
173 // and would not get mouse event as explained in crbug.com/311421.
174 document.querySelector(
175 '#default-search-engine + .controlled-setting-indicator').location =
176 cr.ui.ArrowLocation.TOP_START;
179 if (loadTimeData.valueExists('profilesInfo')) {
180 $('profiles-section').hidden = false;
182 var profilesList = $('profiles-list');
183 options.browser_options.ProfileList.decorate(profilesList);
184 profilesList.autoExpands = true;
186 // The profiles info data in |loadTimeData| might be stale.
187 this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
188 chrome.send('requestProfilesInfo');
190 profilesList.addEventListener('change',
191 this.setProfileViewButtonsStatus_);
192 $('profiles-create').onclick = function(event) {
193 ManageProfileOverlay.showCreateDialog();
195 if (OptionsPage.isSettingsApp()) {
196 $('profiles-app-list-switch').onclick = function(event) {
197 var selectedProfile = self.getSelectedProfileItem_();
198 chrome.send('switchAppListProfile', [selectedProfile.filePath]);
201 $('profiles-manage').onclick = function(event) {
202 ManageProfileOverlay.showManageDialog();
204 $('profiles-delete').onclick = function(event) {
205 var selectedProfile = self.getSelectedProfileItem_();
207 ManageProfileOverlay.showDeleteDialog(selectedProfile);
209 if (loadTimeData.getBoolean('profileIsManaged')) {
210 $('profiles-create').disabled = true;
211 $('profiles-delete').disabled = true;
212 $('profiles-list').canDeleteItems = false;
217 // Username (canonical email) of the currently logged in user or
218 // |kGuestUser| if a guest session is active.
219 this.username_ = loadTimeData.getString('username');
221 this.updateAccountPicture_();
223 $('account-picture').onclick = this.showImagerPickerOverlay_;
224 $('change-picture-caption').onclick = this.showImagerPickerOverlay_;
226 $('manage-accounts-button').onclick = function(event) {
227 OptionsPage.navigateToPage('accounts');
228 chrome.send('coreOptionsUserMetricsAction',
229 ['Options_ManageAccounts']);
232 $('import-data').onclick = function(event) {
233 ImportDataOverlay.show();
234 chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
237 if ($('themes-native-button')) {
238 $('themes-native-button').onclick = function(event) {
239 chrome.send('themesSetNative');
244 // Default browser section.
245 if (!cr.isChromeOS) {
246 $('set-as-default-browser').onclick = function(event) {
247 chrome.send('becomeDefaultBrowser');
250 $('auto-launch').onclick = this.handleAutoLaunchChanged_;
254 $('privacyContentSettingsButton').onclick = function(event) {
255 OptionsPage.navigateToPage('content');
256 OptionsPage.showTab($('cookies-nav-tab'));
257 chrome.send('coreOptionsUserMetricsAction',
258 ['Options_ContentSettings']);
260 $('privacyClearDataButton').onclick = function(event) {
261 OptionsPage.navigateToPage('clearBrowserData');
262 chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
264 $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
265 // 'metricsReportingEnabled' element is only present on Chrome branded
266 // builds, and the 'metricsReportingCheckboxAction' message is only
267 // handled on ChromeOS.
268 if ($('metricsReportingEnabled') && cr.isChromeOS) {
269 $('metricsReportingEnabled').onclick = function(event) {
270 chrome.send('metricsReportingCheckboxAction',
271 [String(event.currentTarget.checked)]);
275 // Bluetooth (CrOS only).
277 options.system.bluetooth.BluetoothDeviceList.decorate(
278 $('bluetooth-paired-devices-list'));
280 $('bluetooth-add-device').onclick =
281 this.handleAddBluetoothDevice_.bind(this);
283 $('enable-bluetooth').onchange = function(event) {
284 var state = $('enable-bluetooth').checked;
285 chrome.send('bluetoothEnableChange', [Boolean(state)]);
288 $('bluetooth-reconnect-device').onclick = function(event) {
289 var device = $('bluetooth-paired-devices-list').selectedItem;
290 var address = device.address;
291 chrome.send('updateBluetoothDevice', [address, 'connect']);
292 OptionsPage.closeOverlay();
295 $('bluetooth-paired-devices-list').addEventListener('change',
297 var item = $('bluetooth-paired-devices-list').selectedItem;
298 var disabled = !item || item.connected || !item.connectable;
299 $('bluetooth-reconnect-device').disabled = disabled;
303 // Passwords and Forms section.
304 $('autofill-settings').onclick = function(event) {
305 OptionsPage.navigateToPage('autofill');
306 chrome.send('coreOptionsUserMetricsAction',
307 ['Options_ShowAutofillSettings']);
309 $('manage-passwords').onclick = function(event) {
310 OptionsPage.navigateToPage('passwords');
311 OptionsPage.showTab($('passwords-nav-tab'));
312 chrome.send('coreOptionsUserMetricsAction',
313 ['Options_ShowPasswordManager']);
315 if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
316 // Disable and turn off Autofill in guest mode.
317 var autofillEnabled = $('autofill-enabled');
318 autofillEnabled.disabled = true;
319 autofillEnabled.checked = false;
320 cr.dispatchSimpleEvent(autofillEnabled, 'change');
321 $('autofill-settings').disabled = true;
323 // Disable and turn off Password Manager in guest mode.
324 var passwordManagerEnabled = $('password-manager-enabled');
325 passwordManagerEnabled.disabled = true;
326 passwordManagerEnabled.checked = false;
327 cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
328 $('manage-passwords').disabled = true;
332 $('mac-passwords-warning').hidden =
333 !loadTimeData.getBoolean('multiple_profiles');
337 if (!cr.isChromeOS) {
338 $('proxiesConfigureButton').onclick = function(event) {
339 chrome.send('showNetworkProxySettings');
343 // Web Content section.
344 $('fontSettingsCustomizeFontsButton').onclick = function(event) {
345 OptionsPage.navigateToPage('fonts');
346 chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
348 $('defaultFontSize').onchange = function(event) {
349 var value = event.target.options[event.target.selectedIndex].value;
350 Preferences.setIntegerPref(
351 'webkit.webprefs.default_fixed_font_size',
352 value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
353 chrome.send('defaultFontSizeAction', [String(value)]);
355 $('defaultZoomFactor').onchange = function(event) {
356 chrome.send('defaultZoomFactorAction',
357 [String(event.target.options[event.target.selectedIndex].value)]);
360 // Languages section.
361 var showLanguageOptions = function(event) {
362 OptionsPage.navigateToPage('languages');
363 chrome.send('coreOptionsUserMetricsAction',
364 ['Options_LanuageAndSpellCheckSettings']);
366 $('language-button').onclick = showLanguageOptions;
367 $('manage-languages').onclick = showLanguageOptions;
369 // Downloads section.
370 Preferences.getInstance().addEventListener('download.default_directory',
371 this.onDefaultDownloadDirectoryChanged_.bind(this));
372 $('downloadLocationChangeButton').onclick = function(event) {
373 chrome.send('selectDownloadLocation');
375 if (!cr.isChromeOS) {
376 $('autoOpenFileTypesResetToDefault').onclick = function(event) {
377 chrome.send('autoOpenFileTypesAction');
380 $('disable-drive-row').hidden =
381 UIAccountTweaks.loggedInAsLocallyManagedUser();
384 // HTTPS/SSL section.
385 if (cr.isWindows || cr.isMac) {
386 $('certificatesManageButton').onclick = function(event) {
387 chrome.send('showManageSSLCertificates');
390 $('certificatesManageButton').onclick = function(event) {
391 OptionsPage.navigateToPage('certificates');
392 chrome.send('coreOptionsUserMetricsAction',
393 ['Options_ManageSSLCertificates']);
397 // Cloud Print section.
398 // 'cloudPrintProxyEnabled' is true for Chrome branded builds on
399 // certain platforms, or could be enabled by a lab.
400 if (!cr.isChromeOS) {
401 $('cloudPrintConnectorSetupButton').onclick = function(event) {
402 if ($('cloudPrintManageButton').style.display == 'none') {
403 // Disable the button, set its text to the intermediate state.
404 $('cloudPrintConnectorSetupButton').textContent =
405 loadTimeData.getString('cloudPrintConnectorEnablingButton');
406 $('cloudPrintConnectorSetupButton').disabled = true;
407 chrome.send('showCloudPrintSetupDialog');
409 chrome.send('disableCloudPrintConnector');
413 $('cloudPrintManageButton').onclick = function(event) {
414 chrome.send('showCloudPrintManagePage');
417 if (loadTimeData.getBoolean('cloudPrintShowMDnsOptions')) {
418 $('cloudprint-options-mdns').hidden = false;
419 $('cloudprint-options-nomdns').hidden = true;
420 $('cloudPrintDevicesPageButton').onclick = function() {
421 chrome.send('showCloudPrintDevicesPage');
425 // Accessibility section (CrOS only).
427 var updateAccessibilitySettingsButton = function() {
428 $('accessibility-settings').hidden =
429 !($('accessibility-spoken-feedback-check').checked);
431 Preferences.getInstance().addEventListener(
432 'settings.accessibility',
433 updateAccessibilitySettingsButton);
434 $('accessibility-settings-button').onclick = function(event) {
435 window.open(loadTimeData.getString('accessibilitySettingsURL'));
437 $('accessibility-spoken-feedback-check').onchange = function(event) {
438 chrome.send('spokenFeedbackChange',
439 [$('accessibility-spoken-feedback-check').checked]);
440 updateAccessibilitySettingsButton();
442 updateAccessibilitySettingsButton();
444 $('accessibility-high-contrast-check').onchange = function(event) {
445 chrome.send('highContrastChange',
446 [$('accessibility-high-contrast-check').checked]);
449 var updateDelayDropdown = function() {
450 $('accessibility-autoclick-dropdown').disabled =
451 !$('accessibility-autoclick-check').checked;
453 Preferences.getInstance().addEventListener(
454 $('accessibility-autoclick-check').getAttribute('pref'),
455 updateDelayDropdown);
457 $('accessibility-sticky-keys').hidden =
458 !loadTimeData.getBoolean('enableStickyKeys');
461 // Display management section (CrOS only).
463 $('display-options').onclick = function(event) {
464 OptionsPage.navigateToPage('display');
465 chrome.send('coreOptionsUserMetricsAction',
466 ['Options_Display']);
470 // Factory reset section (CrOS only).
472 $('factory-reset-restart').onclick = function(event) {
473 OptionsPage.navigateToPage('factoryResetData');
478 if (!cr.isChromeOS) {
479 var updateGpuRestartButton = function() {
480 $('gpu-mode-reset-restart').hidden =
481 loadTimeData.getBoolean('gpuEnabledAtStart') ==
482 $('gpu-mode-checkbox').checked;
484 Preferences.getInstance().addEventListener(
485 $('gpu-mode-checkbox').getAttribute('pref'),
486 updateGpuRestartButton);
487 $('gpu-mode-reset-restart-button').onclick = function(event) {
488 chrome.send('restartBrowser');
490 updateGpuRestartButton();
493 // Reset profile settings section.
494 $('reset-profile-settings').onclick = function(event) {
495 OptionsPage.navigateToPage('resetProfileSettings');
497 $('reset-profile-settings-section').hidden =
498 !loadTimeData.getBoolean('enableResetProfileSettings');
502 didShowPage: function() {
503 $('search-field').focus();
507 * Called after all C++ UI handlers have called InitializePage to notify
508 * that initialization is complete.
511 notifyInitializationComplete_: function() {
512 this.initializationComplete_ = true;
513 cr.dispatchSimpleEvent(document, 'initializationComplete');
517 * Event listener for the 'session.restore_on_startup' pref.
518 * @param {Event} event The preference change event.
521 onRestoreOnStartupChanged_: function(event) {
522 /** @const */ var showHomePageValue = 0;
524 if (event.value.value == showHomePageValue) {
525 // If the user previously selected "Show the homepage", the
526 // preference will already be migrated to "Open a specific page". So
527 // the only way to reach this code is if the 'restore on startup'
528 // preference is managed.
529 assert(event.value.controlledBy);
531 // Select "open the following pages" and lock down the list of URLs
532 // to reflect the intention of the policy.
533 $('startup-show-pages').checked = true;
534 StartupOverlay.getInstance().setControlsDisabled(true);
536 // Re-enable the controls in the startup overlay if necessary.
537 StartupOverlay.getInstance().updateControlStates();
542 * Handler for messages sent from the main uber page.
543 * @param {Event} e The 'message' event from the uber page.
546 handleWindowMessage_: function(e) {
547 if (e.data.method == 'frameSelected')
548 $('search-field').focus();
552 * Shows the given section.
553 * @param {HTMLElement} section The section to be shown.
554 * @param {HTMLElement} container The container for the section. Must be
555 * inside of |section|.
556 * @param {boolean} animate Indicate if the expansion should be animated.
559 showSection_: function(section, container, animate) {
561 this.addTransitionEndListener_(section);
564 section.hidden = false;
565 section.style.height = '0px';
567 var expander = function() {
568 // Reveal the section using a WebKit transition if animating.
570 section.classList.add('sliding');
571 section.style.height = container.offsetHeight + 'px';
573 section.style.height = 'auto';
577 // Delay starting the transition if animating so that hidden change will
580 setTimeout(expander, 0);
586 * Shows the given section, with animation.
587 * @param {HTMLElement} section The section to be shown.
588 * @param {HTMLElement} container The container for the section. Must be
589 * inside of |section|.
592 showSectionWithAnimation_: function(section, container) {
593 this.showSection_(section, container, /*animate */ true);
597 * See showSectionWithAnimation_.
599 hideSectionWithAnimation_: function(section, container) {
600 this.addTransitionEndListener_(section);
602 // Before we start hiding the section, we need to set
603 // the height to a pixel value.
604 section.style.height = container.offsetHeight + 'px';
606 // Delay starting the transition so that the height change will be
608 setTimeout(function() {
609 // Hide the section using a WebKit transition.
610 section.classList.add('sliding');
611 section.style.height = '0px';
616 * See showSectionWithAnimation_.
618 toggleSectionWithAnimation_: function(section, container) {
619 if (section.style.height == '')
620 this.showSectionWithAnimation_(section, container);
622 this.hideSectionWithAnimation_(section, container);
626 * Scrolls the settings page to make the section visible auto-expanding
627 * advanced settings if required. The transition is not animated. This
628 * method is used to ensure that a section associated with an overlay
629 * is visible when the overlay is closed.
630 * @param {!Element} section The section to make visible.
633 scrollToSection_: function(section) {
634 var advancedSettings = $('advanced-settings');
635 var container = $('advanced-settings-container');
636 if (advancedSettings.hidden && section.parentNode == container) {
637 this.showSection_($('advanced-settings'),
638 $('advanced-settings-container'),
639 /* animate */ false);
640 this.updateAdvancedSettingsExpander_();
643 if (!this.initializationComplete_) {
645 var callback = function() {
646 document.removeEventListener('initializationComplete', callback);
647 self.scrollToSection_(section);
649 document.addEventListener('initializationComplete', callback);
653 var pageContainer = $('page-container');
654 // pageContainer.offsetTop is relative to the screen.
655 var pageTop = pageContainer.offsetTop;
656 var sectionBottom = section.offsetTop + section.offsetHeight;
657 // section.offsetTop is relative to the 'page-container'.
658 var sectionTop = section.offsetTop;
659 if (pageTop + sectionBottom > document.body.scrollHeight ||
660 pageTop + sectionTop < 0) {
661 // Currently not all layout updates are guaranteed to precede the
662 // initializationComplete event (for example 'set-as-default-browser'
663 // button) leaving some uncertainty in the optimal scroll position.
664 // The section is placed approximately in the middle of the screen.
665 var top = Math.min(0, document.body.scrollHeight / 2 - sectionBottom);
666 pageContainer.style.top = top + 'px';
667 pageContainer.oldScrollTop = -top;
672 * Adds a |webkitTransitionEnd| listener to the given section so that
673 * it can be animated. The listener will only be added to a given section
674 * once, so this can be called as multiple times.
675 * @param {HTMLElement} section The section to be animated.
678 addTransitionEndListener_: function(section) {
679 if (section.hasTransitionEndListener_)
682 section.addEventListener('webkitTransitionEnd',
683 this.onTransitionEnd_.bind(this));
684 section.hasTransitionEndListener_ = true;
688 * Called after an animation transition has ended.
691 onTransitionEnd_: function(event) {
692 if (event.propertyName != 'height')
695 var section = event.target;
697 // Disable WebKit transitions.
698 section.classList.remove('sliding');
700 if (section.style.height == '0px') {
701 // Hide the content so it can't get tab focus.
702 section.hidden = true;
703 section.style.height = '';
705 // Set the section height to 'auto' to allow for size changes
706 // (due to font change or dynamic content).
707 section.style.height = 'auto';
711 updateAdvancedSettingsExpander_: function() {
712 var expander = $('advanced-settings-expander');
713 if ($('advanced-settings').style.height == '0px')
714 expander.textContent = loadTimeData.getString('showAdvancedSettings');
716 expander.textContent = loadTimeData.getString('hideAdvancedSettings');
720 * Updates the sync section with the given state.
721 * @param {Object} syncData A bunch of data records that describe the status
722 * of the sync system.
725 updateSyncState_: function(syncData) {
726 if (!syncData.signinAllowed &&
727 (!syncData.supervisedUser || !cr.isChromeOS)) {
728 $('sync-section').hidden = true;
732 $('sync-section').hidden = false;
734 var subSection = $('sync-section').firstChild;
736 if (subSection.nodeType == Node.ELEMENT_NODE)
737 subSection.hidden = syncData.supervisedUser;
738 subSection = subSection.nextSibling;
741 if (syncData.supervisedUser) {
742 $('account-picture-wrapper').hidden = false;
743 $('sync-general').hidden = false;
744 $('sync-status').hidden = true;
748 // If the user gets signed out while the advanced sync settings dialog is
749 // visible, say, due to a dashboard clear, close the dialog.
750 // However, if the user gets signed out as a result of abandoning first
751 // time sync setup, do not call closeOverlay as it will redirect the
752 // browser to the main settings page and override any in-progress
753 // user-initiated navigation. See crbug.com/278030.
754 // Note: SyncSetupOverlay.closeOverlay is a no-op if the overlay is
756 if (this.signedIn_ && !syncData.signedIn && !syncData.setupInProgress)
757 SyncSetupOverlay.closeOverlay();
759 this.signedIn_ = syncData.signedIn;
761 // Display the "advanced settings" button if we're signed in and sync is
762 // not managed/disabled. If the user is signed in, but sync is disabled,
763 // this button is used to re-enable sync.
764 var customizeSyncButton = $('customize-sync');
765 customizeSyncButton.hidden = !this.signedIn_ ||
767 !syncData.syncSystemEnabled;
769 // Only modify the customize button's text if the new text is different.
770 // Otherwise, it can affect search-highlighting in the settings page.
771 // See http://crbug.com/268265.
772 var customizeSyncButtonNewText = syncData.setupCompleted ?
773 loadTimeData.getString('customizeSync') :
774 loadTimeData.getString('syncButtonTextStart');
775 if (customizeSyncButton.textContent != customizeSyncButtonNewText)
776 customizeSyncButton.textContent = customizeSyncButtonNewText;
778 // Disable the "sign in" button if we're currently signing in, or if we're
779 // already signed in and signout is not allowed.
780 var signInButton = $('start-stop-sync');
781 signInButton.disabled = syncData.setupInProgress ||
782 !syncData.signoutAllowed;
783 if (!syncData.signoutAllowed)
784 $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
786 $('start-stop-sync-indicator').removeAttribute('controlled-by');
788 // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome.
789 signInButton.hidden = cr.isChromeOS;
791 signInButton.textContent =
793 loadTimeData.getString('syncButtonTextStop') :
794 syncData.setupInProgress ?
795 loadTimeData.getString('syncButtonTextInProgress') :
796 loadTimeData.getString('syncButtonTextSignIn');
797 $('start-stop-sync-indicator').hidden = signInButton.hidden;
799 // TODO(estade): can this just be textContent?
800 $('sync-status-text').innerHTML = syncData.statusText;
801 var statusSet = syncData.statusText.length != 0;
802 $('sync-overview').hidden = statusSet;
803 $('sync-status').hidden = !statusSet;
805 $('sync-action-link').textContent = syncData.actionLinkText;
806 // Don't show the action link if it is empty or undefined.
807 $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
808 $('sync-action-link').disabled = syncData.managed ||
809 !syncData.syncSystemEnabled;
811 // On Chrome OS, sign out the user and sign in again to get fresh
812 // credentials on auth errors.
813 $('sync-action-link').onclick = function(event) {
814 if (cr.isChromeOS && syncData.hasError)
815 SyncSetupOverlay.doSignOutOnAuthError();
817 SyncSetupOverlay.showSetupUI();
820 if (syncData.hasError)
821 $('sync-status').classList.add('sync-error');
823 $('sync-status').classList.remove('sync-error');
825 // Disable the "customize / set up sync" button if sync has an
826 // unrecoverable error. Also disable the button if sync has not been set
827 // up and the user is being presented with a link to re-auth.
828 // See crbug.com/289791.
829 customizeSyncButton.disabled =
830 syncData.hasUnrecoverableError ||
831 (!syncData.setupCompleted && !$('sync-action-link').hidden);
833 // Move #enable-auto-login-checkbox to a different location on CrOS.
835 $('sync-general').insertBefore($('sync-status').nextSibling,
836 $('enable-auto-login-checkbox'));
838 $('enable-auto-login-checkbox').hidden = !syncData.autoLoginVisible;
842 * Update the UI depending on whether the current profile manages any
844 * @param {boolean} value True if the current profile manages any supervised
847 updateManagesSupervisedUsers_: function(value) {
848 $('profiles-supervised-dashboard-tip').hidden = !value;
852 * Get the start/stop sync button DOM element. Used for testing.
853 * @return {DOMElement} The start/stop sync button.
856 getStartStopSyncButton_: function() {
857 return $('start-stop-sync');
861 * Event listener for the 'show home button' preference. Shows/hides the
862 * UI for changing the home page with animation, unless this is the first
863 * time this function is called, in which case there is no animation.
864 * @param {Event} event The preference change event.
866 onShowHomeButtonChanged_: function(event) {
867 var section = $('change-home-page-section');
868 if (this.onShowHomeButtonChangedCalled_) {
869 var container = $('change-home-page-section-container');
870 if (event.value.value)
871 this.showSectionWithAnimation_(section, container);
873 this.hideSectionWithAnimation_(section, container);
875 section.hidden = !event.value.value;
876 this.onShowHomeButtonChangedCalled_ = true;
881 * Event listener for the 'homepage is NTP' preference. Updates the label
882 * next to the 'Change' button.
883 * @param {Event} event The preference change event.
885 onHomePageIsNtpChanged_: function(event) {
886 if (!event.value.uncommitted) {
887 $('home-page-url').hidden = event.value.value;
888 $('home-page-ntp').hidden = !event.value.value;
893 * Event listener for changes to the homepage preference. Updates the label
894 * next to the 'Change' button.
895 * @param {Event} event The preference change event.
897 onHomePageChanged_: function(event) {
898 if (!event.value.uncommitted)
899 $('home-page-url').textContent = this.stripHttp_(event.value.value);
903 * Removes the 'http://' from a URL, like the omnibox does. If the string
904 * doesn't start with 'http://' it is returned unchanged.
905 * @param {string} url The url to be processed
906 * @return {string} The url with the 'http://' removed.
908 stripHttp_: function(url) {
909 return url.replace(/^http:\/\//, '');
913 * Shows the autoLaunch preference and initializes its checkbox value.
914 * @param {bool} enabled Whether autolaunch is enabled or or not.
917 updateAutoLaunchState_: function(enabled) {
918 $('auto-launch-option').hidden = false;
919 $('auto-launch').checked = enabled;
923 * Called when the value of the download.default_directory preference
925 * @param {Event} event Change event.
928 onDefaultDownloadDirectoryChanged_: function(event) {
929 $('downloadLocationPath').value = event.value.value;
931 // On ChromeOS, replace /special/drive/root with Drive for drive paths,
932 // /home/chronos/user/Downloads or /home/chronos/u-<hash>/Downloads
933 // with Downloads for local paths, and '/' with ' \u203a ' (angled quote
934 // sign) everywhere. The modified path is used only for display purpose.
935 var path = $('downloadLocationPath').value;
936 path = path.replace(/^\/special\/drive\/root/, 'Google Drive');
937 path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, '');
938 path = path.replace(/\//g, ' \u203a ');
939 $('downloadLocationPath').value = path;
941 $('download-location-label').classList.toggle('disabled',
942 event.value.disabled);
943 $('downloadLocationChangeButton').disabled = event.value.disabled;
947 * Update the Default Browsers section based on the current state.
948 * @param {string} statusString Description of the current default state.
949 * @param {boolean} isDefault Whether or not the browser is currently
951 * @param {boolean} canBeDefault Whether or not the browser can be default.
954 updateDefaultBrowserState_: function(statusString, isDefault,
956 if (!cr.isChromeOS) {
957 var label = $('default-browser-state');
958 label.textContent = statusString;
960 $('set-as-default-browser').hidden = !canBeDefault || isDefault;
965 * Clears the search engine popup.
968 clearSearchEngines_: function() {
969 $('default-search-engine').textContent = '';
973 * Updates the search engine popup with the given entries.
974 * @param {Array} engines List of available search engines.
975 * @param {number} defaultValue The value of the current default engine.
976 * @param {boolean} defaultManaged Whether the default search provider is
977 * managed. If true, the default search provider can't be changed.
980 updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
981 this.clearSearchEngines_();
982 engineSelect = $('default-search-engine');
983 engineSelect.disabled = defaultManaged;
984 if (defaultManaged && defaultValue == -1)
986 engineCount = engines.length;
987 var defaultIndex = -1;
988 for (var i = 0; i < engineCount; i++) {
989 var engine = engines[i];
990 var option = new Option(engine.name, engine.index);
991 if (defaultValue == option.value)
993 engineSelect.appendChild(option);
995 if (defaultIndex >= 0)
996 engineSelect.selectedIndex = defaultIndex;
1000 * Set the default search engine based on the popup selection.
1003 setDefaultSearchEngine_: function() {
1004 var engineSelect = $('default-search-engine');
1005 var selectedIndex = engineSelect.selectedIndex;
1006 if (selectedIndex >= 0) {
1007 var selection = engineSelect.options[selectedIndex];
1008 chrome.send('setDefaultSearchEngine', [String(selection.value)]);
1013 * Sets or clear whether Chrome should Auto-launch on computer startup.
1016 handleAutoLaunchChanged_: function() {
1017 chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
1021 * Get the selected profile item from the profile list. This also works
1022 * correctly if the list is not displayed.
1023 * @return {Object} the profile item object, or null if nothing is selected.
1026 getSelectedProfileItem_: function() {
1027 var profilesList = $('profiles-list');
1028 if (profilesList.hidden) {
1029 if (profilesList.dataModel.length > 0)
1030 return profilesList.dataModel.item(0);
1032 return profilesList.selectedItem;
1038 * Helper function to set the status of profile view buttons to disabled or
1039 * enabled, depending on the number of profiles and selection status of the
1043 setProfileViewButtonsStatus_: function() {
1044 var profilesList = $('profiles-list');
1045 var selectedProfile = profilesList.selectedItem;
1046 var hasSelection = selectedProfile != null;
1047 var hasSingleProfile = profilesList.dataModel.length == 1;
1048 var isManaged = loadTimeData.getBoolean('profileIsManaged');
1049 $('profiles-manage').disabled = !hasSelection ||
1050 !selectedProfile.isCurrentProfile;
1051 if (hasSelection && !selectedProfile.isCurrentProfile)
1052 $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1054 $('profiles-manage').title = '';
1055 $('profiles-delete').disabled = isManaged ||
1056 (!hasSelection && !hasSingleProfile);
1057 if (OptionsPage.isSettingsApp()) {
1058 $('profiles-app-list-switch').disabled = !hasSelection ||
1059 selectedProfile.isCurrentProfile;
1061 var importData = $('import-data');
1063 importData.disabled = $('import-data').disabled = hasSelection &&
1064 !selectedProfile.isCurrentProfile;
1069 * Display the correct dialog layout, depending on how many profiles are
1071 * @param {number} numProfiles The number of profiles to display.
1074 setProfileViewSingle_: function(numProfiles) {
1075 var hasSingleProfile = numProfiles == 1;
1076 $('profiles-list').hidden = hasSingleProfile;
1077 $('profiles-single-message').hidden = !hasSingleProfile;
1078 $('profiles-manage').hidden =
1079 hasSingleProfile || OptionsPage.isSettingsApp();
1080 $('profiles-delete').textContent = hasSingleProfile ?
1081 loadTimeData.getString('profilesDeleteSingle') :
1082 loadTimeData.getString('profilesDelete');
1083 if (OptionsPage.isSettingsApp())
1084 $('profiles-app-list-switch').hidden = hasSingleProfile;
1088 * Adds all |profiles| to the list.
1089 * @param {Array.<Object>} profiles An array of profile info objects.
1090 * each object is of the form:
1092 * name: "Profile Name",
1093 * iconURL: "chrome://path/to/icon/image",
1094 * filePath: "/path/to/profile/data/on/disk",
1095 * isCurrentProfile: false
1099 setProfilesInfo_: function(profiles) {
1100 this.setProfileViewSingle_(profiles.length);
1101 // add it to the list, even if the list is hidden so we can access it
1103 $('profiles-list').dataModel = new ArrayDataModel(profiles);
1105 // Received new data. If showing the "manage" overlay, keep it up to
1106 // date. If showing the "delete" overlay, close it.
1107 if (ManageProfileOverlay.getInstance().visible &&
1108 !$('manage-profile-overlay-manage').hidden) {
1109 ManageProfileOverlay.showManageDialog();
1111 ManageProfileOverlay.getInstance().visible = false;
1114 this.setProfileViewButtonsStatus_();
1118 * Reports managed user import errors to the ManagedUserImportOverlay.
1119 * @param {string} error The error message to display.
1122 showManagedUserImportError_: function(error) {
1123 ManagedUserImportOverlay.onError(error);
1127 * Reports successful importing of a managed user to
1128 * the ManagedUserImportOverlay.
1131 showManagedUserImportSuccess_: function() {
1132 ManagedUserImportOverlay.onSuccess();
1136 * Reports an error to the "create" overlay during profile creation.
1137 * @param {string} error The error message to display.
1140 showCreateProfileError_: function(error) {
1141 CreateProfileOverlay.onError(error);
1145 * Sends a warning message to the "create" overlay during profile creation.
1146 * @param {string} warning The warning message to display.
1149 showCreateProfileWarning_: function(warning) {
1150 CreateProfileOverlay.onWarning(warning);
1154 * Reports successful profile creation to the "create" overlay.
1155 * @param {Object} profileInfo An object of the form:
1157 * name: "Profile Name",
1158 * filePath: "/path/to/profile/data/on/disk"
1159 * isManaged: (true|false),
1163 showCreateProfileSuccess_: function(profileInfo) {
1164 CreateProfileOverlay.onSuccess(profileInfo);
1168 * Returns the currently active profile for this browser window.
1169 * @return {Object} A profile info object.
1172 getCurrentProfile_: function() {
1173 for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1174 var profile = $('profiles-list').dataModel.item(i);
1175 if (profile.isCurrentProfile)
1180 'There should always be a current profile, but none found.');
1183 setNativeThemeButtonEnabled_: function(enabled) {
1184 var button = $('themes-native-button');
1186 button.disabled = !enabled;
1189 setThemesResetButtonEnabled_: function(enabled) {
1190 $('themes-reset').disabled = !enabled;
1193 setAccountPictureManaged_: function(managed) {
1194 var picture = $('account-picture');
1195 if (managed || UIAccountTweaks.loggedInAsGuest()) {
1196 picture.disabled = true;
1197 ChangePictureOptions.closeOverlay();
1199 picture.disabled = false;
1202 // Create a synthetic pref change event decorated as
1203 // CoreOptionsHandler::CreateValueForPref() does.
1204 var event = new Event('account-picture');
1206 event.value = { controlledBy: 'policy' };
1209 $('account-picture-indicator').handlePrefChange(event);
1213 * (Re)loads IMG element with current user account picture.
1216 updateAccountPicture_: function() {
1217 var picture = $('account-picture');
1219 picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1225 * Handle the 'add device' button click.
1228 handleAddBluetoothDevice_: function() {
1229 chrome.send('findBluetoothDevices');
1230 OptionsPage.showPageByName('bluetooth', false);
1234 * Enables or disables the Manage SSL Certificates button.
1237 enableCertificateButton_: function(enabled) {
1238 $('certificatesManageButton').disabled = !enabled;
1242 * Enables factory reset section.
1245 enableFactoryResetSection_: function() {
1246 $('factory-reset-section').hidden = false;
1250 * Set the checked state of the metrics reporting checkbox.
1253 setMetricsReportingCheckboxState_: function(checked, disabled) {
1254 $('metricsReportingEnabled').checked = checked;
1255 $('metricsReportingEnabled').disabled = disabled;
1261 setMetricsReportingSettingVisibility_: function(visible) {
1263 $('metricsReportingSetting').style.display = 'block';
1265 $('metricsReportingSetting').style.display = 'none';
1269 * Set the visibility of the password generation checkbox.
1272 setPasswordGenerationSettingVisibility_: function(visible) {
1274 $('password-generation-checkbox').style.display = 'block';
1276 $('password-generation-checkbox').style.display = 'none';
1280 * Set the font size selected item. This item actually reflects two
1281 * preferences: the default font size and the default fixed font size.
1283 * @param {Object} pref Information about the font size preferences.
1284 * @param {number} pref.value The value of the default font size pref.
1285 * @param {boolean} pref.disabled True if either pref not user modifiable.
1286 * @param {string} pref.controlledBy The source of the pref value(s) if
1287 * either pref is currently not controlled by the user.
1290 setFontSize_: function(pref) {
1291 var selectCtl = $('defaultFontSize');
1292 selectCtl.disabled = pref.disabled;
1293 // Create a synthetic pref change event decorated as
1294 // CoreOptionsHandler::CreateValueForPref() does.
1295 var event = new Event('synthetic-font-size');
1298 controlledBy: pref.controlledBy,
1299 disabled: pref.disabled
1301 $('font-size-indicator').handlePrefChange(event);
1303 for (var i = 0; i < selectCtl.options.length; i++) {
1304 if (selectCtl.options[i].value == pref.value) {
1305 selectCtl.selectedIndex = i;
1307 selectCtl.remove($('Custom').index);
1312 // Add/Select Custom Option in the font size label list.
1314 var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1316 option.setAttribute('id', 'Custom');
1317 selectCtl.add(option);
1319 $('Custom').selected = true;
1323 * Populate the page zoom selector with values received from the caller.
1324 * @param {Array} items An array of items to populate the selector.
1325 * each object is an array with three elements as follows:
1326 * 0: The title of the item (string).
1327 * 1: The value of the item (number).
1328 * 2: Whether the item should be selected (boolean).
1331 setupPageZoomSelector_: function(items) {
1332 var element = $('defaultZoomFactor');
1334 // Remove any existing content.
1335 element.textContent = '';
1337 // Insert new child nodes into select element.
1338 var value, title, selected;
1339 for (var i = 0; i < items.length; i++) {
1340 title = items[i][0];
1341 value = items[i][1];
1342 selected = items[i][2];
1343 element.appendChild(new Option(title, value, false, selected));
1348 * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1350 * @param {boolean} display Whether to show the button and label or not.
1353 setAutoOpenFileTypesDisplayed_: function(display) {
1357 if ($('advanced-settings').hidden) {
1358 // If the Advanced section is hidden, don't animate the transition.
1359 $('auto-open-file-types-section').hidden = !display;
1362 this.showSectionWithAnimation_(
1363 $('auto-open-file-types-section'),
1364 $('auto-open-file-types-container'));
1366 this.hideSectionWithAnimation_(
1367 $('auto-open-file-types-section'),
1368 $('auto-open-file-types-container'));
1374 * Set the enabled state for the proxy settings button.
1377 setupProxySettingsSection_: function(disabled, extensionControlled) {
1378 if (!cr.isChromeOS) {
1379 $('proxiesConfigureButton').disabled = disabled;
1380 $('proxiesLabel').textContent =
1381 loadTimeData.getString(extensionControlled ?
1382 'proxiesLabelExtension' : 'proxiesLabelSystem');
1387 * Set the Cloud Print proxy UI to enabled, disabled, or processing.
1390 setupCloudPrintConnectorSection_: function(disabled, label, allowed) {
1391 if (!cr.isChromeOS) {
1392 $('cloudPrintConnectorLabel').textContent = label;
1393 if (disabled || !allowed) {
1394 $('cloudPrintConnectorSetupButton').textContent =
1395 loadTimeData.getString('cloudPrintConnectorDisabledButton');
1396 $('cloudPrintManageButton').style.display = 'none';
1398 $('cloudPrintConnectorSetupButton').textContent =
1399 loadTimeData.getString('cloudPrintConnectorEnabledButton');
1400 $('cloudPrintManageButton').style.display = 'inline';
1402 $('cloudPrintConnectorSetupButton').disabled = !allowed;
1409 removeCloudPrintConnectorSection_: function() {
1410 if (!cr.isChromeOS) {
1411 var connectorSectionElm = $('cloud-print-connector-section');
1412 if (connectorSectionElm)
1413 connectorSectionElm.parentNode.removeChild(connectorSectionElm);
1418 * Set the initial state of the spoken feedback checkbox.
1421 setSpokenFeedbackCheckboxState_: function(checked) {
1422 $('accessibility-spoken-feedback-check').checked = checked;
1426 * Set the initial state of the high contrast checkbox.
1429 setHighContrastCheckboxState_: function(checked) {
1430 $('accessibility-high-contrast-check').checked = checked;
1434 * Set the initial state of the virtual keyboard checkbox.
1437 setVirtualKeyboardCheckboxState_: function(checked) {
1438 // TODO(zork): Update UI
1442 * Show/hide mouse settings slider.
1445 showMouseControls_: function(show) {
1446 $('mouse-settings').hidden = !show;
1450 * Show/hide touchpad-related settings.
1453 showTouchpadControls_: function(show) {
1454 $('touchpad-settings').hidden = !show;
1455 $('accessibility-tap-dragging').hidden = !show;
1459 * Activate the Bluetooth settings section on the System settings page.
1462 showBluetoothSettings_: function() {
1463 $('bluetooth-devices').hidden = false;
1467 * Dectivates the Bluetooth settings section from the System settings page.
1470 hideBluetoothSettings_: function() {
1471 $('bluetooth-devices').hidden = true;
1475 * Sets the state of the checkbox indicating if Bluetooth is turned on. The
1476 * state of the "Find devices" button and the list of discovered devices may
1477 * also be affected by a change to the state.
1478 * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
1481 setBluetoothState_: function(checked) {
1482 $('enable-bluetooth').checked = checked;
1483 $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
1484 $('bluetooth-add-device').hidden = !checked;
1485 $('bluetooth-reconnect-device').hidden = !checked;
1486 // Flush list of previously discovered devices if bluetooth is turned off.
1488 $('bluetooth-paired-devices-list').clear();
1489 $('bluetooth-unpaired-devices-list').clear();
1491 chrome.send('getPairedBluetoothDevices');
1496 * Adds an element to the list of available Bluetooth devices. If an element
1497 * with a matching address is found, the existing element is updated.
1498 * @param {{name: string,
1501 * connected: boolean}} device
1502 * Decription of the Bluetooth device.
1505 addBluetoothDevice_: function(device) {
1506 var list = $('bluetooth-unpaired-devices-list');
1507 // Display the "connecting" (already paired or not yet paired) and the
1508 // paired devices in the same list.
1509 if (device.paired || device.connecting) {
1510 // Test to see if the device is currently in the unpaired list, in which
1511 // case it should be removed from that list.
1512 var index = $('bluetooth-unpaired-devices-list').find(device.address);
1513 if (index != undefined)
1514 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1515 list = $('bluetooth-paired-devices-list');
1517 // Test to see if the device is currently in the paired list, in which
1518 // case it should be removed from that list.
1519 var index = $('bluetooth-paired-devices-list').find(device.address);
1520 if (index != undefined)
1521 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1523 list.appendDevice(device);
1525 // One device can be in the process of pairing. If found, display
1526 // the Bluetooth pairing overlay.
1528 BluetoothPairing.showDialog(device);
1532 * Removes an element from the list of available devices.
1533 * @param {string} address Unique address of the device.
1536 removeBluetoothDevice_: function(address) {
1537 var index = $('bluetooth-unpaired-devices-list').find(address);
1538 if (index != undefined) {
1539 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1541 index = $('bluetooth-paired-devices-list').find(address);
1542 if (index != undefined)
1543 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1548 * Shows the overlay dialog for changing the user avatar image.
1551 showImagerPickerOverlay_: function() {
1552 OptionsPage.navigateToPage('changePicture');
1556 //Forward public APIs to private implementations.
1558 'addBluetoothDevice',
1559 'enableCertificateButton',
1560 'enableFactoryResetSection',
1561 'getCurrentProfile',
1562 'getStartStopSyncButton',
1563 'hideBluetoothSettings',
1564 'notifyInitializationComplete',
1565 'removeBluetoothDevice',
1566 'removeCloudPrintConnectorSection',
1568 'setAccountPictureManaged',
1569 'setAutoOpenFileTypesDisplayed',
1570 'setBluetoothState',
1572 'setNativeThemeButtonEnabled',
1573 'setHighContrastCheckboxState',
1574 'setMetricsReportingCheckboxState',
1575 'setMetricsReportingSettingVisibility',
1576 'setPasswordGenerationSettingVisibility',
1578 'setSpokenFeedbackCheckboxState',
1579 'setThemesResetButtonEnabled',
1580 'setVirtualKeyboardCheckboxState',
1581 'setupCloudPrintConnectorSection',
1582 'setupPageZoomSelector',
1583 'setupProxySettingsSection',
1584 'showBluetoothSettings',
1585 'showCreateProfileError',
1586 'showCreateProfileSuccess',
1587 'showCreateProfileWarning',
1588 'showManagedUserImportError',
1589 'showManagedUserImportSuccess',
1590 'showMouseControls',
1591 'showTouchpadControls',
1592 'updateAccountPicture',
1593 'updateAutoLaunchState',
1594 'updateDefaultBrowserState',
1595 'updateManagesSupervisedUsers',
1596 'updateSearchEngines',
1597 'updateStartupPages',
1599 ].forEach(function(name) {
1600 BrowserOptions[name] = function() {
1601 var instance = BrowserOptions.getInstance();
1602 return instance[name + '_'].apply(instance, arguments);
1606 if (cr.isChromeOS) {
1608 * Returns username (canonical email) of the user logged in (ChromeOS only).
1609 * @return {string} user email.
1611 // TODO(jhawkins): Investigate the use case for this method.
1612 BrowserOptions.getLoggedInUsername = function() {
1613 return BrowserOptions.getInstance().username_;
1619 BrowserOptions: BrowserOptions