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.exportPath('options');
8 * @typedef {{actionLinkText: (string|undefined),
9 * childUser: (boolean|undefined),
10 * hasError: (boolean|undefined),
11 * hasUnrecoverableError: (boolean|undefined),
12 * managed: (boolean|undefined),
13 * setupCompleted: (boolean|undefined),
14 * setupInProgress: (boolean|undefined),
15 * signedIn: (boolean|undefined),
16 * signinAllowed: (boolean|undefined),
17 * signoutAllowed: (boolean|undefined),
18 * statusText: (string|undefined),
19 * supervisedUser: (boolean|undefined),
20 * syncSystemEnabled: (boolean|undefined)}}
21 * @see chrome/browser/ui/webui/options/browser_options_handler.cc
26 * @typedef {{id: string, name: string}}
28 options.ExtensionData;
31 * @typedef {{name: string,
33 * isCurrentProfile: boolean,
34 * isSupervised: boolean,
37 * @see chrome/browser/ui/webui/options/browser_options_handler.cc
41 cr.define('options', function() {
42 var OptionsPage = options.OptionsPage;
43 var Page = cr.ui.pageManager.Page;
44 var PageManager = cr.ui.pageManager.PageManager;
45 var ArrayDataModel = cr.ui.ArrayDataModel;
46 var RepeatingButton = cr.ui.RepeatingButton;
47 var HotwordSearchSettingIndicator = options.HotwordSearchSettingIndicator;
48 var NetworkPredictionOptions = {
57 * Encapsulated handling of browser options page.
59 * @extends {cr.ui.pageManager.Page}
61 function BrowserOptions() {
62 Page.call(this, 'settings', loadTimeData.getString('settingsTitle'),
66 cr.addSingletonGetter(BrowserOptions);
69 * @param {HTMLElement} section The section to show or hide.
70 * @return {boolean} Whether the section should be shown.
73 BrowserOptions.shouldShowSection_ = function(section) {
74 // If the section is hidden or hiding, it should be shown.
75 return section.style.height == '' || section.style.height == '0px';
78 BrowserOptions.prototype = {
79 __proto__: Page.prototype,
82 * Keeps track of whether the user is signed in or not.
89 * Indicates whether signing out is allowed or whether a complete profile
90 * wipe is required to remove the current enterprise account.
94 signoutAllowed_: true,
97 * Keeps track of whether |onShowHomeButtonChanged_| has been called. See
98 * |onShowHomeButtonChanged_|.
102 onShowHomeButtonChangedCalled_: false,
105 * Track if page initialization is complete. All C++ UI handlers have the
106 * chance to manipulate page content within their InitializePage methods.
107 * This flag is set to true after all initializers have been called.
111 initializationComplete_: false,
114 initializePage: function() {
115 Page.prototype.initializePage.call(this);
118 if (window.top != window) {
119 // The options page is not in its own window.
120 document.body.classList.add('uber-frame');
121 PageManager.horizontalOffset = 155;
124 // Ensure that navigation events are unblocked on uber page. A reload of
125 // the settings page while an overlay is open would otherwise leave uber
126 // page in a blocked state, where tab switching is not possible.
127 uber.invokeMethodOnParent('stopInterceptingEvents');
129 window.addEventListener('message', this.handleWindowMessage_.bind(this));
131 if (loadTimeData.getBoolean('allowAdvancedSettings')) {
132 $('advanced-settings-expander').onclick = function(e) {
134 BrowserOptions.shouldShowSection_($('advanced-settings'));
136 chrome.send('coreOptionsUserMetricsAction',
137 ['Options_ShowAdvancedSettings']);
139 self.toggleSectionWithAnimation_(
140 $('advanced-settings'),
141 $('advanced-settings-container'));
143 // If the click was triggered using the keyboard and it showed the
144 // section (rather than hiding it), focus the first element in the
146 if (e.detail == 0 && showAdvanced) {
147 var focusElement = $('advanced-settings-container').querySelector(
148 'button, input, list, select, a[href]');
150 focusElement.focus();
154 $('advanced-settings-footer').hidden = true;
155 $('advanced-settings').hidden = true;
158 $('advanced-settings').addEventListener('webkitTransitionEnd',
159 this.updateAdvancedSettingsExpander_.bind(this));
161 if (loadTimeData.getBoolean('showAbout')) {
162 $('about-button').hidden = false;
163 $('about-button').addEventListener('click', function() {
164 PageManager.showPageByName('help');
165 chrome.send('coreOptionsUserMetricsAction',
171 UIAccountTweaks.applyGuestSessionVisibility(document);
172 UIAccountTweaks.applyPublicSessionVisibility(document);
173 if (loadTimeData.getBoolean('secondaryUser'))
174 $('secondary-user-banner').hidden = false;
177 // Sync (Sign in) section.
178 this.updateSyncState_(loadTimeData.getValue('syncData'));
180 $('start-stop-sync').onclick = function(event) {
181 if (self.signedIn_) {
182 if (self.signoutAllowed_)
183 SyncSetupOverlay.showStopSyncingUI();
185 chrome.send('showDisconnectManagedProfileDialog');
186 } else if (cr.isChromeOS) {
187 SyncSetupOverlay.showSetupUI();
189 SyncSetupOverlay.startSignIn();
192 $('customize-sync').onclick = function(event) {
193 SyncSetupOverlay.showSetupUI();
196 // Internet connection section (ChromeOS only).
198 options.network.NetworkList.decorate($('network-list'));
199 // Show that the network settings are shared if this is a secondary user
200 // in a multi-profile session.
201 if (loadTimeData.getBoolean('secondaryUser')) {
202 var networkIndicator = document.querySelector(
203 '#network-section-header > .controlled-setting-indicator');
204 networkIndicator.setAttribute('controlled-by', 'shared');
205 networkIndicator.location = cr.ui.ArrowLocation.TOP_START;
207 options.network.NetworkList.refreshNetworkData(
208 loadTimeData.getValue('networkData'));
211 // On Startup section.
212 Preferences.getInstance().addEventListener('session.restore_on_startup',
213 this.onRestoreOnStartupChanged_.bind(this));
214 Preferences.getInstance().addEventListener(
215 'session.startup_urls',
217 $('startup-set-pages').disabled = event.value.disabled;
220 $('startup-set-pages').onclick = function() {
221 PageManager.showPageByName('startup');
224 // Appearance section.
225 Preferences.getInstance().addEventListener('browser.show_home_button',
226 this.onShowHomeButtonChanged_.bind(this));
228 Preferences.getInstance().addEventListener('homepage',
229 this.onHomePageChanged_.bind(this));
230 Preferences.getInstance().addEventListener('homepage_is_newtabpage',
231 this.onHomePageIsNtpChanged_.bind(this));
233 $('change-home-page').onclick = function(event) {
234 PageManager.showPageByName('homePageOverlay');
235 chrome.send('coreOptionsUserMetricsAction',
236 ['Options_Homepage_ShowSettings']);
239 HotwordSearchSettingIndicator.decorate(
240 $('hotword-search-setting-indicator'));
241 HotwordSearchSettingIndicator.decorate(
242 $('hotword-no-dsp-search-setting-indicator'));
243 var hotwordIndicator = $('hotword-always-on-search-setting-indicator');
244 HotwordSearchSettingIndicator.decorate(hotwordIndicator);
245 hotwordIndicator.disabledOnErrorSection =
246 $('hotword-always-on-search-checkbox');
247 chrome.send('requestHotwordAvailable');
249 if ($('set-wallpaper')) {
250 $('set-wallpaper').onclick = function(event) {
251 chrome.send('openWallpaperManager');
252 chrome.send('coreOptionsUserMetricsAction',
253 ['Options_OpenWallpaperManager']);
257 // Control the hotword-always-on pref with the Hotword Audio
259 $('hotword-always-on-search-checkbox').customChangeHandler =
261 if (!$('hotword-always-on-search-checkbox').checked)
264 $('hotword-always-on-search-checkbox').checked = false;
265 chrome.send('launchHotwordAudioVerificationApp', [false]);
269 // Open the Hotword Audio Verification app to retrain a voice model.
270 $('hotword-retrain-link').onclick = function(event) {
271 chrome.send('launchHotwordAudioVerificationApp', [true]);
273 Preferences.getInstance().addEventListener(
274 'hotword.always_on_search_enabled',
275 this.onHotwordAlwaysOnChanged_.bind(this));
277 $('themes-gallery').onclick = function(event) {
278 window.open(loadTimeData.getString('themesGalleryURL'));
279 chrome.send('coreOptionsUserMetricsAction',
280 ['Options_ThemesGallery']);
282 $('themes-reset').onclick = function(event) {
283 chrome.send('themesReset');
286 if (loadTimeData.getBoolean('profileIsSupervised')) {
287 if ($('themes-native-button')) {
288 $('themes-native-button').disabled = true;
289 $('themes-native-button').hidden = true;
291 // Supervised users have just one default theme, even on Linux. So use
292 // the same button for Linux as for the other platforms.
293 $('themes-reset').textContent = loadTimeData.getString('themesReset');
296 // Device section (ChromeOS only).
298 $('power-settings-button').onclick = function(evt) {
299 PageManager.showPageByName('power-overlay');
300 chrome.send('coreOptionsUserMetricsAction',
301 ['Options_ShowPowerSettings']);
303 $('battery-button').onclick = function(evt) {
304 WebsiteSettingsManager.showWebsiteSettings('battery');
306 $('stored-data-button').onclick = function(evt) {
307 WebsiteSettingsManager.showWebsiteSettings('storage');
309 $('keyboard-settings-button').onclick = function(evt) {
310 PageManager.showPageByName('keyboard-overlay');
311 chrome.send('coreOptionsUserMetricsAction',
312 ['Options_ShowKeyboardSettings']);
314 $('pointer-settings-button').onclick = function(evt) {
315 PageManager.showPageByName('pointer-overlay');
316 chrome.send('coreOptionsUserMetricsAction',
317 ['Options_ShowTouchpadSettings']);
322 $('manage-default-search-engines').onclick = function(event) {
323 PageManager.showPageByName('searchEngines');
324 chrome.send('coreOptionsUserMetricsAction',
325 ['Options_ManageSearchEngines']);
327 $('default-search-engine').addEventListener('change',
328 this.setDefaultSearchEngine_);
331 if (loadTimeData.valueExists('profilesInfo')) {
332 $('profiles-section').hidden = false;
333 this.maybeShowUserSection_();
335 var profilesList = $('profiles-list');
336 options.browser_options.ProfileList.decorate(profilesList);
337 profilesList.autoExpands = true;
339 // The profiles info data in |loadTimeData| might be stale.
340 this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
341 chrome.send('requestProfilesInfo');
343 profilesList.addEventListener('change',
344 this.setProfileViewButtonsStatus_);
345 $('profiles-create').onclick = function(event) {
346 ManageProfileOverlay.showCreateDialog();
348 if (OptionsPage.isSettingsApp()) {
349 $('profiles-app-list-switch').onclick = function(event) {
350 var selectedProfile = self.getSelectedProfileItem_();
351 chrome.send('switchAppListProfile', [selectedProfile.filePath]);
354 $('profiles-manage').onclick = function(event) {
355 ManageProfileOverlay.showManageDialog();
357 $('profiles-delete').onclick = function(event) {
358 var selectedProfile = self.getSelectedProfileItem_();
360 ManageProfileOverlay.showDeleteDialog(selectedProfile);
362 if (loadTimeData.getBoolean('profileIsSupervised')) {
363 $('profiles-create').disabled = true;
365 if (!loadTimeData.getBoolean('allowProfileDeletion')) {
366 $('profiles-delete').disabled = true;
367 $('profiles-list').canDeleteItems = false;
372 // Username (canonical email) of the currently logged in user or
373 // |kGuestUser| if a guest session is active.
374 this.username_ = loadTimeData.getString('username');
376 this.updateAccountPicture_();
378 $('account-picture').onclick = this.showImagerPickerOverlay_;
379 $('change-picture-caption').onclick = this.showImagerPickerOverlay_;
381 $('manage-accounts-button').onclick = function(event) {
382 PageManager.showPageByName('accounts');
383 chrome.send('coreOptionsUserMetricsAction',
384 ['Options_ManageAccounts']);
387 $('import-data').onclick = function(event) {
388 ImportDataOverlay.show();
389 chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
392 if ($('themes-native-button')) {
393 $('themes-native-button').onclick = function(event) {
394 chrome.send('themesSetNative');
399 // Date and time section (CrOS only).
401 if ($('set-time-button'))
402 $('set-time-button').onclick = this.handleSetTime_.bind(this);
405 if (loadTimeData.getBoolean('enableTimeZoneTrackingOption')) {
406 $('resolve-timezone-by-geolocation-selection').hidden = false;
407 this.setSystemTimezoneManaged_(false);
411 // Default browser section.
412 if (!cr.isChromeOS) {
413 if (!loadTimeData.getBoolean('showSetDefault')) {
414 $('set-default-browser-section').hidden = true;
416 $('set-as-default-browser').onclick = function(event) {
417 chrome.send('becomeDefaultBrowser');
420 $('auto-launch').onclick = this.handleAutoLaunchChanged_;
424 $('privacyContentSettingsButton').onclick = function(event) {
425 PageManager.showPageByName('content');
426 OptionsPage.showTab($('cookies-nav-tab'));
427 chrome.send('coreOptionsUserMetricsAction',
428 ['Options_ContentSettings']);
430 $('privacyClearDataButton').onclick = function(event) {
431 PageManager.showPageByName('clearBrowserData');
432 chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
434 $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
435 // 'metricsReportingEnabled' element is only present on Chrome branded
436 // builds, and the 'metricsReportingCheckboxAction' message is only
437 // handled on ChromeOS.
438 if ($('metricsReportingEnabled') && cr.isChromeOS) {
439 $('metricsReportingEnabled').onclick = function(event) {
440 chrome.send('metricsReportingCheckboxAction',
441 [String(event.currentTarget.checked)]);
444 if ($('metricsReportingEnabled') && !cr.isChromeOS) {
445 // The localized string has the | symbol on each side of the text that
446 // needs to be made into a button to restart Chrome. We parse the text
447 // and build the button from that.
448 var restartTextFragments =
449 loadTimeData.getString('metricsReportingResetRestart').split('|');
450 // Assume structure is something like "starting text |link text| ending
451 // text" where both starting text and ending text may or may not be
452 // present, but the split should always be in three pieces.
453 var restartElements =
454 $('metrics-reporting-reset-restart').querySelectorAll('*');
455 for (var i = 0; i < restartTextFragments.length; i++) {
456 restartElements[i].textContent = restartTextFragments[i];
458 restartElements[1].onclick = function(event) {
459 chrome.send('restartBrowser');
461 // Attach the listener for updating the checkbox and restart button.
462 var updateMetricsRestartButton = function() {
463 $('metrics-reporting-reset-restart').hidden =
464 loadTimeData.getBoolean('metricsReportingEnabledAtStart') ==
465 $('metricsReportingEnabled').checked;
467 $('metricsReportingEnabled').onclick = function(event) {
468 chrome.send('metricsReportingCheckboxChanged',
469 [Boolean(event.currentTarget.checked)]);
470 updateMetricsRestartButton();
472 $('metricsReportingEnabled').checked =
473 loadTimeData.getBoolean('metricsReportingEnabledAtStart');
474 updateMetricsRestartButton();
476 $('networkPredictionOptions').onchange = function(event) {
477 var value = (event.target.checked ?
478 NetworkPredictionOptions.WIFI_ONLY :
479 NetworkPredictionOptions.NEVER);
480 var metric = event.target.metric;
481 Preferences.setIntegerPref(
482 'net.network_prediction_options',
487 if (loadTimeData.valueExists('showWakeOnWifi') &&
488 loadTimeData.getBoolean('showWakeOnWifi')) {
489 $('wake-on-wifi').hidden = false;
492 // Bluetooth (CrOS only).
494 options.system.bluetooth.BluetoothDeviceList.decorate(
495 $('bluetooth-paired-devices-list'));
497 $('bluetooth-add-device').onclick =
498 this.handleAddBluetoothDevice_.bind(this);
500 $('enable-bluetooth').onchange = function(event) {
501 var state = $('enable-bluetooth').checked;
502 chrome.send('bluetoothEnableChange', [Boolean(state)]);
505 $('bluetooth-reconnect-device').onclick = function(event) {
506 chrome.send('coreOptionsUserMetricsAction',
507 ['Options_BluetoothConnectPairedDevice']);
508 var device = $('bluetooth-paired-devices-list').selectedItem;
509 var address = device.address;
510 chrome.send('updateBluetoothDevice', [address, 'connect']);
511 PageManager.closeOverlay();
514 $('bluetooth-paired-devices-list').addEventListener('change',
516 var item = $('bluetooth-paired-devices-list').selectedItem;
517 var disabled = !item || item.connected || !item.connectable;
518 $('bluetooth-reconnect-device').disabled = disabled;
522 // Passwords and Forms section.
523 $('autofill-settings').onclick = function(event) {
524 PageManager.showPageByName('autofill');
525 chrome.send('coreOptionsUserMetricsAction',
526 ['Options_ShowAutofillSettings']);
528 $('manage-passwords').onclick = function(event) {
529 PageManager.showPageByName('passwords');
530 OptionsPage.showTab($('passwords-nav-tab'));
531 chrome.send('coreOptionsUserMetricsAction',
532 ['Options_ShowPasswordManager']);
534 if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
535 // Disable and turn off Autofill in guest mode.
536 var autofillEnabled = $('autofill-enabled');
537 autofillEnabled.disabled = true;
538 autofillEnabled.checked = false;
539 cr.dispatchSimpleEvent(autofillEnabled, 'change');
540 $('autofill-settings').disabled = true;
542 // Disable and turn off Password Manager in guest mode.
543 var passwordManagerEnabled = $('password-manager-enabled');
544 passwordManagerEnabled.disabled = true;
545 passwordManagerEnabled.checked = false;
546 cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
547 $('manage-passwords').disabled = true;
551 $('mac-passwords-warning').hidden =
552 !loadTimeData.getBoolean('multiple_profiles');
556 if (!cr.isChromeOS) {
557 $('proxiesConfigureButton').onclick = function(event) {
558 chrome.send('showNetworkProxySettings');
562 // Device control section.
564 UIAccountTweaks.currentUserIsOwner() &&
565 loadTimeData.getBoolean('consumerManagementEnabled')) {
566 $('device-control-section').hidden = false;
567 $('consumer-management-button').onclick = function(event) {
568 PageManager.showPageByName('consumer-management-overlay');
572 // Easy Unlock section.
573 if (loadTimeData.getBoolean('easyUnlockAllowed')) {
574 $('easy-unlock-section').hidden = false;
575 $('easy-unlock-setup-button').onclick = function(event) {
576 chrome.send('launchEasyUnlockSetup');
578 $('easy-unlock-turn-off-button').onclick = function(event) {
579 PageManager.showPageByName('easyUnlockTurnOffOverlay');
582 $('easy-unlock-enable-proximity-detection').hidden =
583 !loadTimeData.getBoolean('easyUnlockProximityDetectionAllowed');
585 // Website Settings section.
586 if (loadTimeData.getBoolean('websiteSettingsManagerEnabled')) {
587 $('website-settings-section').hidden = false;
588 $('website-management-button').onclick = function(event) {
589 PageManager.showPageByName('websiteSettings');
593 // Web Content section.
594 $('fontSettingsCustomizeFontsButton').onclick = function(event) {
595 PageManager.showPageByName('fonts');
596 chrome.send('coreOptionsUserMetricsAction',
597 ['Options_ShowFontSettings']);
599 $('defaultFontSize').onchange = function(event) {
600 var value = event.target.options[event.target.selectedIndex].value;
601 Preferences.setIntegerPref(
602 'webkit.webprefs.default_fixed_font_size',
603 value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
604 chrome.send('defaultFontSizeAction', [String(value)]);
606 $('defaultZoomFactor').onchange = function(event) {
607 chrome.send('defaultZoomFactorAction',
608 [String(event.target.options[event.target.selectedIndex].value)]);
611 // Languages section.
612 var showLanguageOptions = function(event) {
613 PageManager.showPageByName('languages');
614 chrome.send('coreOptionsUserMetricsAction',
615 ['Options_LanuageAndSpellCheckSettings']);
617 $('language-button').onclick = showLanguageOptions;
618 $('manage-languages').onclick = showLanguageOptions;
620 // Downloads section.
621 Preferences.getInstance().addEventListener('download.default_directory',
622 this.onDefaultDownloadDirectoryChanged_.bind(this));
623 $('downloadLocationChangeButton').onclick = function(event) {
624 chrome.send('selectDownloadLocation');
627 $('disable-drive-row').hidden =
628 UIAccountTweaks.loggedInAsSupervisedUser();
630 $('autoOpenFileTypesResetToDefault').onclick = function(event) {
631 chrome.send('autoOpenFileTypesAction');
634 // HTTPS/SSL section.
635 if (cr.isWindows || cr.isMac) {
636 $('certificatesManageButton').onclick = function(event) {
637 chrome.send('showManageSSLCertificates');
640 $('certificatesManageButton').onclick = function(event) {
641 PageManager.showPageByName('certificates');
642 chrome.send('coreOptionsUserMetricsAction',
643 ['Options_ManageSSLCertificates']);
647 if (loadTimeData.getBoolean('cloudPrintShowMDnsOptions')) {
648 $('cloudprint-options-mdns').hidden = false;
649 $('cloudPrintDevicesPageButton').onclick = function() {
650 chrome.send('showCloudPrintDevicesPage');
654 // Accessibility section (CrOS only).
656 var updateAccessibilitySettingsButton = function() {
657 $('accessibility-settings').hidden =
658 !($('accessibility-spoken-feedback-check').checked);
660 Preferences.getInstance().addEventListener(
661 'settings.accessibility',
662 updateAccessibilitySettingsButton);
663 $('accessibility-learn-more').onclick = function(unused_event) {
664 window.open(loadTimeData.getString('accessibilityLearnMoreURL'));
665 chrome.send('coreOptionsUserMetricsAction',
666 ['Options_AccessibilityLearnMore']);
668 $('accessibility-settings-button').onclick = function(unused_event) {
669 window.open(loadTimeData.getString('accessibilitySettingsURL'));
671 $('accessibility-spoken-feedback-check').onchange =
672 updateAccessibilitySettingsButton;
673 updateAccessibilitySettingsButton();
675 var updateDelayDropdown = function() {
676 $('accessibility-autoclick-dropdown').disabled =
677 !$('accessibility-autoclick-check').checked;
679 Preferences.getInstance().addEventListener(
680 $('accessibility-autoclick-check').getAttribute('pref'),
681 updateDelayDropdown);
684 // Display management section (CrOS only).
686 $('display-options').onclick = function(event) {
687 PageManager.showPageByName('display');
688 chrome.send('coreOptionsUserMetricsAction',
689 ['Options_Display']);
693 // Factory reset section (CrOS only).
695 $('factory-reset-restart').onclick = function(event) {
696 PageManager.showPageByName('factoryResetData');
697 chrome.send('onPowerwashDialogShow');
702 if (!cr.isChromeOS) {
703 var updateGpuRestartButton = function() {
704 $('gpu-mode-reset-restart').hidden =
705 loadTimeData.getBoolean('gpuEnabledAtStart') ==
706 $('gpu-mode-checkbox').checked;
708 Preferences.getInstance().addEventListener(
709 $('gpu-mode-checkbox').getAttribute('pref'),
710 updateGpuRestartButton);
711 $('gpu-mode-reset-restart-button').onclick = function(event) {
712 chrome.send('restartBrowser');
714 updateGpuRestartButton();
717 // Reset profile settings section.
718 $('reset-profile-settings').onclick = function(event) {
719 PageManager.showPageByName('resetProfileSettings');
722 // Extension controlled UI.
723 this.addExtensionControlledBox_('search-section-content',
724 'search-engine-controlled',
726 this.addExtensionControlledBox_('extension-controlled-container',
727 'homepage-controlled',
729 this.addExtensionControlledBox_('startup-section-content',
730 'startpage-controlled',
732 this.addExtensionControlledBox_('newtab-section-content',
735 this.addExtensionControlledBox_('proxy-section-content',
739 document.body.addEventListener('click', function(e) {
740 var target = assertInstanceof(e.target, Node);
741 var button = findAncestor(target, function(el) {
742 return el.tagName == 'BUTTON' &&
743 el.dataset.extensionId !== undefined &&
744 el.dataset.extensionId.length;
747 chrome.send('disableExtension', [button.dataset.extensionId]);
752 didShowPage: function() {
753 $('search-field').focus();
757 * Called after all C++ UI handlers have called InitializePage to notify
758 * that initialization is complete.
761 notifyInitializationComplete_: function() {
762 this.initializationComplete_ = true;
763 cr.dispatchSimpleEvent(document, 'initializationComplete');
767 * Event listener for the 'session.restore_on_startup' pref.
768 * @param {Event} event The preference change event.
771 onRestoreOnStartupChanged_: function(event) {
772 /** @const */ var showHomePageValue = 0;
774 if (event.value.value == showHomePageValue) {
775 // If the user previously selected "Show the homepage", the
776 // preference will already be migrated to "Open a specific page". So
777 // the only way to reach this code is if the 'restore on startup'
778 // preference is managed.
779 assert(event.value.controlledBy);
781 // Select "open the following pages" and lock down the list of URLs
782 // to reflect the intention of the policy.
783 $('startup-show-pages').checked = true;
784 StartupOverlay.getInstance().setControlsDisabled(true);
786 // Re-enable the controls in the startup overlay if necessary.
787 StartupOverlay.getInstance().updateControlStates();
792 * Handler for messages sent from the main uber page.
793 * @param {Event} e The 'message' event from the uber page.
796 handleWindowMessage_: function(e) {
797 if ((/** @type {{method: string}} */(e.data)).method == 'frameSelected')
798 $('search-field').focus();
802 * Animatedly changes height |from| a px number |to| a px number.
803 * @param {HTMLElement} section The section to animate.
804 * @param {HTMLElement} container The container of |section|.
805 * @param {boolean} showing Whether to go from 0 -> container height or
806 * container height -> 0.
809 animatedSectionHeightChange_: function(section, container, showing) {
810 // If the section is already animating, dispatch a synthetic transition
811 // end event as the upcoming code will cancel the current one.
812 if (section.classList.contains('sliding'))
813 cr.dispatchSimpleEvent(section, 'webkitTransitionEnd');
815 this.addTransitionEndListener_(section);
817 section.hidden = false;
818 section.style.height = (showing ? 0 : container.offsetHeight) + 'px';
819 section.classList.add('sliding');
821 // Force a style recalc before starting the animation.
822 /** @suppress {suspiciousCode} */
823 section.offsetHeight;
825 section.style.height = (showing ? container.offsetHeight : 0) + 'px';
829 * Shows the given section.
830 * @param {HTMLElement} section The section to be shown.
831 * @param {HTMLElement} container The container for the section. Must be
832 * inside of |section|.
833 * @param {boolean} animate Indicate if the expansion should be animated.
836 showSection_: function(section, container, animate) {
837 // Delay starting the transition if animating so that hidden change will
840 this.animatedSectionHeightChange_(section, container, true);
842 section.hidden = false;
843 section.style.height = 'auto';
848 * Shows the given section, with animation.
849 * @param {HTMLElement} section The section to be shown.
850 * @param {HTMLElement} container The container for the section. Must be
851 * inside of |section|.
854 showSectionWithAnimation_: function(section, container) {
855 this.showSection_(section, container, /* animate */ true);
859 * Hides the given |section| with animation.
860 * @param {HTMLElement} section The section to be hidden.
861 * @param {HTMLElement} container The container for the section. Must be
862 * inside of |section|.
865 hideSectionWithAnimation_: function(section, container) {
866 this.animatedSectionHeightChange_(section, container, false);
870 * Toggles the visibility of |section| in an animated way.
871 * @param {HTMLElement} section The section to be toggled.
872 * @param {HTMLElement} container The container for the section. Must be
873 * inside of |section|.
876 toggleSectionWithAnimation_: function(section, container) {
877 if (BrowserOptions.shouldShowSection_(section))
878 this.showSectionWithAnimation_(section, container);
880 this.hideSectionWithAnimation_(section, container);
884 * Scrolls the settings page to make the section visible auto-expanding
885 * advanced settings if required. The transition is not animated. This
886 * method is used to ensure that a section associated with an overlay
887 * is visible when the overlay is closed.
888 * @param {!Element} section The section to make visible.
891 scrollToSection_: function(section) {
892 var advancedSettings = $('advanced-settings');
893 var container = $('advanced-settings-container');
894 var expander = $('advanced-settings-expander');
895 if (!expander.hidden &&
896 advancedSettings.hidden &&
897 section.parentNode == container) {
898 this.showSection_($('advanced-settings'),
899 $('advanced-settings-container'),
900 /* animate */ false);
901 this.updateAdvancedSettingsExpander_();
904 if (!this.initializationComplete_) {
906 var callback = function() {
907 document.removeEventListener('initializationComplete', callback);
908 self.scrollToSection_(section);
910 document.addEventListener('initializationComplete', callback);
914 var pageContainer = $('page-container');
915 // pageContainer.offsetTop is relative to the screen.
916 var pageTop = pageContainer.offsetTop;
917 var sectionBottom = section.offsetTop + section.offsetHeight;
918 // section.offsetTop is relative to the 'page-container'.
919 var sectionTop = section.offsetTop;
920 if (pageTop + sectionBottom > document.body.scrollHeight ||
921 pageTop + sectionTop < 0) {
922 // Currently not all layout updates are guaranteed to precede the
923 // initializationComplete event (for example 'set-as-default-browser'
924 // button) leaving some uncertainty in the optimal scroll position.
925 // The section is placed approximately in the middle of the screen.
926 var top = Math.min(0, document.body.scrollHeight / 2 - sectionBottom);
927 pageContainer.style.top = top + 'px';
928 pageContainer.oldScrollTop = -top;
933 * Adds a |webkitTransitionEnd| listener to the given section so that
934 * it can be animated. The listener will only be added to a given section
935 * once, so this can be called as multiple times.
936 * @param {HTMLElement} section The section to be animated.
939 addTransitionEndListener_: function(section) {
940 if (section.hasTransitionEndListener_)
943 section.addEventListener('webkitTransitionEnd',
944 this.onTransitionEnd_.bind(this));
945 section.hasTransitionEndListener_ = true;
949 * Called after an animation transition has ended.
950 * @param {Event} event The webkitTransitionEnd event. NOTE: May be
954 onTransitionEnd_: function(event) {
955 if (event.propertyName && event.propertyName != 'height') {
956 // If not a synthetic event or a real transition we care about, bail.
960 var section = event.target;
961 section.classList.remove('sliding');
963 if (!event.propertyName) {
964 // Only real transitions past this point.
968 if (section.style.height == '0px') {
969 // Hide the content so it can't get tab focus.
970 section.hidden = true;
971 section.style.height = '';
973 // Set the section height to 'auto' to allow for size changes
974 // (due to font change or dynamic content).
975 section.style.height = 'auto';
980 updateAdvancedSettingsExpander_: function() {
981 var expander = $('advanced-settings-expander');
982 if (BrowserOptions.shouldShowSection_($('advanced-settings')))
983 expander.textContent = loadTimeData.getString('showAdvancedSettings');
985 expander.textContent = loadTimeData.getString('hideAdvancedSettings');
989 * Updates the sync section with the given state.
990 * @param {options.SyncStatus} syncData A bunch of data records that
991 * describe the status of the sync system.
994 updateSyncState_: function(syncData) {
995 if (!syncData.signinAllowed &&
996 (!syncData.supervisedUser || !cr.isChromeOS)) {
997 $('sync-section').hidden = true;
998 this.maybeShowUserSection_();
1002 $('sync-section').hidden = false;
1003 this.maybeShowUserSection_();
1005 if (cr.isChromeOS && syncData.supervisedUser && !syncData.childUser) {
1006 var subSection = $('sync-section').firstChild;
1007 while (subSection) {
1008 if (subSection.nodeType == Node.ELEMENT_NODE)
1009 subSection.hidden = true;
1010 subSection = subSection.nextSibling;
1013 $('account-picture-wrapper').hidden = false;
1014 $('sync-general').hidden = false;
1015 $('sync-status').hidden = true;
1020 // If the user gets signed out while the advanced sync settings dialog is
1021 // visible, say, due to a dashboard clear, close the dialog.
1022 // However, if the user gets signed out as a result of abandoning first
1023 // time sync setup, do not call closeOverlay as it will redirect the
1024 // browser to the main settings page and override any in-progress
1025 // user-initiated navigation. See crbug.com/278030.
1026 // Note: SyncSetupOverlay.closeOverlay is a no-op if the overlay is
1028 if (this.signedIn_ && !syncData.signedIn && !syncData.setupInProgress)
1029 SyncSetupOverlay.closeOverlay();
1031 this.signedIn_ = !!syncData.signedIn;
1033 // Display the "advanced settings" button if we're signed in and sync is
1034 // not managed/disabled. If the user is signed in, but sync is disabled,
1035 // this button is used to re-enable sync.
1036 var customizeSyncButton = $('customize-sync');
1037 customizeSyncButton.hidden = !this.signedIn_ ||
1039 !syncData.syncSystemEnabled;
1041 // Only modify the customize button's text if the new text is different.
1042 // Otherwise, it can affect search-highlighting in the settings page.
1043 // See http://crbug.com/268265.
1044 var customizeSyncButtonNewText = syncData.setupCompleted ?
1045 loadTimeData.getString('customizeSync') :
1046 loadTimeData.getString('syncButtonTextStart');
1047 if (customizeSyncButton.textContent != customizeSyncButtonNewText)
1048 customizeSyncButton.textContent = customizeSyncButtonNewText;
1050 // Disable the "sign in" button if we're currently signing in, or if we're
1051 // already signed in and signout is not allowed.
1052 var signInButton = $('start-stop-sync');
1053 signInButton.disabled = syncData.setupInProgress;
1054 this.signoutAllowed_ = !!syncData.signoutAllowed;
1055 if (!syncData.signoutAllowed)
1056 $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
1058 $('start-stop-sync-indicator').removeAttribute('controlled-by');
1060 // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome
1061 // (except for supervised users, which can't change their signed-in
1063 signInButton.hidden = cr.isChromeOS || syncData.supervisedUser;
1065 signInButton.textContent =
1067 loadTimeData.getString('syncButtonTextStop') :
1068 syncData.setupInProgress ?
1069 loadTimeData.getString('syncButtonTextInProgress') :
1070 loadTimeData.getString('syncButtonTextSignIn');
1071 $('start-stop-sync-indicator').hidden = signInButton.hidden;
1073 // TODO(estade): can this just be textContent?
1074 $('sync-status-text').innerHTML = syncData.statusText;
1075 var statusSet = syncData.statusText.length != 0;
1076 $('sync-overview').hidden =
1078 (cr.isChromeOS && UIAccountTweaks.loggedInAsPublicAccount());
1079 $('sync-status').hidden = !statusSet;
1081 $('sync-action-link').textContent = syncData.actionLinkText;
1082 // Don't show the action link if it is empty or undefined.
1083 $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
1084 $('sync-action-link').disabled = syncData.managed ||
1085 !syncData.syncSystemEnabled;
1087 // On Chrome OS, sign out the user and sign in again to get fresh
1088 // credentials on auth errors.
1089 $('sync-action-link').onclick = function(event) {
1090 if (cr.isChromeOS && syncData.hasError)
1091 SyncSetupOverlay.doSignOutOnAuthError();
1093 SyncSetupOverlay.showSetupUI();
1096 if (syncData.hasError)
1097 $('sync-status').classList.add('sync-error');
1099 $('sync-status').classList.remove('sync-error');
1101 // Disable the "customize / set up sync" button if sync has an
1102 // unrecoverable error. Also disable the button if sync has not been set
1103 // up and the user is being presented with a link to re-auth.
1104 // See crbug.com/289791.
1105 customizeSyncButton.disabled =
1106 syncData.hasUnrecoverableError ||
1107 (!syncData.setupCompleted && !$('sync-action-link').hidden);
1111 * Update the UI depending on whether Easy Unlock is enabled for the current
1113 * @param {boolean} isEnabled True if the feature is enabled for the current
1116 updateEasyUnlock_: function(isEnabled) {
1117 $('easy-unlock-disabled').hidden = isEnabled;
1118 $('easy-unlock-enabled').hidden = !isEnabled;
1119 if (!isEnabled && EasyUnlockTurnOffOverlay.getInstance().visible) {
1120 EasyUnlockTurnOffOverlay.dismiss();
1125 * Update the UI depending on whether the current profile manages any
1127 * @param {boolean} show True if the current profile manages any supervised
1130 updateManagesSupervisedUsers_: function(show) {
1131 $('profiles-supervised-dashboard-tip').hidden = !show;
1132 this.maybeShowUserSection_();
1136 * Get the start/stop sync button DOM element. Used for testing.
1137 * @return {Element} The start/stop sync button.
1140 getStartStopSyncButton_: function() {
1141 return $('start-stop-sync');
1145 * Event listener for the 'show home button' preference. Shows/hides the
1146 * UI for changing the home page with animation, unless this is the first
1147 * time this function is called, in which case there is no animation.
1148 * @param {Event} event The preference change event.
1150 onShowHomeButtonChanged_: function(event) {
1151 var section = $('change-home-page-section');
1152 if (this.onShowHomeButtonChangedCalled_) {
1153 var container = $('change-home-page-section-container');
1154 if (event.value.value)
1155 this.showSectionWithAnimation_(section, container);
1157 this.hideSectionWithAnimation_(section, container);
1159 section.hidden = !event.value.value;
1160 this.onShowHomeButtonChangedCalled_ = true;
1165 * Activates the Hotword section from the System settings page.
1166 * @param {string} sectionId The id of the section to display.
1167 * @param {string} indicatorId The id of the indicator to display.
1168 * @param {string=} opt_error The error message to display.
1171 showHotwordCheckboxAndIndicator_: function(sectionId, indicatorId,
1173 $(sectionId).hidden = false;
1174 $(indicatorId).setError(opt_error);
1176 $(indicatorId).updateBasedOnError();
1180 * Activates the Hotword section from the System settings page.
1181 * @param {string=} opt_error The error message to display.
1184 showHotwordSection_: function(opt_error) {
1185 this.showHotwordCheckboxAndIndicator_(
1187 'hotword-search-setting-indicator',
1192 * Activates the Audio History and Always-On Hotword sections from the
1193 * System settings page.
1194 * @param {string=} opt_error The error message to display.
1197 showHotwordAlwaysOnSection_: function(opt_error) {
1198 this.showHotwordCheckboxAndIndicator_(
1199 'hotword-always-on-search',
1200 'hotword-always-on-search-setting-indicator',
1205 * Activates the Hotword section on devices with no DSP
1206 * from the System settings page.
1207 * @param {string=} opt_error The error message to display.
1210 showHotwordNoDspSection_: function(opt_error) {
1211 this.showHotwordCheckboxAndIndicator_(
1212 'hotword-no-dsp-search',
1213 'hotword-no-dsp-search-setting-indicator',
1218 * Controls the visibility of all the hotword sections.
1219 * @param {boolean} visible Whether to show hotword sections.
1222 setAllHotwordSectionsVisible_: function(visible) {
1223 $('hotword-search').hidden = !visible;
1224 $('hotword-always-on-search').hidden = !visible;
1225 $('hotword-no-dsp-search').hidden = !visible;
1226 $('audio-history').hidden = !visible;
1230 * Shows or hides the hotword retrain link
1231 * @param {boolean} visible Whether to show the link.
1234 setHotwordRetrainLinkVisible_: function(visible) {
1235 $('hotword-retrain-link').hidden = !visible;
1239 * Event listener for the 'hotword always on search enabled' preference.
1240 * Updates the visibility of the 'retrain' link.
1241 * @param {Event} event The preference change event.
1244 onHotwordAlwaysOnChanged_: function(event) {
1245 this.setHotwordRetrainLinkVisible_(event.value.value);
1249 * Activates the Audio History section of the Settings page.
1250 * @param {boolean} visible Whether the audio history section is visible.
1251 * @param {boolean} alwaysOn Whether always-on hotwording is available.
1252 * @param {string} labelText Text describing current audio history state.
1255 setAudioHistorySectionVisible_: function(visible, alwaysOn, labelText) {
1256 $('audio-history').hidden = !visible;
1257 $('audio-history-label').textContent = labelText;
1258 $('audio-history-always-on-description').hidden = !alwaysOn;
1262 * Event listener for the 'homepage is NTP' preference. Updates the label
1263 * next to the 'Change' button.
1264 * @param {Event} event The preference change event.
1266 onHomePageIsNtpChanged_: function(event) {
1267 if (!event.value.uncommitted) {
1268 $('home-page-url').hidden = event.value.value;
1269 $('home-page-ntp').hidden = !event.value.value;
1274 * Event listener for changes to the homepage preference. Updates the label
1275 * next to the 'Change' button.
1276 * @param {Event} event The preference change event.
1278 onHomePageChanged_: function(event) {
1279 if (!event.value.uncommitted)
1280 $('home-page-url').textContent = this.stripHttp_(event.value.value);
1284 * Removes the 'http://' from a URL, like the omnibox does. If the string
1285 * doesn't start with 'http://' it is returned unchanged.
1286 * @param {string} url The url to be processed
1287 * @return {string} The url with the 'http://' removed.
1289 stripHttp_: function(url) {
1290 return url.replace(/^http:\/\//, '');
1294 * Shows the autoLaunch preference and initializes its checkbox value.
1295 * @param {boolean} enabled Whether autolaunch is enabled or or not.
1298 updateAutoLaunchState_: function(enabled) {
1299 $('auto-launch-option').hidden = false;
1300 $('auto-launch').checked = enabled;
1304 * Called when the value of the download.default_directory preference
1306 * @param {Event} event Change event.
1309 onDefaultDownloadDirectoryChanged_: function(event) {
1310 $('downloadLocationPath').value = event.value.value;
1311 if (cr.isChromeOS) {
1312 // On ChromeOS, replace /special/drive-<hash>/root with "Google Drive"
1313 // for remote files, /home/chronos/user/Downloads or
1314 // /home/chronos/u-<hash>/Downloads with "Downloads" for local paths,
1315 // and '/' with ' \u203a ' (angled quote sign) everywhere. The modified
1316 // path is used only for display purpose.
1317 var path = $('downloadLocationPath').value;
1318 path = path.replace(/^\/special\/drive[^\/]*\/root/, 'Google Drive');
1319 path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, '');
1320 path = path.replace(/\//g, ' \u203a ');
1321 $('downloadLocationPath').value = path;
1323 $('download-location-label').classList.toggle('disabled',
1324 event.value.disabled);
1325 $('downloadLocationChangeButton').disabled = event.value.disabled;
1329 * Update the Default Browsers section based on the current state.
1330 * @param {string} statusString Description of the current default state.
1331 * @param {boolean} isDefault Whether or not the browser is currently
1333 * @param {boolean} canBeDefault Whether or not the browser can be default.
1336 updateDefaultBrowserState_: function(statusString, isDefault,
1338 if (!cr.isChromeOS) {
1339 var label = $('default-browser-state');
1340 label.textContent = statusString;
1342 $('set-as-default-browser').hidden = !canBeDefault || isDefault;
1347 * Clears the search engine popup.
1350 clearSearchEngines_: function() {
1351 $('default-search-engine').textContent = '';
1355 * Updates the search engine popup with the given entries.
1356 * @param {Array} engines List of available search engines.
1357 * @param {number} defaultValue The value of the current default engine.
1358 * @param {boolean} defaultManaged Whether the default search provider is
1359 * managed. If true, the default search provider can't be changed.
1362 updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
1363 this.clearSearchEngines_();
1364 var engineSelect = $('default-search-engine');
1365 engineSelect.disabled = defaultManaged;
1366 if (defaultManaged && defaultValue == -1)
1368 var engineCount = engines.length;
1369 var defaultIndex = -1;
1370 for (var i = 0; i < engineCount; i++) {
1371 var engine = engines[i];
1372 var option = new Option(engine.name, engine.index);
1373 if (defaultValue == option.value)
1375 engineSelect.appendChild(option);
1377 if (defaultIndex >= 0)
1378 engineSelect.selectedIndex = defaultIndex;
1382 * Set the default search engine based on the popup selection.
1385 setDefaultSearchEngine_: function() {
1386 var engineSelect = $('default-search-engine');
1387 var selectedIndex = engineSelect.selectedIndex;
1388 if (selectedIndex >= 0) {
1389 var selection = engineSelect.options[selectedIndex];
1390 chrome.send('setDefaultSearchEngine', [String(selection.value)]);
1395 * Sets or clear whether Chrome should Auto-launch on computer startup.
1398 handleAutoLaunchChanged_: function() {
1399 chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
1403 * Get the selected profile item from the profile list. This also works
1404 * correctly if the list is not displayed.
1405 * @return {?Object} The profile item object, or null if nothing is
1409 getSelectedProfileItem_: function() {
1410 var profilesList = $('profiles-list');
1411 if (profilesList.hidden) {
1412 if (profilesList.dataModel.length > 0)
1413 return profilesList.dataModel.item(0);
1415 return profilesList.selectedItem;
1421 * Helper function to set the status of profile view buttons to disabled or
1422 * enabled, depending on the number of profiles and selection status of the
1426 setProfileViewButtonsStatus_: function() {
1427 var profilesList = $('profiles-list');
1428 var selectedProfile = profilesList.selectedItem;
1429 var hasSelection = selectedProfile != null;
1430 var hasSingleProfile = profilesList.dataModel.length == 1;
1431 $('profiles-manage').disabled = !hasSelection ||
1432 !selectedProfile.isCurrentProfile;
1433 if (hasSelection && !selectedProfile.isCurrentProfile)
1434 $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1436 $('profiles-manage').title = '';
1437 $('profiles-delete').disabled = !profilesList.canDeleteItems ||
1438 (!hasSelection && !hasSingleProfile);
1439 if (OptionsPage.isSettingsApp()) {
1440 $('profiles-app-list-switch').disabled = !hasSelection ||
1441 selectedProfile.isCurrentProfile;
1443 var importData = $('import-data');
1445 importData.disabled = $('import-data').disabled = hasSelection &&
1446 !selectedProfile.isCurrentProfile;
1451 * Display the correct dialog layout, depending on how many profiles are
1453 * @param {number} numProfiles The number of profiles to display.
1456 setProfileViewSingle_: function(numProfiles) {
1457 // Always show the profiles list when using the new Profiles UI.
1458 var usingNewProfilesUI = loadTimeData.getBoolean('usingNewProfilesUI');
1459 var showSingleProfileView = !usingNewProfilesUI && numProfiles == 1;
1460 $('profiles-list').hidden = showSingleProfileView;
1461 $('profiles-single-message').hidden = !showSingleProfileView;
1462 $('profiles-manage').hidden =
1463 showSingleProfileView || OptionsPage.isSettingsApp();
1464 $('profiles-delete').textContent = showSingleProfileView ?
1465 loadTimeData.getString('profilesDeleteSingle') :
1466 loadTimeData.getString('profilesDelete');
1467 if (OptionsPage.isSettingsApp())
1468 $('profiles-app-list-switch').hidden = showSingleProfileView;
1472 * Adds all |profiles| to the list.
1473 * @param {Array.<!options.Profile>} profiles An array of profile info
1477 setProfilesInfo_: function(profiles) {
1478 this.setProfileViewSingle_(profiles.length);
1479 // add it to the list, even if the list is hidden so we can access it
1481 $('profiles-list').dataModel = new ArrayDataModel(profiles);
1483 // Received new data. If showing the "manage" overlay, keep it up to
1484 // date. If showing the "delete" overlay, close it.
1485 if (ManageProfileOverlay.getInstance().visible &&
1486 !$('manage-profile-overlay-manage').hidden) {
1487 ManageProfileOverlay.showManageDialog(false);
1489 ManageProfileOverlay.getInstance().visible = false;
1492 this.setProfileViewButtonsStatus_();
1496 * Reports supervised user import errors to the SupervisedUserImportOverlay.
1497 * @param {string} error The error message to display.
1500 showSupervisedUserImportError_: function(error) {
1501 SupervisedUserImportOverlay.onError(error);
1505 * Reports successful importing of a supervised user to
1506 * the SupervisedUserImportOverlay.
1509 showSupervisedUserImportSuccess_: function() {
1510 SupervisedUserImportOverlay.onSuccess();
1514 * Reports an error to the "create" overlay during profile creation.
1515 * @param {string} error The error message to display.
1518 showCreateProfileError_: function(error) {
1519 CreateProfileOverlay.onError(error);
1523 * Sends a warning message to the "create" overlay during profile creation.
1524 * @param {string} warning The warning message to display.
1527 showCreateProfileWarning_: function(warning) {
1528 CreateProfileOverlay.onWarning(warning);
1532 * Reports successful profile creation to the "create" overlay.
1533 * @param {options.Profile} profileInfo An object of the form:
1535 * name: "Profile Name",
1536 * filePath: "/path/to/profile/data/on/disk"
1537 * isSupervised: (true|false),
1541 showCreateProfileSuccess_: function(profileInfo) {
1542 CreateProfileOverlay.onSuccess(profileInfo);
1546 * Returns the currently active profile for this browser window.
1547 * @return {options.Profile} A profile info object.
1550 getCurrentProfile_: function() {
1551 for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1552 var profile = $('profiles-list').dataModel.item(i);
1553 if (profile.isCurrentProfile)
1557 assertNotReached('There should always be a current profile.');
1561 * Propmpts user to confirm deletion of the profile for this browser
1565 deleteCurrentProfile_: function() {
1566 ManageProfileOverlay.showDeleteDialog(this.getCurrentProfile_());
1570 * @param {boolean} enabled
1572 setNativeThemeButtonEnabled_: function(enabled) {
1573 var button = $('themes-native-button');
1575 button.disabled = !enabled;
1579 * @param {boolean} enabled
1581 setThemesResetButtonEnabled_: function(enabled) {
1582 $('themes-reset').disabled = !enabled;
1586 * @param {boolean} managed
1588 setAccountPictureManaged_: function(managed) {
1589 var picture = $('account-picture');
1590 if (managed || UIAccountTweaks.loggedInAsGuest()) {
1591 picture.disabled = true;
1592 ChangePictureOptions.closeOverlay();
1594 picture.disabled = false;
1597 // Create a synthetic pref change event decorated as
1598 // CoreOptionsHandler::CreateValueForPref() does.
1599 var event = new Event('account-picture');
1601 event.value = { controlledBy: 'policy' };
1604 $('account-picture-indicator').handlePrefChange(event);
1608 * (Re)loads IMG element with current user account picture.
1611 updateAccountPicture_: function() {
1612 var picture = $('account-picture');
1614 picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1620 * @param {boolean} managed
1622 setWallpaperManaged_: function(managed) {
1624 $('set-wallpaper').disabled = true;
1626 this.enableElementIfPossible_(getRequiredElement('set-wallpaper'));
1628 // Create a synthetic pref change event decorated as
1629 // CoreOptionsHandler::CreateValueForPref() does.
1630 var event = new Event('wallpaper');
1631 event.value = managed ? { controlledBy: 'policy' } : {};
1632 $('wallpaper-indicator').handlePrefChange(event);
1636 * This is called from chromium code when system timezone "managed" state
1637 * is changed. Enables or disables dependent settings.
1638 * @param {boolean} managed Is true when system Timezone is managed by
1639 * enterprise policy. False otherwize.
1641 setSystemTimezoneManaged_: function(managed) {
1642 if (loadTimeData.getBoolean('enableTimeZoneTrackingOption')) {
1644 $('resolve-timezone-by-geolocation-selection').disabled = true;
1645 $('resolve-timezone-by-geolocation').onclick = function(event) {};
1647 this.enableElementIfPossible_(
1648 getRequiredElement('resolve-timezone-by-geolocation-selection'));
1649 $('resolve-timezone-by-geolocation').onclick = function(event) {
1650 $('timezone-value-select').disabled = event.currentTarget.checked;
1652 $('timezone-value-select').disabled =
1653 $('resolve-timezone-by-geolocation').checked;
1659 * Handle the 'add device' button click.
1662 handleAddBluetoothDevice_: function() {
1663 chrome.send('coreOptionsUserMetricsAction',
1664 ['Options_BluetoothShowAddDevice']);
1665 chrome.send('findBluetoothDevices');
1666 PageManager.showPageByName('bluetooth', false);
1670 * Enables or disables the Manage SSL Certificates button.
1673 enableCertificateButton_: function(enabled) {
1674 $('certificatesManageButton').disabled = !enabled;
1678 * Enables or disables the ChromeOS display settings button.
1681 enableDisplayButton_: function(enabled) {
1683 $('display-options').disabled = !enabled;
1687 * Enables factory reset section.
1690 enableFactoryResetSection_: function() {
1691 $('factory-reset-section').hidden = false;
1695 * Set the checked state of the metrics reporting checkbox.
1698 setMetricsReportingCheckboxState_: function(checked, disabled) {
1699 $('metricsReportingEnabled').checked = checked;
1700 $('metricsReportingEnabled').disabled = disabled;
1702 // If checkbox gets disabled then add an attribute for displaying the
1703 // special icon. Otherwise remove the indicator attribute.
1705 $('metrics-reporting-disabled-icon').setAttribute('controlled-by',
1708 $('metrics-reporting-disabled-icon').removeAttribute('controlled-by');
1715 setMetricsReportingSettingVisibility_: function(visible) {
1717 $('metricsReportingSetting').style.display = 'block';
1719 $('metricsReportingSetting').style.display = 'none';
1723 * Set network prediction checkbox value.
1725 * @param {{value: number, disabled: boolean}} pref Information about
1726 * network prediction options. |pref.value| is the value of network
1727 * prediction options. |pref.disabled| shows if the pref is not user
1731 setNetworkPredictionValue_: function(pref) {
1732 var checkbox = $('networkPredictionOptions');
1733 checkbox.disabled = pref.disabled;
1734 if (pref.value == NetworkPredictionOptions.UNSET) {
1735 checkbox.checked = (NetworkPredictionOptions.DEFAULT !=
1736 NetworkPredictionOptions.NEVER);
1738 checkbox.checked = (pref.value != NetworkPredictionOptions.NEVER);
1743 * Set the font size selected item. This item actually reflects two
1744 * preferences: the default font size and the default fixed font size.
1746 * @param {{value: number, disabled: boolean, controlledBy: string}} pref
1747 * Information about the font size preferences. |pref.value| is the
1748 * value of the default font size pref. |pref.disabled| is true if
1749 * either pref not user modifiable. |pref.controlledBy| is the source of
1750 * the pref value(s) if either pref is currently not controlled by the
1754 setFontSize_: function(pref) {
1755 var selectCtl = $('defaultFontSize');
1756 selectCtl.disabled = pref.disabled;
1757 // Create a synthetic pref change event decorated as
1758 // CoreOptionsHandler::CreateValueForPref() does.
1759 var event = new Event('synthetic-font-size');
1762 controlledBy: pref.controlledBy,
1763 disabled: pref.disabled
1765 $('font-size-indicator').handlePrefChange(event);
1767 for (var i = 0; i < selectCtl.options.length; i++) {
1768 if (selectCtl.options[i].value == pref.value) {
1769 selectCtl.selectedIndex = i;
1771 selectCtl.remove($('Custom').index);
1776 // Add/Select Custom Option in the font size label list.
1778 var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1780 option.setAttribute('id', 'Custom');
1781 selectCtl.add(option);
1783 $('Custom').selected = true;
1787 * Populate the page zoom selector with values received from the caller.
1788 * @param {Array} items An array of items to populate the selector.
1789 * each object is an array with three elements as follows:
1790 * 0: The title of the item (string).
1791 * 1: The value of the item (number).
1792 * 2: Whether the item should be selected (boolean).
1795 setupPageZoomSelector_: function(items) {
1796 var element = $('defaultZoomFactor');
1798 // Remove any existing content.
1799 element.textContent = '';
1801 // Insert new child nodes into select element.
1802 var value, title, selected;
1803 for (var i = 0; i < items.length; i++) {
1804 title = items[i][0];
1805 value = items[i][1];
1806 selected = items[i][2];
1807 element.appendChild(new Option(title, value, false, selected));
1812 * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1814 * @param {boolean} display Whether to show the button and label or not.
1817 setAutoOpenFileTypesDisplayed_: function(display) {
1818 if ($('advanced-settings').hidden) {
1819 // If the Advanced section is hidden, don't animate the transition.
1820 $('auto-open-file-types-section').hidden = !display;
1823 this.showSectionWithAnimation_(
1824 $('auto-open-file-types-section'),
1825 $('auto-open-file-types-container'));
1827 this.hideSectionWithAnimation_(
1828 $('auto-open-file-types-section'),
1829 $('auto-open-file-types-container'));
1835 * Set the enabled state for the proxy settings button and its associated
1836 * message when extension controlled.
1837 * @param {boolean} disabled Whether the button should be disabled.
1838 * @param {boolean} extensionControlled Whether the proxy is extension
1842 setupProxySettingsButton_: function(disabled, extensionControlled) {
1843 if (!cr.isChromeOS) {
1844 $('proxiesConfigureButton').disabled = disabled;
1845 $('proxiesLabel').textContent =
1846 loadTimeData.getString(extensionControlled ?
1847 'proxiesLabelExtension' : 'proxiesLabelSystem');
1852 * Set the initial state of the spoken feedback checkbox.
1855 setSpokenFeedbackCheckboxState_: function(checked) {
1856 $('accessibility-spoken-feedback-check').checked = checked;
1860 * Set the initial state of the high contrast checkbox.
1863 setHighContrastCheckboxState_: function(checked) {
1864 $('accessibility-high-contrast-check').checked = checked;
1868 * Set the initial state of the virtual keyboard checkbox.
1871 setVirtualKeyboardCheckboxState_: function(checked) {
1872 // TODO(zork): Update UI
1876 * Show/hide mouse settings slider.
1879 showMouseControls_: function(show) {
1880 $('mouse-settings').hidden = !show;
1884 * Adds hidden warning boxes for settings potentially controlled by
1886 * @param {string} parentDiv The div name to append the bubble to.
1887 * @param {string} bubbleId The ID to use for the bubble.
1888 * @param {boolean} first Add as first node if true, otherwise last.
1891 addExtensionControlledBox_: function(parentDiv, bubbleId, first) {
1892 var bubble = $('extension-controlled-warning-template').cloneNode(true);
1893 bubble.id = bubbleId;
1894 var parent = $(parentDiv);
1896 parent.insertBefore(bubble, parent.firstChild);
1898 parent.appendChild(bubble);
1902 * Adds a bubble showing that an extension is controlling a particular
1904 * @param {string} parentDiv The div name to append the bubble to.
1905 * @param {string} bubbleId The ID to use for the bubble.
1906 * @param {string} extensionId The ID of the controlling extension.
1907 * @param {string} extensionName The name of the controlling extension.
1910 toggleExtensionControlledBox_: function(
1911 parentDiv, bubbleId, extensionId, extensionName) {
1912 var bubble = $(bubbleId);
1914 bubble.hidden = extensionId.length == 0;
1918 // Set the extension image.
1919 var div = bubble.firstElementChild;
1920 div.style.backgroundImage =
1921 'url(chrome://extension-icon/' + extensionId + '/24/1)';
1923 // Set the bubble label.
1924 var label = loadTimeData.getStringF('extensionControlled', extensionName);
1925 var docFrag = parseHtmlSubset('<div>' + label + '</div>', ['B', 'DIV']);
1926 div.innerHTML = docFrag.firstChild.innerHTML;
1928 // Wire up the button to disable the right extension.
1929 var button = div.nextElementSibling;
1930 button.dataset.extensionId = extensionId;
1934 * Toggles the warning boxes that show which extension is controlling
1935 * various settings of Chrome.
1936 * @param {{searchEngine: options.ExtensionData,
1937 * homePage: options.ExtensionData,
1938 * startUpPage: options.ExtensionData,
1939 * newTabPage: options.ExtensionData,
1940 * proxy: options.ExtensionData}} details A dictionary of ID+name
1941 * pairs for each of the settings controlled by an extension.
1944 toggleExtensionIndicators_: function(details) {
1945 this.toggleExtensionControlledBox_('search-section-content',
1946 'search-engine-controlled',
1947 details.searchEngine.id,
1948 details.searchEngine.name);
1949 this.toggleExtensionControlledBox_('extension-controlled-container',
1950 'homepage-controlled',
1951 details.homePage.id,
1952 details.homePage.name);
1953 this.toggleExtensionControlledBox_('startup-section-content',
1954 'startpage-controlled',
1955 details.startUpPage.id,
1956 details.startUpPage.name);
1957 this.toggleExtensionControlledBox_('newtab-section-content',
1958 'newtab-controlled',
1959 details.newTabPage.id,
1960 details.newTabPage.name);
1961 this.toggleExtensionControlledBox_('proxy-section-content',
1964 details.proxy.name);
1966 // The proxy section contains just the warning box and nothing else, so
1967 // if we're hiding the proxy warning box, we should also hide its header
1969 $('proxy-section').hidden = details.proxy.id.length == 0;
1974 * Show/hide touchpad-related settings.
1977 showTouchpadControls_: function(show) {
1978 $('touchpad-settings').hidden = !show;
1979 $('accessibility-tap-dragging').hidden = !show;
1983 * Activate the Bluetooth settings section on the System settings page.
1986 showBluetoothSettings_: function() {
1987 $('bluetooth-devices').hidden = false;
1991 * Dectivates the Bluetooth settings section from the System settings page.
1994 hideBluetoothSettings_: function() {
1995 $('bluetooth-devices').hidden = true;
1999 * Sets the state of the checkbox indicating if Bluetooth is turned on. The
2000 * state of the "Find devices" button and the list of discovered devices may
2001 * also be affected by a change to the state.
2002 * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
2005 setBluetoothState_: function(checked) {
2006 $('enable-bluetooth').checked = checked;
2007 $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
2008 $('bluetooth-add-device').hidden = !checked;
2009 $('bluetooth-reconnect-device').hidden = !checked;
2010 // Flush list of previously discovered devices if bluetooth is turned off.
2012 $('bluetooth-paired-devices-list').clear();
2013 $('bluetooth-unpaired-devices-list').clear();
2015 chrome.send('getPairedBluetoothDevices');
2020 * Adds an element to the list of available Bluetooth devices. If an element
2021 * with a matching address is found, the existing element is updated.
2022 * @param {{name: string,
2025 * connected: boolean}} device
2026 * Decription of the Bluetooth device.
2029 addBluetoothDevice_: function(device) {
2030 var list = $('bluetooth-unpaired-devices-list');
2031 // Display the "connecting" (already paired or not yet paired) and the
2032 // paired devices in the same list.
2033 if (device.paired || device.connecting) {
2034 // Test to see if the device is currently in the unpaired list, in which
2035 // case it should be removed from that list.
2036 var index = $('bluetooth-unpaired-devices-list').find(device.address);
2037 if (index != undefined)
2038 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
2039 list = $('bluetooth-paired-devices-list');
2041 // Test to see if the device is currently in the paired list, in which
2042 // case it should be removed from that list.
2043 var index = $('bluetooth-paired-devices-list').find(device.address);
2044 if (index != undefined)
2045 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
2047 list.appendDevice(device);
2049 // One device can be in the process of pairing. If found, display
2050 // the Bluetooth pairing overlay.
2052 BluetoothPairing.showDialog(device);
2056 * Removes an element from the list of available devices.
2057 * @param {string} address Unique address of the device.
2060 removeBluetoothDevice_: function(address) {
2061 var index = $('bluetooth-unpaired-devices-list').find(address);
2062 if (index != undefined) {
2063 $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
2065 index = $('bluetooth-paired-devices-list').find(address);
2066 if (index != undefined)
2067 $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
2072 * Shows the overlay dialog for changing the user avatar image.
2075 showImagerPickerOverlay_: function() {
2076 PageManager.showPageByName('changePicture');
2080 * Shows (or not) the "User" section of the settings page based on whether
2081 * any of the sub-sections are present (or not).
2084 maybeShowUserSection_: function() {
2085 $('sync-users-section').hidden =
2086 $('profiles-section').hidden &&
2087 $('sync-section').hidden &&
2088 $('profiles-supervised-dashboard-tip').hidden;
2092 * Updates the date and time section with time sync information.
2093 * @param {boolean} canSetTime Whether the system time can be set.
2096 setCanSetTime_: function(canSetTime) {
2097 // If the time has been network-synced, it cannot be set manually.
2098 $('time-synced-explanation').hidden = canSetTime;
2099 $('set-time').hidden = !canSetTime;
2103 * Handle the 'set date and time' button click.
2106 handleSetTime_: function() {
2107 chrome.send('showSetTime');
2111 * Enables the given element if possible; on Chrome OS, it won't enable
2112 * an element that must stay disabled for the session type.
2113 * @param {!Element} element Element to enable.
2115 enableElementIfPossible_: function(element) {
2117 UIAccountTweaks.enableElementIfPossible(element);
2119 element.disabled = false;
2123 * Sets the icon in the battery section.
2124 * @param {string} iconData The data representing the icon to display.
2127 setBatteryIcon_: function(iconData) {
2128 $('battery-icon').style.backgroundImage = 'url(' + iconData + ')';
2129 $('battery-icon').hidden = false;
2133 * Sets the text for the battery section.
2134 * @param {string} statusText The battery status, with a relevant label.
2137 setBatteryStatusText_: function(statusText) {
2138 $('battery').hidden = !statusText.length;
2139 if (statusText.length) {
2140 $('battery-status').textContent = statusText;
2141 chrome.send('requestBatteryIcon');
2146 //Forward public APIs to private implementations.
2147 cr.makePublic(BrowserOptions, [
2148 'addBluetoothDevice',
2149 'deleteCurrentProfile',
2150 'enableCertificateButton',
2151 'enableDisplayButton',
2152 'enableFactoryResetSection',
2153 'getCurrentProfile',
2154 'getStartStopSyncButton',
2155 'hideBluetoothSettings',
2156 'notifyInitializationComplete',
2157 'removeBluetoothDevice',
2159 'setAccountPictureManaged',
2160 'setWallpaperManaged',
2161 'setAutoOpenFileTypesDisplayed',
2163 'setBatteryStatusText',
2164 'setBluetoothState',
2167 'setHotwordRetrainLinkVisible',
2168 'setNativeThemeButtonEnabled',
2169 'setNetworkPredictionValue',
2170 'setHighContrastCheckboxState',
2171 'setAllHotwordSectionsVisible',
2172 'setMetricsReportingCheckboxState',
2173 'setMetricsReportingSettingVisibility',
2175 'setSpokenFeedbackCheckboxState',
2176 'setThemesResetButtonEnabled',
2177 'setVirtualKeyboardCheckboxState',
2178 'setupPageZoomSelector',
2179 'setupProxySettingsButton',
2180 'setAudioHistorySectionVisible',
2181 'showBluetoothSettings',
2182 'showCreateProfileError',
2183 'showCreateProfileSuccess',
2184 'showCreateProfileWarning',
2185 'showHotwordAlwaysOnSection',
2186 'showHotwordNoDspSection',
2187 'showHotwordSection',
2188 'showMouseControls',
2189 'showSupervisedUserImportError',
2190 'showSupervisedUserImportSuccess',
2191 'showTouchpadControls',
2192 'toggleExtensionIndicators',
2193 'updateAccountPicture',
2194 'updateAutoLaunchState',
2195 'updateDefaultBrowserState',
2197 'updateManagesSupervisedUsers',
2198 'updateSearchEngines',
2202 if (cr.isChromeOS) {
2204 * Returns username (canonical email) of the user logged in (ChromeOS only).
2205 * @return {string} user email.
2207 // TODO(jhawkins): Investigate the use case for this method.
2208 BrowserOptions.getLoggedInUsername = function() {
2209 return BrowserOptions.getInstance().username_;
2213 * Shows different button text for each consumer management enrollment
2215 * @enum {string} status Consumer management service status string.
2217 BrowserOptions.setConsumerManagementStatus = function(status) {
2218 var button = $('consumer-management-button');
2219 if (status == 'StatusUnknown') {
2220 button.hidden = true;
2224 button.hidden = false;
2227 case ConsumerManagementOverlay.Status.STATUS_UNENROLLED:
2228 strId = 'consumerManagementEnrollButton';
2229 button.disabled = false;
2230 ConsumerManagementOverlay.setStatus(status);
2232 case ConsumerManagementOverlay.Status.STATUS_ENROLLING:
2233 strId = 'consumerManagementEnrollingButton';
2234 button.disabled = true;
2236 case ConsumerManagementOverlay.Status.STATUS_ENROLLED:
2237 strId = 'consumerManagementUnenrollButton';
2238 button.disabled = false;
2239 ConsumerManagementOverlay.setStatus(status);
2241 case ConsumerManagementOverlay.Status.STATUS_UNENROLLING:
2242 strId = 'consumerManagementUnenrollingButton';
2243 button.disabled = true;
2246 button.textContent = loadTimeData.getString(strId);
2252 BrowserOptions: BrowserOptions