diff --git a/devtools/client/aboutdebugging-new/src/actions/runtime.js b/devtools/client/aboutdebugging-new/src/actions/runtime.js index 936a62d8a68e..1a901d89b79d 100644 --- a/devtools/client/aboutdebugging-new/src/actions/runtime.js +++ b/devtools/client/aboutdebugging-new/src/actions/runtime.js @@ -5,13 +5,16 @@ "use strict"; const { AddonManager } = require("resource://gre/modules/AddonManager.jsm"); -const { BrowserToolboxProcess } = - require("resource://devtools/client/framework/ToolboxProcess.jsm"); -const { Cc, Ci } = require("chrome"); const { DebuggerClient } = require("devtools/shared/client/debugger-client"); const { DebuggerServer } = require("devtools/server/main"); const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser"); +const { + debugLocalAddon, + openTemporaryExtension, + uninstallAddon, +} = require("devtools/client/aboutdebugging/modules/addon"); + const { CONNECT_RUNTIME_FAILURE, CONNECT_RUNTIME_START, @@ -31,8 +34,6 @@ const { REQUEST_WORKERS_SUCCESS, } = require("../constants"); -let browserToolboxProcess = null; - function connectRuntime() { return async (dispatch, getState) => { dispatch({ type: CONNECT_RUNTIME_START }); @@ -80,17 +81,7 @@ function inspectDebugTarget(type, id) { break; } case DEBUG_TARGETS.EXTENSION: { - // Close current debugging toolbox and open a new one. - if (browserToolboxProcess) { - browserToolboxProcess.close(); - } - - browserToolboxProcess = BrowserToolboxProcess.init({ - addonID: id, - onClose: () => { - browserToolboxProcess = null; - } - }); + debugLocalAddon(id); break; } case DEBUG_TARGETS.WORKER: { @@ -108,30 +99,15 @@ function inspectDebugTarget(type, id) { } function installTemporaryExtension() { - const fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - fp.init(window, "Select Manifest File or Package (.xpi)", Ci.nsIFilePicker.modeOpen); - fp.open(async res => { - if (res == Ci.nsIFilePicker.returnCancel || !fp.file) { - return; - } - - let file = fp.file; - - // AddonManager.installTemporaryAddon accepts either - // addon directory or final xpi file. - if (!file.isDirectory() && - !file.leafName.endsWith(".xpi") && !file.leafName.endsWith(".zip")) { - file = file.parent; - } - + return async (dispatch, getState) => { + const message = "Select Manifest File or Package (.xpi)"; + const file = await openTemporaryExtension(window, message); try { await AddonManager.installTemporaryAddon(file); } catch (e) { console.error(e); } - }); - - return () => {}; + }; } function pushServiceWorker(actor) { @@ -161,11 +137,7 @@ function reloadTemporaryExtension(actor) { function removeTemporaryExtension(id) { return async () => { try { - const addon = await AddonManager.getAddonByID(id); - - if (addon) { - await addon.uninstall(); - } + await uninstallAddon(id); } catch (e) { console.error(e); } diff --git a/devtools/client/aboutdebugging-new/src/middleware/extension-component-data.js b/devtools/client/aboutdebugging-new/src/middleware/extension-component-data.js index 45b1f1572ef7..0461a105abc1 100644 --- a/devtools/client/aboutdebugging-new/src/middleware/extension-component-data.js +++ b/devtools/client/aboutdebugging-new/src/middleware/extension-component-data.js @@ -9,6 +9,11 @@ const { REQUEST_EXTENSIONS_SUCCESS, } = require("../constants"); +const { + getExtensionUuid, + parseFileUri +} = require("devtools/client/aboutdebugging/modules/addon"); + /** * This middleware converts extensions object that get from DebuggerClient.listAddons() * to data which is used in DebugTargetItem. @@ -33,16 +38,7 @@ function getFilePath(extension) { return null; } - // Strip a leading slash from Windows drive letter URIs. - // file:///home/foo ~> /home/foo - // file:///C:/foo ~> C:/foo - const windowsRegex = /^file:\/\/\/([a-zA-Z]:\/.*)/; - - if (windowsRegex.test(extension.url)) { - return windowsRegex.exec(extension.url)[1]; - } - - return extension.url.slice("file://".length); + return parseFileUri(extension.url); } function toComponentData(extensions) { @@ -51,7 +47,7 @@ function toComponentData(extensions) { const { actor, iconURL, id, manifestURL, name } = extension; const icon = iconURL || "chrome://mozapps/skin/extensions/extensionGeneric.svg"; const location = getFilePath(extension); - const uuid = manifestURL ? /moz-extension:\/\/([^/]*)/.exec(manifestURL)[1] : null; + const uuid = getExtensionUuid(extension); return { name, icon, diff --git a/devtools/client/aboutdebugging/components/addons/Controls.js b/devtools/client/aboutdebugging/components/addons/Controls.js index db02eece50bd..6a90e6c07159 100644 --- a/devtools/client/aboutdebugging/components/addons/Controls.js +++ b/devtools/client/aboutdebugging/components/addons/Controls.js @@ -10,10 +10,10 @@ loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); -const { Cc, Ci } = require("chrome"); const { createFactory, Component } = require("devtools/client/shared/vendor/react"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); +const { openTemporaryExtension } = require("devtools/client/aboutdebugging/modules/addon"); const Services = require("Services"); const AddonsInstallError = createFactory(require("./InstallError")); @@ -49,26 +49,10 @@ class AddonsControls extends Component { Services.prefs.setBoolPref("devtools.debugger.remote-enabled", enabled); } - loadAddonFromFile() { - this.setState({ installError: null }); - const fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - fp.init(window, - Strings.GetStringFromName("selectAddonFromFile2"), - Ci.nsIFilePicker.modeOpen); - fp.open(res => { - if (res == Ci.nsIFilePicker.returnCancel || !fp.file) { - return; - } - let file = fp.file; - // AddonManager.installTemporaryAddon accepts either - // addon directory or final xpi file. - if (!file.isDirectory() && - !file.leafName.endsWith(".xpi") && !file.leafName.endsWith(".zip")) { - file = file.parent; - } - - this.installAddon(file); - }); + async loadAddonFromFile() { + const message = Strings.GetStringFromName("selectAddonFromFile2"); + const file = await openTemporaryExtension(window, message); + this.installAddon(file); } retryInstall() { diff --git a/devtools/client/aboutdebugging/components/addons/Target.js b/devtools/client/aboutdebugging/components/addons/Target.js index c3f46550c84e..8522d4b9f7d7 100644 --- a/devtools/client/aboutdebugging/components/addons/Target.js +++ b/devtools/client/aboutdebugging/components/addons/Target.js @@ -12,6 +12,7 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories"); const { debugLocalAddon, debugRemoteAddon, + getExtensionUuid, isLegacyTemporaryExtension, isTemporaryID, parseFileUri, @@ -65,11 +66,11 @@ function addonIDforTarget(target) { } function internalIDForTarget(target) { - if (!target.manifestURL) { + const uuid = getExtensionUuid(target); + if (!uuid) { return []; } - // Strip off the protocol and rest, leaving us with just the UUID. - const uuid = /moz-extension:\/\/([^/]*)/.exec(target.manifestURL)[1]; + return [ dom.dt( { className: "addon-target-info-label" }, diff --git a/devtools/client/aboutdebugging/modules/addon.js b/devtools/client/aboutdebugging/modules/addon.js index 31dc19734798..67f881eb6ac4 100644 --- a/devtools/client/aboutdebugging/modules/addon.js +++ b/devtools/client/aboutdebugging/modules/addon.js @@ -4,6 +4,7 @@ "use strict"; +const { Cc, Ci } = require("chrome"); loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm"); loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); @@ -72,6 +73,10 @@ exports.debugRemoteAddon = async function(addonForm, client) { }); }; +/** + * Uninstall the addon with the provided id. + * Resolves when the addon shutdown has completed. + */ exports.uninstallAddon = async function(addonID) { const addon = await AddonManager.getAddonByID(addonID); return addon && addon.uninstall(); @@ -104,3 +109,47 @@ exports.parseFileUri = function(url) { } return url.slice("file://".length); }; + +exports.getExtensionUuid = function(extension) { + const { manifestURL } = extension; + // Strip off the protocol and rest, leaving us with just the UUID. + return manifestURL ? /moz-extension:\/\/([^/]*)/.exec(manifestURL)[1] : null; +}; + +/** + * Open a file picker to allow the user to locate a temporary extension. A temporary + * extension can either be: + * - a folder + * - a .xpi file + * - a .zip file + * + * @param {Window} win + * The window object where the filepicker should be opened. + * Note: We cannot use the global window object here because it is undefined if + * this module is loaded from a file outside of devtools/client/aboutdebugging/. + * See browser-loader.js `uri.startsWith(baseURI)` for more details. + * @param {String} message + * The help message that should be displayed to the user in the filepicker. + * @return {Promise} returns a promise that resolves a File object corresponding to the + * file selected by the user. + */ +exports.openTemporaryExtension = function(win, message) { + return new Promise(resolve => { + const fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); + fp.init(win, message, Ci.nsIFilePicker.modeOpen); + fp.open(res => { + if (res == Ci.nsIFilePicker.returnCancel || !fp.file) { + return; + } + let file = fp.file; + // AddonManager.installTemporaryAddon accepts either + // addon directory or final xpi file. + if (!file.isDirectory() && + !file.leafName.endsWith(".xpi") && !file.leafName.endsWith(".zip")) { + file = file.parent; + } + + resolve(file); + }); + }); +};