Bug 1823457 - Expose uninstallAddon method on addons actor. r=jdescottes,devtools-backward-compat-reviewers,devtools-reviewers

Depends on D173053

Differential Revision: https://phabricator.services.mozilla.com/D173126
This commit is contained in:
William Durand 2023-03-23 11:43:13 +00:00
parent 7c5ebbcf74
commit 475d4dd995
10 changed files with 93 additions and 33 deletions

View File

@ -21,7 +21,6 @@ const {
const { const {
openTemporaryExtension, openTemporaryExtension,
uninstallAddon,
} = require("resource://devtools/client/aboutdebugging/src/modules/extensions-helper.js"); } = require("resource://devtools/client/aboutdebugging/src/modules/extensions-helper.js");
const { const {
@ -174,9 +173,11 @@ function reloadTemporaryExtension(id) {
} }
function removeTemporaryExtension(id) { function removeTemporaryExtension(id) {
return async () => { return async ({ getState }) => {
const clientWrapper = getCurrentClient(getState().runtimes);
try { try {
await uninstallAddon(id); await clientWrapper.uninstallAddon({ id });
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }

View File

@ -31,11 +31,8 @@ const {
} = require("resource://devtools/client/aboutdebugging/src/constants.js"); } = require("resource://devtools/client/aboutdebugging/src/constants.js");
const { const {
getCurrentRuntimeDetails, getCurrentClient,
} = require("resource://devtools/client/aboutdebugging/src/modules/runtimes-state-helper.js"); } = require("resource://devtools/client/aboutdebugging/src/modules/runtimes-state-helper.js");
const {
RUNTIMES,
} = require("resource://devtools/client/aboutdebugging/src/constants.js");
/** /**
* This component provides components that reload/remove temporary extension. * This component provides components that reload/remove temporary extension.
@ -46,7 +43,7 @@ class TemporaryExtensionAdditionalActions extends PureComponent {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
target: Types.debugTarget.isRequired, target: Types.debugTarget.isRequired,
// Provided by redux state // Provided by redux state
runtimeDetails: Types.runtimeDetails.isRequired, supportsAddonsUninstall: PropTypes.bool.isRequired,
}; };
} }
@ -145,10 +142,7 @@ class TemporaryExtensionAdditionalActions extends PureComponent {
} }
renderRemoveButton() { renderRemoveButton() {
// TODO: Bug 1823457 - Uninstalling an add-on is currently limited to a if (!this.props.supportsAddonsUninstall) {
// local runtime. Once it becomes possible to use the RDP protocol, we can
// show this "Remove" button.
if (this.props.runtimeDetails.info.type !== RUNTIMES.THIS_FIREFOX) {
return null; return null;
} }
@ -199,8 +193,11 @@ class TemporaryExtensionAdditionalActions extends PureComponent {
} }
const mapStateToProps = state => { const mapStateToProps = state => {
const clientWrapper = getCurrentClient(state.runtimes);
return { return {
runtimeDetails: getCurrentRuntimeDetails(state.runtimes), supportsAddonsUninstall:
clientWrapper.traits.supportsAddonsUninstall === true,
}; };
}; };

View File

@ -146,6 +146,11 @@ class ClientWrapper {
return this.client.mainRoot.getAddon({ id }); return this.client.mainRoot.getAddon({ id });
} }
async uninstallAddon({ id }) {
const addonsFront = await this.getFront("addons");
return addonsFront.uninstallAddon(id);
}
async getMainProcess() { async getMainProcess() {
return this.client.mainRoot.getMainProcess(); return this.client.mainRoot.getMainProcess();
} }
@ -206,6 +211,10 @@ class ClientWrapper {
openRemoteDevTools openRemoteDevTools
); );
} }
get traits() {
return { ...this.client.mainRoot.traits };
}
} }
exports.ClientWrapper = ClientWrapper; exports.ClientWrapper = ClientWrapper;

View File

@ -6,11 +6,6 @@
const lazy = {}; const lazy = {};
ChromeUtils.defineModuleGetter(
lazy,
"AddonManager",
"resource://gre/modules/AddonManager.jsm"
);
ChromeUtils.defineESModuleGetters(lazy, { ChromeUtils.defineESModuleGetters(lazy, {
FileUtils: "resource://gre/modules/FileUtils.sys.mjs", FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
}); });
@ -19,15 +14,6 @@ const {
PREFERENCES, PREFERENCES,
} = require("resource://devtools/client/aboutdebugging/src/constants.js"); } = require("resource://devtools/client/aboutdebugging/src/constants.js");
/**
* Uninstall the addon with the provided id.
* Resolves when the addon shutdown has completed.
*/
exports.uninstallAddon = async function(addonID) {
const addon = await lazy.AddonManager.getAddonByID(addonID);
return addon && addon.uninstall();
};
exports.parseFileUri = function(url) { exports.parseFileUri = function(url) {
// Strip a leading slash from Windows drive letter URIs. // Strip a leading slash from Windows drive letter URIs.
// file:///home/foo ~> /home/foo // file:///home/foo ~> /home/foo

View File

@ -31,6 +31,12 @@ add_task(async function() {
mocks.thisFirefoxClient, mocks.thisFirefoxClient,
document document
); );
await testAddonsOnMockedRemoteClient(
usbClient,
mocks.thisFirefoxClient,
document,
/* supportsAddonsUninstall */ true
);
info("Prepare Network client mock"); info("Prepare Network client mock");
const networkClient = mocks.createNetworkRuntime(NETWORK_RUNTIME_HOST, { const networkClient = mocks.createNetworkRuntime(NETWORK_RUNTIME_HOST, {
@ -45,6 +51,12 @@ add_task(async function() {
mocks.thisFirefoxClient, mocks.thisFirefoxClient,
document document
); );
await testAddonsOnMockedRemoteClient(
networkClient,
mocks.thisFirefoxClient,
document,
/* supportsAddonsUninstall */ true
);
await removeTab(tab); await removeTab(tab);
}); });
@ -55,7 +67,8 @@ add_task(async function() {
async function testAddonsOnMockedRemoteClient( async function testAddonsOnMockedRemoteClient(
remoteClient, remoteClient,
firefoxClient, firefoxClient,
document document,
supportsAddonsUninstall = false
) { ) {
const extensionPane = getDebugTargetPane("Extensions", document); const extensionPane = getDebugTargetPane("Extensions", document);
info("Check an empty target pane message is displayed"); info("Check an empty target pane message is displayed");
@ -73,6 +86,8 @@ async function testAddonsOnMockedRemoteClient(
}; };
remoteClient.listAddons = () => [addon, temporaryAddon]; remoteClient.listAddons = () => [addon, temporaryAddon];
remoteClient._eventEmitter.emit("addonListChanged"); remoteClient._eventEmitter.emit("addonListChanged");
// We use a mock client (wrapper) so we must set the trait ourselves.
remoteClient.traits.supportsAddonsUninstall = supportsAddonsUninstall;
info("Wait until the extension appears"); info("Wait until the extension appears");
await waitUntil( await waitUntil(
@ -94,11 +109,14 @@ async function testAddonsOnMockedRemoteClient(
"Temporary Extension target appeared for the remote runtime" "Temporary Extension target appeared for the remote runtime"
); );
// TODO: Bug 1823457 - Allow to remove an extension using a non-local runtime.
const removeButton = temporaryExtensionTarget.querySelector( const removeButton = temporaryExtensionTarget.querySelector(
".qa-temporary-extension-remove-button" ".qa-temporary-extension-remove-button"
); );
ok(!removeButton, "No remove button expected for the temporary extension"); if (supportsAddonsUninstall) {
ok(removeButton, "Remove button expected for the temporary extension");
} else {
ok(!removeButton, "No remove button expected for the temporary extension");
}
const reloadButton = temporaryExtensionTarget.querySelector( const reloadButton = temporaryExtensionTarget.querySelector(
".qa-temporary-extension-reload-button" ".qa-temporary-extension-reload-button"

View File

@ -107,6 +107,8 @@ function createClientMock() {
} = require("resource://devtools/client/shared/remote-debugging/version-checker.js"); } = require("resource://devtools/client/shared/remote-debugging/version-checker.js");
return { status: COMPATIBILITY_STATUS.COMPATIBLE }; return { status: COMPATIBILITY_STATUS.COMPATIBLE };
}, },
// No traits by default but allow updates.
traits: {},
}; };
} }

View File

@ -16,8 +16,8 @@ const { FileUtils } = ChromeUtils.importESModule(
"resource://gre/modules/FileUtils.sys.mjs" "resource://gre/modules/FileUtils.sys.mjs"
); );
// This actor is not used by DevTools, but is relied on externally by // This actor is used by DevTools as well as external tools such as webext-run
// webext-run and the Firefox VS-Code plugin. see bug #1578108 // and the Firefox VS-Code plugin. see bug #1578108
class AddonsActor extends Actor { class AddonsActor extends Actor {
constructor(conn) { constructor(conn) {
super(conn, addonsSpec); super(conn, addonsSpec);
@ -65,6 +65,14 @@ class AddonsActor extends Actor {
// gets upgraded to a real actor object. // gets upgraded to a real actor object.
return { id: addon.id, actor: false }; return { id: addon.id, actor: false };
} }
async uninstallAddon(addonId) {
const addon = await AddonManager.getAddonByID(addonId);
if (addon) {
await addon.uninstall();
}
}
} }
exports.AddonsActor = AddonsActor; exports.AddonsActor = AddonsActor;

View File

@ -141,6 +141,9 @@ class RootActor extends Actor {
supportsJavascriptTracing: true, supportsJavascriptTracing: true,
// @backward-compat { version 112 } Checks if the server supports override // @backward-compat { version 112 } Checks if the server supports override
isOverridesSupported: true, isOverridesSupported: true,
// @backward-compat { version 113 } Fx 113 added support for uninstalling
// add-ons via the Addons actor.
supportsAddonsUninstall: true,
}; };
} }

View File

@ -3,6 +3,10 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict"; "use strict";
const { AddonManager } = ChromeUtils.import(
"resource://gre/modules/AddonManager.jsm"
);
// The `AddonsManager` test helper can only be called once per test script. // The `AddonsManager` test helper can only be called once per test script.
// This `setup` task will run first. // This `setup` task will run first.
add_task(async function setup() { add_task(async function setup() {
@ -73,11 +77,12 @@ add_task(async function test_webext_run_apis() {
equal(addon.actor, false, "temporary add-on does not have an actor"); equal(addon.actor, false, "temporary add-on does not have an actor");
// listAddons // listAddons
const { addons } = await sendRequest(transport, { let { addons } = await sendRequest(transport, {
to: "root", to: "root",
type: "listAddons", type: "listAddons",
}); });
ok(Array.isArray(addons), "listAddons() returns a list of add-ons"); ok(Array.isArray(addons), "listAddons() returns a list of add-ons");
equal(addons.length, 1, "expected an add-on installed");
const installedAddon = addons[0]; const installedAddon = addons[0];
equal(installedAddon.id, addonId, "installed add-on is the expected one"); equal(installedAddon.id, addonId, "installed add-on is the expected one");
@ -92,5 +97,29 @@ add_task(async function test_webext_run_apis() {
}); });
await Promise.all([promiseReloaded, promiseRestarted]); await Promise.all([promiseReloaded, promiseRestarted]);
// uninstallAddon
const promiseUninstalled = new Promise(resolve => {
const listener = {};
listener.onUninstalled = uninstalledAddon => {
if (uninstalledAddon.id == addonId) {
AddonManager.removeAddonListener(listener);
resolve();
}
};
AddonManager.addAddonListener(listener);
});
await sendRequest(transport, {
to: getRootResponse.addonsActor,
type: "uninstallAddon",
addonId,
});
await promiseUninstalled;
({ addons } = await sendRequest(transport, {
to: "root",
type: "listAddons",
}));
equal(addons.length, 0, "expected no add-on installed");
transport.close(); transport.close();
}); });

View File

@ -20,6 +20,13 @@ const addonsSpec = generateActorSpec({
}, },
response: { addon: RetVal("json") }, response: { addon: RetVal("json") },
}, },
uninstallAddon: {
request: {
addonId: Arg(0, "string"),
},
response: {},
},
}, },
}); });