blob: 2a4630562561e1411f2980dfefea589d497dfe03 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cr.exportPath('options');
/**
* @typedef {{actionLinkText: (string|undefined),
* hasError: (boolean|undefined),
* hasUnrecoverableError: (boolean|undefined),
* managed: (boolean|undefined),
* setupCompleted: (boolean|undefined),
* setupInProgress: (boolean|undefined),
* signedIn: (boolean|undefined),
* signinAllowed: (boolean|undefined),
* signoutAllowed: (boolean|undefined),
* statusText: (string|undefined),
* syncSystemEnabled: (boolean|undefined)}}
* @see chrome/browser/ui/webui/options/browser_options_handler.cc
*/
options.SyncStatus;
/**
* @typedef {{id: string, name: string}}
*/
options.ExtensionData;
cr.define('options', function() {
var OptionsPage = options.OptionsPage;
var Page = cr.ui.pageManager.Page;
var PageManager = cr.ui.pageManager.PageManager;
var ArrayDataModel = cr.ui.ArrayDataModel;
var RepeatingButton = cr.ui.RepeatingButton;
var HotwordSearchSettingIndicator = options.HotwordSearchSettingIndicator;
var NetworkPredictionOptions = {
ALWAYS: 0,
WIFI_ONLY: 1,
NEVER: 2,
UNSET: 3,
DEFAULT: 1
};
/**
* Encapsulated handling of browser options page.
* @constructor
* @extends {cr.ui.pageManager.Page}
*/
function BrowserOptions() {
Page.call(this, 'settings', loadTimeData.getString('settingsTitle'),
'settings');
}
cr.addSingletonGetter(BrowserOptions);
/**
* @param {HTMLElement} section The section to show or hide.
* @return {boolean} Whether the section should be shown.
* @private
*/
BrowserOptions.shouldShowSection_ = function(section) {
// If the section is hidden or hiding, it should be shown.
return section.style.height == '' || section.style.height == '0px';
};
BrowserOptions.prototype = {
__proto__: Page.prototype,
/**
* Keeps track of whether the user is signed in or not.
* @type {boolean}
* @private
*/
signedIn_: false,
/**
* Indicates whether signing out is allowed or whether a complete profile
* wipe is required to remove the current enterprise account.
* @type {boolean}
* @private
*/
signoutAllowed_: true,
/**
* Keeps track of whether |onShowHomeButtonChanged_| has been called. See
* |onShowHomeButtonChanged_|.
* @type {boolean}
* @private
*/
onShowHomeButtonChangedCalled_: false,
/**
* Track if page initialization is complete. All C++ UI handlers have the
* chance to manipulate page content within their InitializePage methods.
* This flag is set to true after all initializers have been called.
* @type {boolean}
* @private
*/
initializationComplete_: false,
/** @override */
initializePage: function() {
Page.prototype.initializePage.call(this);
var self = this;
if (window.top != window) {
// The options page is not in its own window.
document.body.classList.add('uber-frame');
PageManager.horizontalOffset = 155;
}
// Ensure that navigation events are unblocked on uber page. A reload of
// the settings page while an overlay is open would otherwise leave uber
// page in a blocked state, where tab switching is not possible.
uber.invokeMethodOnParent('stopInterceptingEvents');
window.addEventListener('message', this.handleWindowMessage_.bind(this));
if (loadTimeData.getBoolean('allowAdvancedSettings')) {
$('advanced-settings-expander').onclick = function() {
var showAdvanced =
BrowserOptions.shouldShowSection_($('advanced-settings'));
if (showAdvanced) {
chrome.send('coreOptionsUserMetricsAction',
['Options_ShowAdvancedSettings']);
}
self.toggleSectionWithAnimation_(
$('advanced-settings'),
$('advanced-settings-container'));
// If the link was focused (i.e., it was activated using the keyboard)
// and it was used to show the section (rather than hiding it), focus
// the first element in the container.
if (document.activeElement === $('advanced-settings-expander') &&
showAdvanced) {
var focusElement = $('advanced-settings-container').querySelector(
'button, input, list, select, a[href]');
if (focusElement)
focusElement.focus();
}
};
} else {
$('advanced-settings-expander').hidden = true;
$('advanced-settings').hidden = true;
}
$('advanced-settings').addEventListener('webkitTransitionEnd',
this.updateAdvancedSettingsExpander_.bind(this));
if (loadTimeData.getBoolean('showAbout')) {
$('about-button').hidden = false;
$('about-button').addEventListener('click', function() {
PageManager.showPageByName('help');
chrome.send('coreOptionsUserMetricsAction',
['Options_About']);
});
}
if (cr.isChromeOS) {
UIAccountTweaks.applyGuestSessionVisibility(document);
UIAccountTweaks.applyPublicSessionVisibility(document);
if (loadTimeData.getBoolean('secondaryUser'))
$('secondary-user-banner').hidden = false;
}
// Sync (Sign in) section.
this.updateSyncState_(loadTimeData.getValue('syncData'));
$('start-stop-sync').onclick = function(event) {
if (self.signedIn_) {
if (self.signoutAllowed_)
SyncSetupOverlay.showStopSyncingUI();
else
chrome.send('showDisconnectManagedProfileDialog');
} else if (cr.isChromeOS) {
SyncSetupOverlay.showSetupUI();
} else {
SyncSetupOverlay.startSignIn();
}
};
$('customize-sync').onclick = function(event) {
SyncSetupOverlay.showSetupUI();
};
// Internet connection section (ChromeOS only).
if (cr.isChromeOS) {
options.network.NetworkList.decorate($('network-list'));
// Show that the network settings are shared if this is a secondary user
// in a multi-profile session.
if (loadTimeData.getBoolean('secondaryUser')) {
var networkIndicator = document.querySelector(
'#network-section-header > .controlled-setting-indicator');
networkIndicator.setAttribute('controlled-by', 'shared');
networkIndicator.location = cr.ui.ArrowLocation.TOP_START;
}
options.network.NetworkList.refreshNetworkData(
loadTimeData.getValue('networkData'));
}
// On Startup section.
Preferences.getInstance().addEventListener('session.restore_on_startup',
this.onRestoreOnStartupChanged_.bind(this));
Preferences.getInstance().addEventListener(
'session.startup_urls',
function(event) {
$('startup-set-pages').disabled = event.value.disabled;
});
$('startup-set-pages').onclick = function() {
PageManager.showPageByName('startup');
};
// Appearance section.
Preferences.getInstance().addEventListener('browser.show_home_button',
this.onShowHomeButtonChanged_.bind(this));
Preferences.getInstance().addEventListener('homepage',
this.onHomePageChanged_.bind(this));
Preferences.getInstance().addEventListener('homepage_is_newtabpage',
this.onHomePageIsNtpChanged_.bind(this));
$('change-home-page').onclick = function(event) {
PageManager.showPageByName('homePageOverlay');
chrome.send('coreOptionsUserMetricsAction',
['Options_Homepage_ShowSettings']);
};
var hotwordIndicator = $('hotword-search-setting-indicator');
HotwordSearchSettingIndicator.decorate(hotwordIndicator);
chrome.send('requestHotwordAvailable');
if ($('set-wallpaper')) {
$('set-wallpaper').onclick = function(event) {
chrome.send('openWallpaperManager');
chrome.send('coreOptionsUserMetricsAction',
['Options_OpenWallpaperManager']);
};
}
// Control the hotword-always-on pref with the Hotword Audio
// Verification app.
$('hotword-always-on-search-checkbox').customChangeHandler =
function(event) {
if (!$('hotword-always-on-search-checkbox').checked)
return false;
$('hotword-always-on-search-checkbox').checked = false;
chrome.send('launchHotwordAudioVerificationApp', [false]);
return true;
};
$('themes-gallery').onclick = function(event) {
window.open(loadTimeData.getString('themesGalleryURL'));
chrome.send('coreOptionsUserMetricsAction',
['Options_ThemesGallery']);
};
$('themes-reset').onclick = function(event) {
chrome.send('themesReset');
};
if (loadTimeData.getBoolean('profileIsSupervised')) {
if ($('themes-native-button')) {
$('themes-native-button').disabled = true;
$('themes-native-button').hidden = true;
}
// Supervised users have just one default theme, even on Linux. So use
// the same button for Linux as for the other platforms.
$('themes-reset').textContent = loadTimeData.getString('themesReset');
}
// Device section (ChromeOS only).
if (cr.isChromeOS) {
$('battery-button').onclick = function(evt) {
WebsiteSettingsManager.showWebsiteSettings('battery');
};
$('stored-data-button').onclick = function(evt) {
WebsiteSettingsManager.showWebsiteSettings('storage');
};
$('keyboard-settings-button').onclick = function(evt) {
PageManager.showPageByName('keyboard-overlay');
chrome.send('coreOptionsUserMetricsAction',
['Options_ShowKeyboardSettings']);
};
$('pointer-settings-button').onclick = function(evt) {
PageManager.showPageByName('pointer-overlay');
chrome.send('coreOptionsUserMetricsAction',
['Options_ShowTouchpadSettings']);
};
}
// Search section.
$('manage-default-search-engines').onclick = function(event) {
PageManager.showPageByName('searchEngines');
chrome.send('coreOptionsUserMetricsAction',
['Options_ManageSearchEngines']);
};
$('default-search-engine').addEventListener('change',
this.setDefaultSearchEngine_);
// Users section.
if (loadTimeData.valueExists('profilesInfo')) {
$('profiles-section').hidden = false;
this.maybeShowUserSection_();
var profilesList = $('profiles-list');
options.browser_options.ProfileList.decorate(profilesList);
profilesList.autoExpands = true;
// The profiles info data in |loadTimeData| might be stale.
this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
chrome.send('requestProfilesInfo');
profilesList.addEventListener('change',
this.setProfileViewButtonsStatus_);
$('profiles-create').onclick = function(event) {
ManageProfileOverlay.showCreateDialog();
};
if (OptionsPage.isSettingsApp()) {
$('profiles-app-list-switch').onclick = function(event) {
var selectedProfile = self.getSelectedProfileItem_();
chrome.send('switchAppListProfile', [selectedProfile.filePath]);
};
}
$('profiles-manage').onclick = function(event) {
ManageProfileOverlay.showManageDialog();
};
$('profiles-delete').onclick = function(event) {
var selectedProfile = self.getSelectedProfileItem_();
if (selectedProfile)
ManageProfileOverlay.showDeleteDialog(selectedProfile);
};
if (loadTimeData.getBoolean('profileIsSupervised')) {
$('profiles-create').disabled = true;
$('profiles-delete').disabled = true;
$('profiles-list').canDeleteItems = false;
}
}
if (cr.isChromeOS) {
// Username (canonical email) of the currently logged in user or
// |kGuestUser| if a guest session is active.
this.username_ = loadTimeData.getString('username');
this.updateAccountPicture_();
$('account-picture').onclick = this.showImagerPickerOverlay_;
$('change-picture-caption').onclick = this.showImagerPickerOverlay_;
$('manage-accounts-button').onclick = function(event) {
PageManager.showPageByName('accounts');
chrome.send('coreOptionsUserMetricsAction',
['Options_ManageAccounts']);
};
} else {
$('import-data').onclick = function(event) {
ImportDataOverlay.show();
chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
};
if ($('themes-native-button')) {
$('themes-native-button').onclick = function(event) {
chrome.send('themesSetNative');
};
}
}
// Date and time section (CrOS only).
if ($('set-time-button'))
$('set-time-button').onclick = this.handleSetTime_.bind(this);
// Default browser section.
if (!cr.isChromeOS) {
if (!loadTimeData.getBoolean('showSetDefault')) {
$('set-default-browser-section').hidden = true;
}
$('set-as-default-browser').onclick = function(event) {
chrome.send('becomeDefaultBrowser');
};
$('auto-launch').onclick = this.handleAutoLaunchChanged_;
}
// Privacy section.
$('privacyContentSettingsButton').onclick = function(event) {
PageManager.showPageByName('content');
OptionsPage.showTab($('cookies-nav-tab'));
chrome.send('coreOptionsUserMetricsAction',
['Options_ContentSettings']);
};
$('privacyClearDataButton').onclick = function(event) {
PageManager.showPageByName('clearBrowserData');
chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
};
$('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
// 'metricsReportingEnabled' element is only present on Chrome branded
// builds, and the 'metricsReportingCheckboxAction' message is only
// handled on ChromeOS.
if ($('metricsReportingEnabled') && cr.isChromeOS) {
$('metricsReportingEnabled').onclick = function(event) {
chrome.send('metricsReportingCheckboxAction',
[String(event.currentTarget.checked)]);
};
}
if ($('metricsReportingEnabled') && !cr.isChromeOS) {
// The localized string has the | symbol on each side of the text that
// needs to be made into a button to restart Chrome. We parse the text
// and build the button from that.
var restartTextFragments =
loadTimeData.getString('metricsReportingResetRestart').split('|');
// Assume structure is something like "starting text |link text| ending
// text" where both starting text and ending text may or may not be
// present, but the split should always be in three pieces.
var restartElements =
$('metrics-reporting-reset-restart').querySelectorAll('*');
for (var i = 0; i < restartTextFragments.length; i++) {
restartElements[i].textContent = restartTextFragments[i];
}
restartElements[1].onclick = function(event) {
chrome.send('restartBrowser');
};
// Attach the listener for updating the checkbox and restart button.
var updateMetricsRestartButton = function() {
$('metrics-reporting-reset-restart').hidden =
loadTimeData.getBoolean('metricsReportingEnabledAtStart') ==
$('metricsReportingEnabled').checked;
};
$('metricsReportingEnabled').onclick = function(event) {
chrome.send('metricsReportingCheckboxChanged',
[Boolean(event.currentTarget.checked)]);
updateMetricsRestartButton();
};
$('metricsReportingEnabled').checked =
loadTimeData.getBoolean('metricsReportingEnabledAtStart');
updateMetricsRestartButton();
}
$('networkPredictionOptions').onchange = function(event) {
var value = (event.target.checked ?
NetworkPredictionOptions.WIFI_ONLY :
NetworkPredictionOptions.NEVER);
var metric = event.target.metric;
Preferences.setIntegerPref(
'net.network_prediction_options',
value,
true,
metric);
};
// Bluetooth (CrOS only).
if (cr.isChromeOS) {
options.system.bluetooth.BluetoothDeviceList.decorate(
$('bluetooth-paired-devices-list'));
$('bluetooth-add-device').onclick =
this.handleAddBluetoothDevice_.bind(this);
$('enable-bluetooth').onchange = function(event) {
var state = $('enable-bluetooth').checked;
chrome.send('bluetoothEnableChange', [Boolean(state)]);
};
$('bluetooth-reconnect-device').onclick = function(event) {
var device = $('bluetooth-paired-devices-list').selectedItem;
var address = device.address;
chrome.send('updateBluetoothDevice', [address, 'connect']);
PageManager.closeOverlay();
};
$('bluetooth-paired-devices-list').addEventListener('change',
function() {
var item = $('bluetooth-paired-devices-list').selectedItem;
var disabled = !item || item.connected || !item.connectable;
$('bluetooth-reconnect-device').disabled = disabled;
});
}
// Passwords and Forms section.
$('autofill-settings').onclick = function(event) {
PageManager.showPageByName('autofill');
chrome.send('coreOptionsUserMetricsAction',
['Options_ShowAutofillSettings']);
};
$('manage-passwords').onclick = function(event) {
PageManager.showPageByName('passwords');
OptionsPage.showTab($('passwords-nav-tab'));
chrome.send('coreOptionsUserMetricsAction',
['Options_ShowPasswordManager']);
};
if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
// Disable and turn off Autofill in guest mode.
var autofillEnabled = $('autofill-enabled');
autofillEnabled.disabled = true;
autofillEnabled.checked = false;
cr.dispatchSimpleEvent(autofillEnabled, 'change');
$('autofill-settings').disabled = true;
// Disable and turn off Password Manager in guest mode.
var passwordManagerEnabled = $('password-manager-enabled');
passwordManagerEnabled.disabled = true;
passwordManagerEnabled.checked = false;
cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
$('manage-passwords').disabled = true;
}
if (cr.isMac) {
$('mac-passwords-warning').hidden =
!loadTimeData.getBoolean('multiple_profiles');
}
// Network section.
if (!cr.isChromeOS) {
$('proxiesConfigureButton').onclick = function(event) {
chrome.send('showNetworkProxySettings');
};
}
// Device control section.
if (cr.isChromeOS &&
UIAccountTweaks.currentUserIsOwner() &&
loadTimeData.getBoolean('consumerManagementEnabled')) {
$('device-control-section').hidden = false;
$('consumer-management-button').onclick = function(event) {
PageManager.showPageByName('consumer-management-overlay');
};
}
// Easy Unlock section.
if (loadTimeData.getBoolean('easyUnlockAllowed')) {
$('easy-unlock-section').hidden = false;
$('easy-unlock-setup-button').onclick = function(event) {
chrome.send('launchEasyUnlockSetup');
};
$('easy-unlock-turn-off-button').onclick = function(event) {
PageManager.showPageByName('easyUnlockTurnOffOverlay');
};
}
// Website Settings section.
if (loadTimeData.getBoolean('websiteSettingsManagerEnabled')) {
$('website-settings-section').hidden = false;
$('website-management-button').onclick = function(event) {
PageManager.showPageByName('websiteSettings');
};
}
// Web Content section.
$('fontSettingsCustomizeFontsButton').onclick = function(event) {
PageManager.showPageByName('fonts');
chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
};
$('defaultFontSize').onchange = function(event) {
var value = event.target.options[event.target.selectedIndex].value;
Preferences.setIntegerPref(
'webkit.webprefs.default_fixed_font_size',
value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
chrome.send('defaultFontSizeAction', [String(value)]);
};
$('defaultZoomFactor').onchange = function(event) {
chrome.send('defaultZoomFactorAction',
[String(event.target.options[event.target.selectedIndex].value)]);
};
// Languages section.
var showLanguageOptions = function(event) {
PageManager.showPageByName('languages');
chrome.send('coreOptionsUserMetricsAction',
['Options_LanuageAndSpellCheckSettings']);
};
$('language-button').onclick = showLanguageOptions;
$('manage-languages').onclick = showLanguageOptions;
// Downloads section.
Preferences.getInstance().addEventListener('download.default_directory',
this.onDefaultDownloadDirectoryChanged_.bind(this));
$('downloadLocationChangeButton').onclick = function(event) {
chrome.send('selectDownloadLocation');
};
if (cr.isChromeOS) {
$('disable-drive-row').hidden =
UIAccountTweaks.loggedInAsSupervisedUser();
}
$('autoOpenFileTypesResetToDefault').onclick = function(event) {
chrome.send('autoOpenFileTypesAction');
};
// HTTPS/SSL section.
if (cr.isWindows || cr.isMac) {
$('certificatesManageButton').onclick = function(event) {
chrome.send('showManageSSLCertificates');
};
} else {
$('certificatesManageButton').onclick = function(event) {
PageManager.showPageByName('certificates');
chrome.send('coreOptionsUserMetricsAction',
['Options_ManageSSLCertificates']);
};
}
if (loadTimeData.getBoolean('cloudPrintShowMDnsOptions')) {
$('cloudprint-options-mdns').hidden = false;
$('cloudPrintDevicesPageButton').onclick = function() {
chrome.send('showCloudPrintDevicesPage');
};
}
// Accessibility section (CrOS only).
if (cr.isChromeOS) {
var updateAccessibilitySettingsButton = function() {
$('accessibility-settings').hidden =
!($('accessibility-spoken-feedback-check').checked);
};
Preferences.getInstance().addEventListener(
'settings.accessibility',
updateAccessibilitySettingsButton);
$('accessibility-learn-more').onclick = function(unused_event) {
window.open(loadTimeData.getString('accessibilityLearnMoreURL'));
chrome.send('coreOptionsUserMetricsAction',
['Options_AccessibilityLearnMore']);
};
$('accessibility-settings-button').onclick = function(unused_event) {
window.open(loadTimeData.getString('accessibilitySettingsURL'));
};
$('accessibility-spoken-feedback-check').onchange = function(
unused_event) {
chrome.send('spokenFeedbackChange',
[$('accessibility-spoken-feedback-check').checked]);
updateAccessibilitySettingsButton();
};
updateAccessibilitySettingsButton();
$('accessibility-high-contrast-check').onchange = function(
unused_event) {
chrome.send('highContrastChange',
[$('accessibility-high-contrast-check').checked]);
};
var updateDelayDropdown = function() {
$('accessibility-autoclick-dropdown').disabled =
!$('accessibility-autoclick-check').checked;
};
Preferences.getInstance().addEventListener(
$('accessibility-autoclick-check').getAttribute('pref'),
updateDelayDropdown);
}
// Display management section (CrOS only).
if (cr.isChromeOS) {
$('display-options').onclick = function(event) {
PageManager.showPageByName('display');
chrome.send('coreOptionsUserMetricsAction',
['Options_Display']);
};
}
// Factory reset section (CrOS only).
if (cr.isChromeOS) {
$('factory-reset-restart').onclick = function(event) {
PageManager.showPageByName('factoryResetData');
chrome.send('onPowerwashDialogShow');
};
}
// System section.
if (!cr.isChromeOS) {
var updateGpuRestartButton = function() {
$('gpu-mode-reset-restart').hidden =
loadTimeData.getBoolean('gpuEnabledAtStart') ==
$('gpu-mode-checkbox').checked;
};
Preferences.getInstance().addEventListener(
$('gpu-mode-checkbox').getAttribute('pref'),
updateGpuRestartButton);
$('gpu-mode-reset-restart-button').onclick = function(event) {
chrome.send('restartBrowser');
};
updateGpuRestartButton();
}
// Reset profile settings section.
$('reset-profile-settings').onclick = function(event) {
PageManager.showPageByName('resetProfileSettings');
};
// Extension controlled UI.
this.addExtensionControlledBox_('search-section-content',
'search-engine-controlled',
true);
this.addExtensionControlledBox_('extension-controlled-container',
'homepage-controlled',
true);
this.addExtensionControlledBox_('startup-section-content',
'startpage-controlled',
false);
this.addExtensionControlledBox_('newtab-section-content',
'newtab-controlled',
false);
this.addExtensionControlledBox_('proxy-section-content',
'proxy-controlled',
true);
document.body.addEventListener('click', function(e) {
var target = assertInstanceof(e.target, Node);
var button = findAncestor(target, function(el) {
return el.tagName == 'BUTTON' &&
el.dataset.extensionId !== undefined &&
el.dataset.extensionId.length;
});
if (button)
chrome.send('disableExtension', [button.dataset.extensionId]);
});
},
/** @override */
didShowPage: function() {
$('search-field').focus();
},
/**
* Called after all C++ UI handlers have called InitializePage to notify
* that initialization is complete.
* @private
*/
notifyInitializationComplete_: function() {
this.initializationComplete_ = true;
cr.dispatchSimpleEvent(document, 'initializationComplete');
},
/**
* Event listener for the 'session.restore_on_startup' pref.
* @param {Event} event The preference change event.
* @private
*/
onRestoreOnStartupChanged_: function(event) {
/** @const */ var showHomePageValue = 0;
if (event.value.value == showHomePageValue) {
// If the user previously selected "Show the homepage", the
// preference will already be migrated to "Open a specific page". So
// the only way to reach this code is if the 'restore on startup'
// preference is managed.
assert(event.value.controlledBy);
// Select "open the following pages" and lock down the list of URLs
// to reflect the intention of the policy.
$('startup-show-pages').checked = true;
StartupOverlay.getInstance().setControlsDisabled(true);
} else {
// Re-enable the controls in the startup overlay if necessary.
StartupOverlay.getInstance().updateControlStates();
}
},
/**
* Handler for messages sent from the main uber page.
* @param {Event} e The 'message' event from the uber page.
* @private
*/
handleWindowMessage_: function(e) {
if ((/** @type {{method: string}} */(e.data)).method == 'frameSelected')
$('search-field').focus();
},
/**
* Animatedly changes height |from| a px number |to| a px number.
* @param {HTMLElement} section The section to animate.
* @param {HTMLElement} container The container of |section|.
* @param {boolean} showing Whether to go from 0 -> container height or
* container height -> 0.
* @private
*/
animatedSectionHeightChange_: function(section, container, showing) {
// If the section is already animating, dispatch a synthetic transition
// end event as the upcoming code will cancel the current one.
if (section.classList.contains('sliding'))
cr.dispatchSimpleEvent(section, 'webkitTransitionEnd');
this.addTransitionEndListener_(section);
section.hidden = false;
section.style.height = (showing ? 0 : container.offsetHeight) + 'px';
section.classList.add('sliding');
// Force a style recalc before starting the animation.
/** @suppress {suspiciousCode} */
section.offsetHeight;
section.style.height = (showing ? container.offsetHeight : 0) + 'px';
},
/**
* Shows the given section.
* @param {HTMLElement} section The section to be shown.
* @param {HTMLElement} container The container for the section. Must be
* inside of |section|.
* @param {boolean} animate Indicate if the expansion should be animated.
* @private
*/
showSection_: function(section, container, animate) {
// Delay starting the transition if animating so that hidden change will
// be processed.
if (animate) {
this.animatedSectionHeightChange_(section, container, true);
} else {
section.hidden = false;
section.style.height = 'auto';
}
},
/**
* Shows the given section, with animation.
* @param {HTMLElement} section The section to be shown.
* @param {HTMLElement} container The container for the section. Must be
* inside of |section|.
* @private
*/
showSectionWithAnimation_: function(section, container) {
this.showSection_(section, container, /* animate */ true);
},
/**
* Hides the given |section| with animation.
* @param {HTMLElement} section The section to be hidden.
* @param {HTMLElement} container The container for the section. Must be
* inside of |section|.
* @private
*/
hideSectionWithAnimation_: function(section, container) {
this.animatedSectionHeightChange_(section, container, false);
},
/**
* Toggles the visibility of |section| in an animated way.
* @param {HTMLElement} section The section to be toggled.
* @param {HTMLElement} container The container for the section. Must be
* inside of |section|.
* @private
*/
toggleSectionWithAnimation_: function(section, container) {
if (BrowserOptions.shouldShowSection_(section))
this.showSectionWithAnimation_(section, container);
else
this.hideSectionWithAnimation_(section, container);
},
/**
* Scrolls the settings page to make the section visible auto-expanding
* advanced settings if required. The transition is not animated. This
* method is used to ensure that a section associated with an overlay
* is visible when the overlay is closed.
* @param {!Element} section The section to make visible.
* @private
*/
scrollToSection_: function(section) {
var advancedSettings = $('advanced-settings');
var container = $('advanced-settings-container');
var expander = $('advanced-settings-expander');
if (!expander.hidden &&
advancedSettings.hidden &&
section.parentNode == container) {
this.showSection_($('advanced-settings'),
$('advanced-settings-container'),
/* animate */ false);
this.updateAdvancedSettingsExpander_();
}
if (!this.initializationComplete_) {
var self = this;
var callback = function() {
document.removeEventListener('initializationComplete', callback);
self.scrollToSection_(section);
};
document.addEventListener('initializationComplete', callback);
return;
}
var pageContainer = $('page-container');
// pageContainer.offsetTop is relative to the screen.
var pageTop = pageContainer.offsetTop;
var sectionBottom = section.offsetTop + section.offsetHeight;
// section.offsetTop is relative to the 'page-container'.
var sectionTop = section.offsetTop;
if (pageTop + sectionBottom > document.body.scrollHeight ||
pageTop + sectionTop < 0) {
// Currently not all layout updates are guaranteed to precede the
// initializationComplete event (for example 'set-as-default-browser'
// button) leaving some uncertainty in the optimal scroll position.
// The section is placed approximately in the middle of the screen.
var top = Math.min(0, document.body.scrollHeight / 2 - sectionBottom);
pageContainer.style.top = top + 'px';
pageContainer.oldScrollTop = -top;
}
},
/**
* Adds a |webkitTransitionEnd| listener to the given section so that
* it can be animated. The listener will only be added to a given section
* once, so this can be called as multiple times.
* @param {HTMLElement} section The section to be animated.
* @private
*/
addTransitionEndListener_: function(section) {
if (section.hasTransitionEndListener_)
return;
section.addEventListener('webkitTransitionEnd',
this.onTransitionEnd_.bind(this));
section.hasTransitionEndListener_ = true;
},
/**
* Called after an animation transition has ended.
* @param {Event} event The webkitTransitionEnd event. NOTE: May be
* synthetic.
* @private
*/
onTransitionEnd_: function(event) {
if (event.propertyName && event.propertyName != 'height') {
// If not a synthetic event or a real transition we care about, bail.
return;
}
var section = event.target;
section.classList.remove('sliding');
if (!event.propertyName) {
// Only real transitions past this point.
return;
}
if (section.style.height == '0px') {
// Hide the content so it can't get tab focus.
section.hidden = true;
section.style.height = '';
} else {
// Set the section height to 'auto' to allow for size changes
// (due to font change or dynamic content).
section.style.height = 'auto';
}
},
/** @private */
updateAdvancedSettingsExpander_: function() {
var expander = $('advanced-settings-expander');
if (BrowserOptions.shouldShowSection_($('advanced-settings')))
expander.textContent = loadTimeData.getString('showAdvancedSettings');
else
expander.textContent = loadTimeData.getString('hideAdvancedSettings');
},
/**
* Updates the sync section with the given state.
* @param {options.SyncStatus} syncData A bunch of data records that
* describe the status of the sync system.
* @private
*/
updateSyncState_: function(syncData) {
if (!syncData.signinAllowed &&
(!syncData.supervisedUser || !cr.isChromeOS)) {
$('sync-section').hidden = true;
this.maybeShowUserSection_();
return;
}
$('sync-section').hidden = false;
this.maybeShowUserSection_();
if (cr.isChromeOS && syncData.supervisedUser) {
var subSection = $('sync-section').firstChild;
while (subSection) {
if (subSection.nodeType == Node.ELEMENT_NODE)
subSection.hidden = true;
subSection = subSection.nextSibling;
}
$('account-picture-wrapper').hidden = false;
$('sync-general').hidden = false;
$('sync-status').hidden = true;
return;
}
// If the user gets signed out while the advanced sync settings dialog is
// visible, say, due to a dashboard clear, close the dialog.
// However, if the user gets signed out as a result of abandoning first
// time sync setup, do not call closeOverlay as it will redirect the
// browser to the main settings page and override any in-progress
// user-initiated navigation. See crbug.com/278030.
// Note: SyncSetupOverlay.closeOverlay is a no-op if the overlay is
// already hidden.
if (this.signedIn_ && !syncData.signedIn && !syncData.setupInProgress)
SyncSetupOverlay.closeOverlay();
this.signedIn_ = !!syncData.signedIn;
// Display the "advanced settings" button if we're signed in and sync is
// not managed/disabled. If the user is signed in, but sync is disabled,
// this button is used to re-enable sync.
var customizeSyncButton = $('customize-sync');
customizeSyncButton.hidden = !this.signedIn_ ||
syncData.managed ||
!syncData.syncSystemEnabled;
// Only modify the customize button's text if the new text is different.
// Otherwise, it can affect search-highlighting in the settings page.
// See http://crbug.com/268265.
var customizeSyncButtonNewText = syncData.setupCompleted ?
loadTimeData.getString('customizeSync') :
loadTimeData.getString('syncButtonTextStart');
if (customizeSyncButton.textContent != customizeSyncButtonNewText)
customizeSyncButton.textContent = customizeSyncButtonNewText;
// Disable the "sign in" button if we're currently signing in, or if we're
// already signed in and signout is not allowed.
var signInButton = $('start-stop-sync');
signInButton.disabled = syncData.setupInProgress;
this.signoutAllowed_ = !!syncData.signoutAllowed;
if (!syncData.signoutAllowed)
$('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
else
$('start-stop-sync-indicator').removeAttribute('controlled-by');
// Hide the "sign in" button on Chrome OS, and show it on desktop Chrome
// (except for supervised users, which can't change their signed-in
// status).
signInButton.hidden = cr.isChromeOS || syncData.supervisedUser;
signInButton.textContent =
this.signedIn_ ?
loadTimeData.getString('syncButtonTextStop') :
syncData.setupInProgress ?
loadTimeData.getString('syncButtonTextInProgress') :
loadTimeData.getString('syncButtonTextSignIn');
$('start-stop-sync-indicator').hidden = signInButton.hidden;
// TODO(estade): can this just be textContent?
$('sync-status-text').innerHTML = syncData.statusText;
var statusSet = syncData.statusText.length != 0;
$('sync-overview').hidden =
statusSet ||
(cr.isChromeOS && UIAccountTweaks.loggedInAsPublicAccount());
$('sync-status').hidden = !statusSet;
$('sync-action-link').textContent = syncData.actionLinkText;
// Don't show the action link if it is empty or undefined.
$('sync-action-link').hidden = syncData.actionLinkText.length == 0;
$('sync-action-link').disabled = syncData.managed ||
!syncData.syncSystemEnabled;
// On Chrome OS, sign out the user and sign in again to get fresh
// credentials on auth errors.
$('sync-action-link').onclick = function(event) {
if (cr.isChromeOS && syncData.hasError)
SyncSetupOverlay.doSignOutOnAuthError();
else
SyncSetupOverlay.showSetupUI();
};
if (syncData.hasError)
$('sync-status').classList.add('sync-error');
else
$('sync-status').classList.remove('sync-error');
// Disable the "customize / set up sync" button if sync has an
// unrecoverable error. Also disable the button if sync has not been set
// up and the user is being presented with a link to re-auth.
// See crbug.com/289791.
customizeSyncButton.disabled =
syncData.hasUnrecoverableError ||
(!syncData.setupCompleted && !$('sync-action-link').hidden);
},
/**
* Update the UI depending on whether the current profile has a pairing for
* Easy Unlock.
* @param {boolean} hasPairing True if the current profile has a pairing.
*/
updateEasyUnlock_: function(hasPairing) {
$('easy-unlock-setup').hidden = hasPairing;
$('easy-unlock-enable').hidden = !hasPairing;
if (!hasPairing && EasyUnlockTurnOffOverlay.getInstance().visible) {
EasyUnlockTurnOffOverlay.dismiss();
}
},
/**
* Update the UI depending on whether the current profile manages any
* supervised users.
* @param {boolean} show True if the current profile manages any supervised
* users.
*/
updateManagesSupervisedUsers_: function(show) {
$('profiles-supervised-dashboard-tip').hidden = !show;
this.maybeShowUserSection_();
},
/**
* Get the start/stop sync button DOM element. Used for testing.
* @return {Element} The start/stop sync button.
* @private
*/
getStartStopSyncButton_: function() {
return $('start-stop-sync');
},
/**
* Event listener for the 'show home button' preference. Shows/hides the
* UI for changing the home page with animation, unless this is the first
* time this function is called, in which case there is no animation.
* @param {Event} event The preference change event.
*/
onShowHomeButtonChanged_: function(event) {
var section = $('change-home-page-section');
if (this.onShowHomeButtonChangedCalled_) {
var container = $('change-home-page-section-container');
if (event.value.value)
this.showSectionWithAnimation_(section, container);
else
this.hideSectionWithAnimation_(section, container);
} else {
section.hidden = !event.value.value;
this.onShowHomeButtonChangedCalled_ = true;
}
},
/**
* Activates the Hotword section from the System settings page.
* @param {boolean} opt_enabled Current preference state for hotwording.
* @param {string} opt_error The error message to display.
* @private
*/
showHotwordSection_: function(opt_enabled, opt_error) {
$('hotword-search').hidden = false;
$('hotword-search-setting-indicator').setError(opt_error);
if (opt_enabled && opt_error)
$('hotword-search-setting-indicator').updateBasedOnError();
},
/**
* Activates the Audio History and Always-On Hotword sections from the
* System settings page.
* @private
*/
showHotwordAlwaysOnSection_: function() {
$('voice-section-title').hidden = false;
$('hotword-always-on-search').hidden = false;
$('audio-logging').hidden = false;
},
/**
* Event listener for the 'homepage is NTP' preference. Updates the label
* next to the 'Change' button.
* @param {Event} event The preference change event.
*/
onHomePageIsNtpChanged_: function(event) {
if (!event.value.uncommitted) {
$('home-page-url').hidden = event.value.value;
$('home-page-ntp').hidden = !event.value.value;
}
},
/**
* Event listener for changes to the homepage preference. Updates the label
* next to the 'Change' button.
* @param {Event} event The preference change event.
*/
onHomePageChanged_: function(event) {
if (!event.value.uncommitted)
$('home-page-url').textContent = this.stripHttp_(event.value.value);
},
/**
* Removes the 'http://' from a URL, like the omnibox does. If the string
* doesn't start with 'http://' it is returned unchanged.
* @param {string} url The url to be processed
* @return {string} The url with the 'http://' removed.
*/
stripHttp_: function(url) {
return url.replace(/^http:\/\//, '');
},
/**
* Shows the autoLaunch preference and initializes its checkbox value.
* @param {boolean} enabled Whether autolaunch is enabled or or not.
* @private
*/
updateAutoLaunchState_: function(enabled) {
$('auto-launch-option').hidden = false;
$('auto-launch').checked = enabled;
},
/**
* Called when the value of the download.default_directory preference
* changes.
* @param {Event} event Change event.
* @private
*/
onDefaultDownloadDirectoryChanged_: function(event) {
$('downloadLocationPath').value = event.value.value;
if (cr.isChromeOS) {
// On ChromeOS, replace /special/drive-<hash>/root with "Google Drive"
// for remote files, /home/chronos/user/Downloads or
// /home/chronos/u-<hash>/Downloads with "Downloads" for local paths,
// and '/' with ' \u203a ' (angled quote sign) everywhere. The modified
// path is used only for display purpose.
var path = $('downloadLocationPath').value;
path = path.replace(/^\/special\/drive[^\/]*\/root/, 'Google Drive');
path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, '');
path = path.replace(/\//g, ' \u203a ');
$('downloadLocationPath').value = path;
}
$('download-location-label').classList.toggle('disabled',
event.value.disabled);
$('downloadLocationChangeButton').disabled = event.value.disabled;
},
/**
* Update the Default Browsers section based on the current state.
* @param {string} statusString Description of the current default state.
* @param {boolean} isDefault Whether or not the browser is currently
* default.
* @param {boolean} canBeDefault Whether or not the browser can be default.
* @private
*/
updateDefaultBrowserState_: function(statusString, isDefault,
canBeDefault) {
if (!cr.isChromeOS) {
var label = $('default-browser-state');
label.textContent = statusString;
$('set-as-default-browser').hidden = !canBeDefault || isDefault;
}
},
/**
* Clears the search engine popup.
* @private
*/
clearSearchEngines_: function() {
$('default-search-engine').textContent = '';
},
/**
* Updates the search engine popup with the given entries.
* @param {Array} engines List of available search engines.
* @param {number} defaultValue The value of the current default engine.
* @param {boolean} defaultManaged Whether the default search provider is
* managed. If true, the default search provider can't be changed.
* @private
*/
updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
this.clearSearchEngines_();
var engineSelect = $('default-search-engine');
engineSelect.disabled = defaultManaged;
if (defaultManaged && defaultValue == -1)
return;
var engineCount = engines.length;
var defaultIndex = -1;
for (var i = 0; i < engineCount; i++) {
var engine = engines[i];
var option = new Option(engine.name, engine.index);
if (defaultValue == option.value)
defaultIndex = i;
engineSelect.appendChild(option);
}
if (defaultIndex >= 0)
engineSelect.selectedIndex = defaultIndex;
},
/**
* Set the default search engine based on the popup selection.
* @private
*/
setDefaultSearchEngine_: function() {
var engineSelect = $('default-search-engine');
var selectedIndex = engineSelect.selectedIndex;
if (selectedIndex >= 0) {
var selection = engineSelect.options[selectedIndex];
chrome.send('setDefaultSearchEngine', [String(selection.value)]);
}
},
/**
* Sets or clear whether Chrome should Auto-launch on computer startup.
* @private
*/
handleAutoLaunchChanged_: function() {
chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
},
/**
* Get the selected profile item from the profile list. This also works
* correctly if the list is not displayed.
* @return {?Object} The profile item object, or null if nothing is
* selected.
* @private
*/
getSelectedProfileItem_: function() {
var profilesList = $('profiles-list');
if (profilesList.hidden) {
if (profilesList.dataModel.length > 0)
return profilesList.dataModel.item(0);
} else {
return profilesList.selectedItem;
}
return null;
},
/**
* Helper function to set the status of profile view buttons to disabled or
* enabled, depending on the number of profiles and selection status of the
* profiles list.
* @private
*/
setProfileViewButtonsStatus_: function() {
var profilesList = $('profiles-list');
var selectedProfile = profilesList.selectedItem;
var hasSelection = selectedProfile != null;
var hasSingleProfile = profilesList.dataModel.length == 1;
var isSupervised = loadTimeData.getBoolean('profileIsSupervised');
$('profiles-manage').disabled = !hasSelection ||
!selectedProfile.isCurrentProfile;
if (hasSelection && !selectedProfile.isCurrentProfile)
$('profiles-manage').title = loadTimeData.getString('currentUserOnly');
else
$('profiles-manage').title = '';
$('profiles-delete').disabled = isSupervised ||
(!hasSelection && !hasSingleProfile);
if (OptionsPage.isSettingsApp()) {
$('profiles-app-list-switch').disabled = !hasSelection ||
selectedProfile.isCurrentProfile;
}
var importData = $('import-data');
if (importData) {
importData.disabled = $('import-data').disabled = hasSelection &&
!selectedProfile.isCurrentProfile;
}
},
/**
* Display the correct dialog layout, depending on how many profiles are
* available.
* @param {number} numProfiles The number of profiles to display.
* @private
*/
setProfileViewSingle_: function(numProfiles) {
// Always show the profiles list when using the new Profiles UI.
var usingNewProfilesUI = loadTimeData.getBoolean('usingNewProfilesUI');
var showSingleProfileView = !usingNewProfilesUI && numProfiles == 1;
$('profiles-list').hidden = showSingleProfileView;
$('profiles-single-message').hidden = !showSingleProfileView;
$('profiles-manage').hidden =
showSingleProfileView || OptionsPage.isSettingsApp();
$('profiles-delete').textContent = showSingleProfileView ?
loadTimeData.getString('profilesDeleteSingle') :
loadTimeData.getString('profilesDelete');
if (OptionsPage.isSettingsApp())
$('profiles-app-list-switch').hidden = showSingleProfileView;
},
/**
* Adds all |profiles| to the list.
* @param {Array.<{name: string, filePath: string,
* isCurrentProfile: boolean, isSupervised: boolean}>} profiles An array
* of profile info objects.
* @private
*/
setProfilesInfo_: function(profiles) {
this.setProfileViewSingle_(profiles.length);
// add it to the list, even if the list is hidden so we can access it
// later.
$('profiles-list').dataModel = new ArrayDataModel(profiles);
// Received new data. If showing the "manage" overlay, keep it up to
// date. If showing the "delete" overlay, close it.
if (ManageProfileOverlay.getInstance().visible &&
!$('manage-profile-overlay-manage').hidden) {
ManageProfileOverlay.showManageDialog();
} else {
ManageProfileOverlay.getInstance().visible = false;
}
this.setProfileViewButtonsStatus_();
},
/**
* Reports supervised user import errors to the SupervisedUserImportOverlay.
* @param {string} error The error message to display.
* @private
*/
showSupervisedUserImportError_: function(error) {
SupervisedUserImportOverlay.onError(error);
},
/**
* Reports successful importing of a supervised user to
* the SupervisedUserImportOverlay.
* @private
*/
showSupervisedUserImportSuccess_: function() {
SupervisedUserImportOverlay.onSuccess();
},
/**
* Reports an error to the "create" overlay during profile creation.
* @param {string} error The error message to display.
* @private
*/
showCreateProfileError_: function(error) {
CreateProfileOverlay.onError(error);
},
/**
* Sends a warning message to the "create" overlay during profile creation.
* @param {string} warning The warning message to display.
* @private
*/
showCreateProfileWarning_: function(warning) {
CreateProfileOverlay.onWarning(warning);
},
/**
* Reports successful profile creation to the "create" overlay.
* @param {Object} profileInfo An object of the form:
* profileInfo = {
* name: "Profile Name",
* filePath: "/path/to/profile/data/on/disk"
* isSupervised: (true|false),
* };
* @private
*/
showCreateProfileSuccess_: function(profileInfo) {
CreateProfileOverlay.onSuccess(profileInfo);
},
/**
* Returns the currently active profile for this browser window.
* @return {Object} A profile info object.
* @private
*/
getCurrentProfile_: function() {
for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
var profile = $('profiles-list').dataModel.item(i);
if (profile.isCurrentProfile)
return profile;
}
assertNotReached('There should always be a current profile.');
},
/**
* Propmpts user to confirm deletion of the profile for this browser
* window.
* @private
*/
deleteCurrentProfile_: function() {
ManageProfileOverlay.showDeleteDialog(this.getCurrentProfile_());
},
/**
* @param {boolean} enabled
*/
setNativeThemeButtonEnabled_: function(enabled) {
var button = $('themes-native-button');
if (button)
button.disabled = !enabled;
},
/**
* @param {boolean} enabled
*/
setThemesResetButtonEnabled_: function(enabled) {
$('themes-reset').disabled = !enabled;
},
/**
* @param {boolean} managed
*/
setAccountPictureManaged_: function(managed) {
var picture = $('account-picture');
if (managed || UIAccountTweaks.loggedInAsGuest()) {
picture.disabled = true;
ChangePictureOptions.closeOverlay();
} else {
picture.disabled = false;
}
// Create a synthetic pref change event decorated as
// CoreOptionsHandler::CreateValueForPref() does.
var event = new Event('account-picture');
if (managed)
event.value = { controlledBy: 'policy' };
else
event.value = {};
$('account-picture-indicator').handlePrefChange(event);
},
/**
* (Re)loads IMG element with current user account picture.
* @private
*/
updateAccountPicture_: function() {
var picture = $('account-picture');
if (picture) {
picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
Date.now();
}
},
/**
* @param {boolean} managed
*/
setWallpaperManaged_: function(managed) {
if (managed)
$('set-wallpaper').disabled = true;
else
this.enableElementIfPossible_(getRequiredElement('set-wallpaper'));
// Create a synthetic pref change event decorated as
// CoreOptionsHandler::CreateValueForPref() does.
var event = new Event('wallpaper');
event.value = managed ? { controlledBy: 'policy' } : {};
$('wallpaper-indicator').handlePrefChange(event);
},
/**
* Handle the 'add device' button click.
* @private
*/
handleAddBluetoothDevice_: function() {
chrome.send('findBluetoothDevices');
PageManager.showPageByName('bluetooth', false);
},
/**
* Enables or disables the Manage SSL Certificates button.
* @private
*/
enableCertificateButton_: function(enabled) {
$('certificatesManageButton').disabled = !enabled;
},
/**
* Enables or disables the ChromeOS display settings button.
* @private
*/
enableDisplayButton_: function(enabled) {
if (cr.isChromeOS)
$('display-options').disabled = !enabled;
},
/**
* Enables factory reset section.
* @private
*/
enableFactoryResetSection_: function() {
$('factory-reset-section').hidden = false;
},
/**
* Set the checked state of the metrics reporting checkbox.
* @private
*/
setMetricsReportingCheckboxState_: function(checked, disabled) {
$('metricsReportingEnabled').checked = checked;
$('metricsReportingEnabled').disabled = disabled;
// If checkbox gets disabled then add an attribute for displaying the
// special icon. The opposite shouldn't be possible to do.
if (disabled) {
$('metrics-reporting-disabled-icon').setAttribute('controlled-by',
'policy');
}
},
/**
* @private
*/
setMetricsReportingSettingVisibility_: function(visible) {
if (visible)
$('metricsReportingSetting').style.display = 'block';
else
$('metricsReportingSetting').style.display = 'none';
},
/**
* Set network prediction checkbox value.
*
* @param {{value: number, disabled: boolean}} pref Information about
* network prediction options. |pref.value| is the value of network
* prediction options. |pref.disabled| shows if the pref is not user
* modifiable.
* @private
*/
setNetworkPredictionValue_: function(pref) {
var checkbox = $('networkPredictionOptions');
checkbox.disabled = pref.disabled;
if (pref.value == NetworkPredictionOptions.UNSET) {
checkbox.checked = (NetworkPredictionOptions.DEFAULT !=
NetworkPredictionOptions.NEVER);
} else {
checkbox.checked = (pref.value != NetworkPredictionOptions.NEVER);
}
},
/**
* Set the font size selected item. This item actually reflects two
* preferences: the default font size and the default fixed font size.
*
* @param {{value: number, disabled: boolean, controlledBy: string}} pref
* Information about the font size preferences. |pref.value| is the
* value of the default font size pref. |pref.disabled| is true if
* either pref not user modifiable. |pref.controlledBy| is the source of
* the pref value(s) if either pref is currently not controlled by the
* user.
* @private
*/
setFontSize_: function(pref) {
var selectCtl = $('defaultFontSize');
selectCtl.disabled = pref.disabled;
// Create a synthetic pref change event decorated as
// CoreOptionsHandler::CreateValueForPref() does.
var event = new Event('synthetic-font-size');
event.value = {
value: pref.value,
controlledBy: pref.controlledBy,
disabled: pref.disabled
};
$('font-size-indicator').handlePrefChange(event);
for (var i = 0; i < selectCtl.options.length; i++) {
if (selectCtl.options[i].value == pref.value) {
selectCtl.selectedIndex = i;
if ($('Custom'))
selectCtl.remove($('Custom').index);
return;
}
}
// Add/Select Custom Option in the font size label list.
if (!$('Custom')) {
var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
-1, false, true);
option.setAttribute('id', 'Custom');
selectCtl.add(option);
}
$('Custom').selected = true;
},
/**
* Populate the page zoom selector with values received from the caller.
* @param {Array} items An array of items to populate the selector.
* each object is an array with three elements as follows:
* 0: The title of the item (string).
* 1: The value of the item (number).
* 2: Whether the item should be selected (boolean).
* @private
*/
setupPageZoomSelector_: function(items) {
var element = $('defaultZoomFactor');
// Remove any existing content.
element.textContent = '';
// Insert new child nodes into select element.
var value, title, selected;
for (var i = 0; i < items.length; i++) {
title = items[i][0];
value = items[i][1];
selected = items[i][2];
element.appendChild(new Option(title, value, false, selected));
}
},
/**
* Shows/hides the autoOpenFileTypesResetToDefault button and label, with
* animation.
* @param {boolean} display Whether to show the button and label or not.
* @private
*/
setAutoOpenFileTypesDisplayed_: function(display) {
if ($('advanced-settings').hidden) {
// If the Advanced section is hidden, don't animate the transition.
$('auto-open-file-types-section').hidden = !display;
} else {
if (display) {
this.showSectionWithAnimation_(
$('auto-open-file-types-section'),
$('auto-open-file-types-container'));
} else {
this.hideSectionWithAnimation_(
$('auto-open-file-types-section'),
$('auto-open-file-types-container'));
}
}
},
/**
* Set the enabled state for the proxy settings button and its associated
* message when extension controlled.
* @param {boolean} disabled Whether the button should be disabled.
* @param {boolean} extensionControlled Whether the proxy is extension
* controlled.
* @private
*/
setupProxySettingsButton_: function(disabled, extensionControlled) {
if (!cr.isChromeOS) {
$('proxiesConfigureButton').disabled = disabled;
$('proxiesLabel').textContent =
loadTimeData.getString(extensionControlled ?
'proxiesLabelExtension' : 'proxiesLabelSystem');
}
},
/**
* Set the initial state of the spoken feedback checkbox.
* @private
*/
setSpokenFeedbackCheckboxState_: function(checked) {
$('accessibility-spoken-feedback-check').checked = checked;
},
/**
* Set the initial state of the high contrast checkbox.
* @private
*/
setHighContrastCheckboxState_: function(checked) {
$('accessibility-high-contrast-check').checked = checked;
},
/**
* Set the initial state of the virtual keyboard checkbox.
* @private
*/
setVirtualKeyboardCheckboxState_: function(checked) {
// TODO(zork): Update UI
},
/**
* Show/hide mouse settings slider.
* @private
*/
showMouseControls_: function(show) {
$('mouse-settings').hidden = !show;
},
/**
* Adds hidden warning boxes for settings potentially controlled by
* extensions.
* @param {string} parentDiv The div name to append the bubble to.
* @param {string} bubbleId The ID to use for the bubble.
* @param {boolean} first Add as first node if true, otherwise last.
* @private
*/
addExtensionControlledBox_: function(parentDiv, bubbleId, first) {
var bubble = $('extension-controlled-warning-template').cloneNode(true);
bubble.id = bubbleId;
var parent = $(parentDiv);
if (first)
parent.insertBefore(bubble, parent.firstChild);
else
parent.appendChild(bubble);
},
/**
* Adds a bubble showing that an extension is controlling a particular
* setting.
* @param {string} parentDiv The div name to append the bubble to.
* @param {string} bubbleId The ID to use for the bubble.
* @param {string} extensionId The ID of the controlling extension.
* @param {string} extensionName The name of the controlling extension.
* @private
*/
toggleExtensionControlledBox_: function(
parentDiv, bubbleId, extensionId, extensionName) {
var bubble = $(bubbleId);
assert(bubble);
bubble.hidden = extensionId.length == 0;
if (bubble.hidden)
return;
// Set the extension image.
var div = bubble.firstElementChild;
div.style.backgroundImage =
'url(chrome://extension-icon/' + extensionId + '/24/1)';
// Set the bubble label.
var label = loadTimeData.getStringF('extensionControlled', extensionName);
var docFrag = parseHtmlSubset('<div>' + label + '</div>', ['B', 'DIV']);
div.innerHTML = docFrag.firstChild.innerHTML;
// Wire up the button to disable the right extension.
var button = div.nextElementSibling;
button.dataset.extensionId = extensionId;
},
/**
* Toggles the warning boxes that show which extension is controlling
* various settings of Chrome.
* @param {{searchEngine: options.ExtensionData,
* homePage: options.ExtensionData,
* startUpPage: options.ExtensionData,
* newTabPage: options.ExtensionData,
* proxy: options.ExtensionData}} details A dictionary of ID+name
* pairs for each of the settings controlled by an extension.
* @private
*/
toggleExtensionIndicators_: function(details) {
this.toggleExtensionControlledBox_('search-section-content',
'search-engine-controlled',
details.searchEngine.id,
details.searchEngine.name);
this.toggleExtensionControlledBox_('extension-controlled-container',
'homepage-controlled',
details.homePage.id,
details.homePage.name);
this.toggleExtensionControlledBox_('startup-section-content',
'startpage-controlled',
details.startUpPage.id,
details.startUpPage.name);
this.toggleExtensionControlledBox_('newtab-section-content',
'newtab-controlled',
details.newTabPage.id,
details.newTabPage.name);
this.toggleExtensionControlledBox_('proxy-section-content',
'proxy-controlled',
details.proxy.id,
details.proxy.name);
// The proxy section contains just the warning box and nothing else, so
// if we're hiding the proxy warning box, we should also hide its header
// section.
$('proxy-section').hidden = details.proxy.id.length == 0;
},
/**
* Show/hide touchpad-related settings.
* @private
*/
showTouchpadControls_: function(show) {
$('touchpad-settings').hidden = !show;
$('accessibility-tap-dragging').hidden = !show;
},
/**
* Activate the Bluetooth settings section on the System settings page.
* @private
*/
showBluetoothSettings_: function() {
$('bluetooth-devices').hidden = false;
},
/**
* Dectivates the Bluetooth settings section from the System settings page.
* @private
*/
hideBluetoothSettings_: function() {
$('bluetooth-devices').hidden = true;
},
/**
* Sets the state of the checkbox indicating if Bluetooth is turned on. The
* state of the "Find devices" button and the list of discovered devices may
* also be affected by a change to the state.
* @param {boolean} checked Flag Indicating if Bluetooth is turned on.
* @private
*/
setBluetoothState_: function(checked) {
$('enable-bluetooth').checked = checked;
$('bluetooth-paired-devices-list').parentNode.hidden = !checked;
$('bluetooth-add-device').hidden = !checked;
$('bluetooth-reconnect-device').hidden = !checked;
// Flush list of previously discovered devices if bluetooth is turned off.
if (!checked) {
$('bluetooth-paired-devices-list').clear();
$('bluetooth-unpaired-devices-list').clear();
} else {
chrome.send('getPairedBluetoothDevices');
}
},
/**
* Adds an element to the list of available Bluetooth devices. If an element
* with a matching address is found, the existing element is updated.
* @param {{name: string,
* address: string,
* paired: boolean,
* connected: boolean}} device
* Decription of the Bluetooth device.
* @private
*/
addBluetoothDevice_: function(device) {
var list = $('bluetooth-unpaired-devices-list');
// Display the "connecting" (already paired or not yet paired) and the
// paired devices in the same list.
if (device.paired || device.connecting) {
// Test to see if the device is currently in the unpaired list, in which
// case it should be removed from that list.
var index = $('bluetooth-unpaired-devices-list').find(device.address);
if (index != undefined)
$('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
list = $('bluetooth-paired-devices-list');
} else {
// Test to see if the device is currently in the paired list, in which
// case it should be removed from that list.
var index = $('bluetooth-paired-devices-list').find(device.address);
if (index != undefined)
$('bluetooth-paired-devices-list').deleteItemAtIndex(index);
}
list.appendDevice(device);
// One device can be in the process of pairing. If found, display
// the Bluetooth pairing overlay.
if (device.pairing)
BluetoothPairing.showDialog(device);
},
/**
* Removes an element from the list of available devices.
* @param {string} address Unique address of the device.
* @private
*/
removeBluetoothDevice_: function(address) {
var index = $('bluetooth-unpaired-devices-list').find(address);
if (index != undefined) {
$('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
} else {
index = $('bluetooth-paired-devices-list').find(address);
if (index != undefined)
$('bluetooth-paired-devices-list').deleteItemAtIndex(index);
}
},
/**
* Shows the overlay dialog for changing the user avatar image.
* @private
*/
showImagerPickerOverlay_: function() {
PageManager.showPageByName('changePicture');
},
/**
* Shows (or not) the "User" section of the settings page based on whether
* any of the sub-sections are present (or not).
* @private
*/
maybeShowUserSection_: function() {
$('sync-users-section').hidden =
$('profiles-section').hidden &&
$('sync-section').hidden &&
$('profiles-supervised-dashboard-tip').hidden;
},
/**
* Updates the date and time section with time sync information.
* @param {boolean} canSetTime Whether the system time can be set.
* @private
*/
setCanSetTime_: function(canSetTime) {
// If the time has been network-synced, it cannot be set manually.
$('time-synced-explanation').hidden = canSetTime;
$('set-time').hidden = !canSetTime;
},
/**
* Handle the 'set date and time' button click.
* @private
*/
handleSetTime_: function() {
chrome.send('showSetTime');
},
/**
* Enables the given element if possible; on Chrome OS, it won't enable
* an element that must stay disabled for the session type.
* @param {!Element} element Element to enable.
*/
enableElementIfPossible_: function(element) {
if (cr.isChromeOS)
UIAccountTweaks.enableElementIfPossible(element);
else
element.disabled = false;
},
};
//Forward public APIs to private implementations.
cr.makePublic(BrowserOptions, [
'addBluetoothDevice',
'deleteCurrentProfile',
'enableCertificateButton',
'enableDisplayButton',
'enableFactoryResetSection',
'getCurrentProfile',
'getStartStopSyncButton',
'hideBluetoothSettings',
'notifyInitializationComplete',
'removeBluetoothDevice',
'scrollToSection',
'setAccountPictureManaged',
'setWallpaperManaged',
'setAutoOpenFileTypesDisplayed',
'setBluetoothState',
'setCanSetTime',
'setFontSize',
'setNativeThemeButtonEnabled',
'setNetworkPredictionValue',
'setHighContrastCheckboxState',
'setMetricsReportingCheckboxState',
'setMetricsReportingSettingVisibility',
'setProfilesInfo',
'setSpokenFeedbackCheckboxState',
'setThemesResetButtonEnabled',
'setVirtualKeyboardCheckboxState',
'setupPageZoomSelector',
'setupProxySettingsButton',
'showBluetoothSettings',
'showCreateProfileError',
'showCreateProfileSuccess',
'showCreateProfileWarning',
'showHotwordAlwaysOnSection',
'showHotwordSection',
'showMouseControls',
'showSupervisedUserImportError',
'showSupervisedUserImportSuccess',
'showTouchpadControls',
'toggleExtensionIndicators',
'updateAccountPicture',
'updateAutoLaunchState',
'updateDefaultBrowserState',
'updateEasyUnlock',
'updateManagesSupervisedUsers',
'updateSearchEngines',
'updateSyncState',
]);
if (cr.isChromeOS) {
/**
* Returns username (canonical email) of the user logged in (ChromeOS only).
* @return {string} user email.
*/
// TODO(jhawkins): Investigate the use case for this method.
BrowserOptions.getLoggedInUsername = function() {
return BrowserOptions.getInstance().username_;
};
/**
* Shows different button text for each consumer management enrollment
* status.
* @enum {string} status Consumer management service status string.
*/
BrowserOptions.setConsumerManagementStatus = function(status) {
var button = $('consumer-management-button');
if (status == 'StatusUnknown') {
button.hidden = true;
return;
}
button.hidden = false;
var strId;
switch (status) {
case ConsumerManagementOverlay.Status.STATUS_UNENROLLED:
strId = 'consumerManagementEnrollButton';
button.disabled = false;
ConsumerManagementOverlay.setStatus(status);
break;
case ConsumerManagementOverlay.Status.STATUS_ENROLLING:
strId = 'consumerManagementEnrollingButton';
button.disabled = true;
break;
case ConsumerManagementOverlay.Status.STATUS_ENROLLED:
strId = 'consumerManagementUnenrollButton';
button.disabled = false;
ConsumerManagementOverlay.setStatus(status);
break;
case ConsumerManagementOverlay.Status.STATUS_UNENROLLING:
strId = 'consumerManagementUnenrollingButton';
button.disabled = true;
break;
}
button.textContent = loadTimeData.getString(strId);
};
}
// Export
return {
BrowserOptions: BrowserOptions
};
});