// 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.
<include src="../uber/uber_utils.js"></include>
<include src="extension_commands_overlay.js"></include>
<include src="extension_focus_manager.js"></include>
<include src="extension_list.js"></include>
<include src="pack_extension_overlay.js"></include>
<include src="extension_error_overlay.js"></include>
<if expr="pp_ifdef('chromeos')">
<include src="chromeos/kiosk_apps.js"></include>
</if>
// Used for observing function of the backend datasource for this page by
// tests.
var webuiResponded = false;
cr.define('extensions', function() {
var ExtensionsList = options.ExtensionsList;
// Implements the DragWrapper handler interface.
var dragWrapperHandler = {
/** @override */
shouldAcceptDrag: function(e) {
// We can't access filenames during the 'dragenter' event, so we have to
// wait until 'drop' to decide whether to do something with the file or
// not.
// See: http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html#concept-dnd-p
return (e.dataTransfer.types &&
e.dataTransfer.types.indexOf('Files') > -1);
},
/** @override */
doDragEnter: function() {
chrome.send('startDrag');
ExtensionSettings.showOverlay(null);
ExtensionSettings.showOverlay($('drop-target-overlay'));
},
/** @override */
doDragLeave: function() {
ExtensionSettings.showOverlay(null);
chrome.send('stopDrag');
},
/** @override */
doDragOver: function(e) {
e.preventDefault();
},
/** @override */
doDrop: function(e) {
ExtensionSettings.showOverlay(null);
if (e.dataTransfer.files.length != 1)
return;
var toSend = null;
// Files lack a check if they're a directory, but we can find out through
// its item entry.
for (var i = 0; i < e.dataTransfer.items.length; ++i) {
if (e.dataTransfer.items[i].kind == 'file' &&
e.dataTransfer.items[i].webkitGetAsEntry().isDirectory) {
toSend = 'installDroppedDirectory';
break;
}
}
// Only process files that look like extensions. Other files should
// navigate the browser normally.
if (!toSend && /\.(crx|user\.js)$/i.test(e.dataTransfer.files[0].name))
toSend = 'installDroppedFile';
if (toSend) {
e.preventDefault();
chrome.send(toSend);
}
}
};
/**
* ExtensionSettings class
* @class
*/
function ExtensionSettings() {}
cr.addSingletonGetter(ExtensionSettings);
ExtensionSettings.prototype = {
__proto__: HTMLDivElement.prototype,
/**
* Perform initial setup.
*/
initialize: function() {
uber.onContentFrameLoaded();
cr.ui.FocusOutlineManager.forDocument(document);
measureCheckboxStrings();
// Set the title.
var title = loadTimeData.getString('extensionSettings');
uber.invokeMethodOnParent('setTitle', {title: title});
// This will request the data to show on the page and will get a response
// back in returnExtensionsData.
chrome.send('extensionSettingsRequestExtensionsData');
$('toggle-dev-on').addEventListener('change',
this.handleToggleDevMode_.bind(this));
$('dev-controls').addEventListener('webkitTransitionEnd',
this.handleDevControlsTransitionEnd_.bind(this));
$('open-apps-dev-tools').addEventListener('click',
this.handleOpenAppsDevTools_.bind(this));
// Set up the three dev mode buttons (load unpacked, pack and update).
$('load-unpacked').addEventListener('click',
this.handleLoadUnpackedExtension_.bind(this));
$('pack-extension').addEventListener('click',
this.handlePackExtension_.bind(this));
$('update-extensions-now').addEventListener('click',
this.handleUpdateExtensionNow_.bind(this));
if (!loadTimeData.getBoolean('offStoreInstallEnabled')) {
this.dragWrapper_ = new cr.ui.DragWrapper(document.documentElement,
dragWrapperHandler);
}
extensions.PackExtensionOverlay.getInstance().initializePage();
// Hook up the configure commands link to the overlay.
var link = document.querySelector('.extension-commands-config');
link.addEventListener('click',
this.handleExtensionCommandsConfig_.bind(this));
// Initialize the Commands overlay.
extensions.ExtensionCommandsOverlay.getInstance().initializePage();
extensions.ExtensionErrorOverlay.getInstance().initializePage();
// Initialize the kiosk overlay.
if (cr.isChromeOS) {
var kioskOverlay = extensions.KioskAppsOverlay.getInstance();
kioskOverlay.initialize();
$('add-kiosk-app').addEventListener('click', function() {
ExtensionSettings.showOverlay($('kiosk-apps-page'));
kioskOverlay.didShowPage();
});
extensions.KioskDisableBailoutConfirm.getInstance().initialize();
}
cr.ui.overlay.setupOverlay($('drop-target-overlay'));
cr.ui.overlay.globalInitialization();
extensions.ExtensionFocusManager.getInstance().initialize();
var path = document.location.pathname;
if (path.length > 1) {
// Skip starting slash and remove trailing slash (if any).
var overlayName = path.slice(1).replace(/\/$/, '');
if (overlayName == 'configureCommands')
this.showExtensionCommandsConfigUi_();
}
preventDefaultOnPoundLinkClicks(); // From webui/js/util.js.
},
/**
* Handles the Load Unpacked Extension button.
* @param {Event} e Change event.
* @private
*/
handleLoadUnpackedExtension_: function(e) {
chrome.send('extensionSettingsLoadUnpackedExtension');
},
/**
* Handles the Pack Extension button.
* @param {Event} e Change event.
* @private
*/
handlePackExtension_: function(e) {
ExtensionSettings.showOverlay($('pack-extension-overlay'));
chrome.send('metricsHandler:recordAction', ['Options_PackExtension']);
},
/**
* Shows the Extension Commands configuration UI.
* @param {Event} e Change event.
* @private
*/
showExtensionCommandsConfigUi_: function(e) {
ExtensionSettings.showOverlay($('extension-commands-overlay'));
chrome.send('metricsHandler:recordAction',
['Options_ExtensionCommands']);
},
/**
* Handles the Configure (Extension) Commands link.
* @param {Event} e Change event.
* @private
*/
handleExtensionCommandsConfig_: function(e) {
this.showExtensionCommandsConfigUi_();
},
/**
* Handles the Update Extension Now button.
* @param {Event} e Change event.
* @private
*/
handleUpdateExtensionNow_: function(e) {
chrome.send('extensionSettingsAutoupdate');
},
/**
* Handles the Toggle Dev Mode button.
* @param {Event} e Change event.
* @private
*/
handleToggleDevMode_: function(e) {
if ($('toggle-dev-on').checked) {
$('dev-controls').hidden = false;
window.setTimeout(function() {
$('extension-settings').classList.add('dev-mode');
}, 0);
} else {
$('extension-settings').classList.remove('dev-mode');
}
chrome.send('extensionSettingsToggleDeveloperMode');
},
/**
* Called when a transition has ended for #dev-controls.
* @param {Event} e webkitTransitionEnd event.
* @private
*/
handleDevControlsTransitionEnd_: function(e) {
if (e.propertyName == 'height' &&
!$('extension-settings').classList.contains('dev-mode')) {
$('dev-controls').hidden = true;
}
},
/**
* Called when the user clicked on the button to launch Apps Developer
* Tools.
* @param {!Event} e A click event.
* @private
*/
handleOpenAppsDevTools_: function(e) {
chrome.send('extensionSettingsLaunch',
['lphgohfeebnhcpiohjndkgbhhkoapkjc']);
},
};
/**
* Called by the dom_ui_ to re-populate the page with data representing
* the current state of installed extensions.
*/
ExtensionSettings.returnExtensionsData = function(extensionsData) {
// We can get called many times in short order, thus we need to
// be careful to remove the 'finished loading' timeout.
if (this.loadingTimeout_)
window.clearTimeout(this.loadingTimeout_);
document.documentElement.classList.add('loading');
this.loadingTimeout_ = window.setTimeout(function() {
document.documentElement.classList.remove('loading');
}, 0);
webuiResponded = true;
if (extensionsData.extensions.length > 0) {
// Enforce order specified in the data or (if equal) then sort by
// extension name (case-insensitive) followed by their ID (in the case
// where extensions have the same name).
extensionsData.extensions.sort(function(a, b) {
function compare(x, y) {
return x < y ? -1 : (x > y ? 1 : 0);
}
return compare(a.order, b.order) ||
compare(a.name.toLowerCase(), b.name.toLowerCase()) ||
compare(a.id, b.id);
});
}
var pageDiv = $('extension-settings');
var marginTop = 0;
if (extensionsData.profileIsManaged) {
pageDiv.classList.add('profile-is-managed');
} else {
pageDiv.classList.remove('profile-is-managed');
}
if (extensionsData.profileIsManaged) {
pageDiv.classList.add('showing-banner');
$('toggle-dev-on').disabled = true;
marginTop += 45;
} else {
pageDiv.classList.remove('showing-banner');
$('toggle-dev-on').disabled = false;
}
pageDiv.style.marginTop = marginTop + 'px';
if (extensionsData.developerMode) {
pageDiv.classList.add('dev-mode');
$('toggle-dev-on').checked = true;
$('dev-controls').hidden = false;
} else {
pageDiv.classList.remove('dev-mode');
$('toggle-dev-on').checked = false;
}
if (extensionsData.appsDevToolsEnabled)
pageDiv.classList.add('apps-dev-tools-mode');
$('load-unpacked').disabled = extensionsData.loadUnpackedDisabled;
ExtensionsList.prototype.data_ = extensionsData;
var extensionList = $('extension-settings-list');
ExtensionsList.decorate(extensionList);
}
// Indicate that warning |message| has occured for pack of |crx_path| and
// |pem_path| files. Ask if user wants override the warning. Send
// |overrideFlags| to repeated 'pack' call to accomplish the override.
ExtensionSettings.askToOverrideWarning =
function(message, crx_path, pem_path, overrideFlags) {
var closeAlert = function() {
ExtensionSettings.showOverlay(null);
};
alertOverlay.setValues(
loadTimeData.getString('packExtensionWarningTitle'),
message,
loadTimeData.getString('packExtensionProceedAnyway'),
loadTimeData.getString('cancel'),
function() {
chrome.send('pack', [crx_path, pem_path, overrideFlags]);
closeAlert();
},
closeAlert);
ExtensionSettings.showOverlay($('alertOverlay'));
}
/**
* Returns the current overlay or null if one does not exist.
* @return {Element} The overlay element.
*/
ExtensionSettings.getCurrentOverlay = function() {
return document.querySelector('#overlay .page.showing');
}
/**
* Sets the given overlay to show. This hides whatever overlay is currently
* showing, if any.
* @param {HTMLElement} node The overlay page to show. If falsey, all overlays
* are hidden.
*/
ExtensionSettings.showOverlay = function(node) {
var pageDiv = $('extension-settings');
if (node) {
pageDiv.style.width = window.getComputedStyle(pageDiv).width;
document.body.classList.add('no-scroll');
} else {
document.body.classList.remove('no-scroll');
pageDiv.style.width = '';
}
var currentlyShowingOverlay = ExtensionSettings.getCurrentOverlay();
if (currentlyShowingOverlay)
currentlyShowingOverlay.classList.remove('showing');
if (node)
node.classList.add('showing');
var pages = document.querySelectorAll('.page');
for (var i = 0; i < pages.length; i++) {
pages[i].setAttribute('aria-hidden', node ? 'true' : 'false');
}
overlay.hidden = !node;
uber.invokeMethodOnParent(node ? 'beginInterceptingEvents' :
'stopInterceptingEvents');
}
/**
* Utility function to find the width of various UI strings and synchronize
* the width of relevant spans. This is crucial for making sure the
* Enable/Enabled checkboxes align, as well as the Developer Mode checkbox.
*/
function measureCheckboxStrings() {
var trashWidth = 30;
var measuringDiv = $('font-measuring-div');
measuringDiv.textContent =
loadTimeData.getString('extensionSettingsEnabled');
var pxWidth = measuringDiv.clientWidth + trashWidth;
measuringDiv.textContent =
loadTimeData.getString('extensionSettingsEnable');
pxWidth = Math.max(measuringDiv.clientWidth + trashWidth, pxWidth);
measuringDiv.textContent =
loadTimeData.getString('extensionSettingsDeveloperMode');
pxWidth = Math.max(measuringDiv.clientWidth, pxWidth);
var style = document.createElement('style');
style.type = 'text/css';
style.textContent =
'.enable-checkbox-text {' +
' min-width: ' + (pxWidth - trashWidth) + 'px;' +
'}' +
'#dev-toggle span {' +
' min-width: ' + pxWidth + 'px;' +
'}';
document.querySelector('head').appendChild(style);
}
// Export
return {
ExtensionSettings: ExtensionSettings
};
});
window.addEventListener('load', function(e) {
extensions.ExtensionSettings.getInstance().initialize();
});