162 lines
5.2 KiB
JavaScript
162 lines
5.2 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
const { AddonManager } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/AddonManager.sys.mjs"
|
|
);
|
|
const { ExtensionTestUtils } = ChromeUtils.importESModule(
|
|
"resource://testing-common/ExtensionXPCShellUtils.sys.mjs"
|
|
);
|
|
|
|
const DistinctDevToolsServer = getDistinctDevToolsServer();
|
|
ExtensionTestUtils.init(this);
|
|
|
|
add_setup(async () => {
|
|
Services.prefs.setBoolPref("extensions.blocklist.enabled", false);
|
|
await startupAddonsManager();
|
|
});
|
|
|
|
// Basic request wrapper that sends a request and resolves on the next packet.
|
|
// Will only work for very basic scenarios, without events emitted on the server
|
|
// etc...
|
|
async function sendRequest(transport, request) {
|
|
return new Promise(resolve => {
|
|
transport.hooks = {
|
|
onPacket: packet => {
|
|
dump(`received packet: ${JSON.stringify(packet)}\n`);
|
|
// Let's resolve only when we get a packet that is related to our
|
|
// request. It is needed because some methods do not return the correct
|
|
// response right away. This is the case of the `reload` method, which
|
|
// receives a `addonListChanged` message first and then a `reload`
|
|
// message.
|
|
if (packet.from === request.to) {
|
|
resolve(packet);
|
|
}
|
|
},
|
|
};
|
|
transport.send(request);
|
|
});
|
|
}
|
|
|
|
// If this test case fails, please reach out to webext peers because
|
|
// https://github.com/mozilla/web-ext relies on the APIs tested here.
|
|
add_task(async function test_webext_run_apis() {
|
|
DistinctDevToolsServer.init();
|
|
DistinctDevToolsServer.registerAllActors();
|
|
|
|
const transport = DistinctDevToolsServer.connectPipe();
|
|
|
|
// After calling connectPipe, the root actor will be created on the server
|
|
// and a packet will be emitted after a tick. Wait for the initial packet.
|
|
await new Promise(resolve => {
|
|
transport.hooks = { onPacket: resolve };
|
|
});
|
|
|
|
const getRootResponse = await sendRequest(transport, {
|
|
to: "root",
|
|
type: "getRoot",
|
|
});
|
|
|
|
ok(getRootResponse, "received a response after calling RootActor::getRoot");
|
|
ok(getRootResponse.addonsActor, "getRoot returned an addonsActor id");
|
|
|
|
// installTemporaryAddon
|
|
const addonId = "test-addons-actor@mozilla.org";
|
|
const addonPath = getFilePath("addons/web-extension", false, true);
|
|
const promiseStarted = AddonTestUtils.promiseWebExtensionStartup(addonId);
|
|
const { addon } = await sendRequest(transport, {
|
|
to: getRootResponse.addonsActor,
|
|
type: "installTemporaryAddon",
|
|
addonPath,
|
|
// The openDevTools parameter is not always passed by web-ext. This test
|
|
// omits it, to make sure that the request without the flag is accepted.
|
|
// openDevTools: false,
|
|
});
|
|
await promiseStarted;
|
|
|
|
ok(addon, "addonsActor allows to install a temporary add-on");
|
|
equal(addon.id, addonId, "temporary add-on is the expected one");
|
|
equal(addon.actor, false, "temporary add-on does not have an actor");
|
|
|
|
// listAddons
|
|
let { addons } = await sendRequest(transport, {
|
|
to: "root",
|
|
type: "listAddons",
|
|
});
|
|
ok(Array.isArray(addons), "listAddons() returns a list of add-ons");
|
|
equal(addons.length, 1, "expected an add-on installed");
|
|
|
|
const installedAddon = addons[0];
|
|
equal(installedAddon.id, addonId, "installed add-on is the expected one");
|
|
ok(installedAddon.actor, "returned add-on has an actor");
|
|
|
|
// reload
|
|
const promiseReloaded = AddonTestUtils.promiseAddonEvent("onInstalled");
|
|
const promiseRestarted = AddonTestUtils.promiseWebExtensionStartup(addonId);
|
|
await sendRequest(transport, {
|
|
to: installedAddon.actor,
|
|
type: "reload",
|
|
});
|
|
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");
|
|
|
|
// Attempt to uninstall an add-on that is (no longer) installed.
|
|
let error = await sendRequest(transport, {
|
|
to: getRootResponse.addonsActor,
|
|
type: "uninstallAddon",
|
|
addonId,
|
|
});
|
|
equal(
|
|
error?.message,
|
|
`Could not uninstall add-on "${addonId}"`,
|
|
"expected error"
|
|
);
|
|
|
|
// Attempt to uninstall a non-temporarily loaded extension, which we do not
|
|
// allow at the moment. We start by loading an extension, then we call the
|
|
// `uninstallAddon`.
|
|
const id = "not-a-temporary@extension";
|
|
const extension = ExtensionTestUtils.loadExtension({
|
|
manifest: {
|
|
browser_specific_settings: { gecko: { id } },
|
|
},
|
|
useAddonManager: "permanent",
|
|
});
|
|
await extension.startup();
|
|
|
|
error = await sendRequest(transport, {
|
|
to: getRootResponse.addonsActor,
|
|
type: "uninstallAddon",
|
|
addonId: id,
|
|
});
|
|
equal(error?.message, `Could not uninstall add-on "${id}"`, "expected error");
|
|
|
|
await extension.unload();
|
|
|
|
transport.close();
|
|
});
|