Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: condprof OR os == 'android'
- Manifest: toolkit/mozapps/extensions/test/xpcshell/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
*/
ChromeUtils.defineESModuleGetters(this, {
ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
});
AddonTestUtils.usePrivilegedSignatures = id => id === "privileged@ext";
const EXTENSION_API_IMPL = `
this.extensionApiImpl = class extends ExtensionAPI {
onStartup() {
extensions.emit("test-ExtensionAPI-onStartup", {
extensionId: this.extension.id,
version: this.extension.manifest.version,
});
}
static onUpdate(id, manifest) {
extensions.emit("test-ExtensionAPI-onUpdate", {
extensionId: id,
version: manifest.version,
});
}
};`;
function setupTestExtensionAPI() {
// The EXTENSION_API_IMPL script is going to be loaded in the main process,
// where only safe loads are permitted. So we generate a resource:-URL, to
// avoid the use of security.allow_parent_unrestricted_js_loads.
let resProto = Services.io
.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
resProto.setSubstitution(
"extensionApiImplJs",
Services.io.newURI(`data:,${encodeURIComponent(EXTENSION_API_IMPL)}`)
);
registerCleanupFunction(() => {
resProto.setSubstitution("extensionApiImplJs", null);
});
const modules = {
},
};
Services.catMan.addCategoryEntry(
"webextension-modules",
"test-register-extensionApiImpl",
`data:,${JSON.stringify(modules)}`,
false,
false
);
}
async function runInstallAndUpdate({
extensionId,
expectPrivileged,
installExtensionData,
}) {
let extensionData = {
useAddonManager: "permanent",
manifest: {
browser_specific_settings: { gecko: { id: extensionId } },
version: "1.1",
},
};
let events = [];
function onUpdated(type, params) {
params = { type, ...params };
// resourceURI cannot be serialized for use with deepEqual.
delete params.resourceURI;
events.push(params);
}
function onExtensionAPI(type, params) {
events.push({ type, ...params });
}
ExtensionParent.apiManager.on("update", onUpdated);
ExtensionParent.apiManager.on("test-ExtensionAPI-onStartup", onExtensionAPI);
ExtensionParent.apiManager.on("test-ExtensionAPI-onUpdate", onExtensionAPI);
let { addon } = await installExtensionData(extensionData);
equal(addon.isPrivileged, expectPrivileged, "Expected isPrivileged");
extensionData.manifest.version = "2.22";
extensionData.manifest.permissions = ["mozillaAddons"];
// May warn about invalid permissions when the extension is not privileged.
ExtensionTestUtils.failOnSchemaWarnings(false);
let extension = await installExtensionData(extensionData);
ExtensionTestUtils.failOnSchemaWarnings(true);
await extension.unload();
ExtensionParent.apiManager.off("update", onUpdated);
ExtensionParent.apiManager.off("test-ExtensionAPI-onStartup", onExtensionAPI);
ExtensionParent.apiManager.off("test-ExtensionAPI-onUpdate", onExtensionAPI);
// Verify that we have (1) installed and (2) updated the extension.
Assert.deepEqual(
events,
[
{ type: "test-ExtensionAPI-onStartup", extensionId, version: "1.1" },
// The next two events show that ExtensionParent has run the onUpdate
// handler, during which ExtensionData has supposedly been constructed.
{ type: "update", id: extensionId, isPrivileged: expectPrivileged },
{ type: "test-ExtensionAPI-onUpdate", extensionId, version: "2.22" },
{ type: "test-ExtensionAPI-onStartup", extensionId, version: "2.22" },
],
"Expected startup and update events"
);
}
add_task(async function setup() {
setupTestExtensionAPI();
await ExtensionTestUtils.startAddonManager();
});
// Tests that privileged extensions (e.g builtins) are always parsed with the
// correct isPrivileged flag.
add_task(async function test_install_and_update_builtin() {
let { messages } = await promiseConsoleOutput(async () => {
await runInstallAndUpdate({
extensionId: "builtin@ext",
expectPrivileged: true,
async installExtensionData(extData) {
return installBuiltinExtension(extData);
},
});
});
AddonTestUtils.checkMessages(messages, {
expected: [{ message: /Addon with ID builtin@ext already installed,/ }],
forbidden: [{ message: /Invalid extension permission: mozillaAddons/ }],
});
});
add_task(async function test_install_and_update_regular_ext() {
let { messages } = await promiseConsoleOutput(async () => {
await runInstallAndUpdate({
extensionId: "regular@ext",
expectPrivileged: false,
async installExtensionData(extData) {
let extension = ExtensionTestUtils.loadExtension(extData);
await extension.startup();
return extension;
},
});
});
let errPattern =
/Loading extension 'regular@ext': Reading manifest: Invalid extension permission: mozillaAddons/;
let permissionWarnings = messages.filter(msg => errPattern.test(msg.message));
// Expected number of warnings after triggering the update:
// 1. Generated when the loaded by the Addons manager (ExtensionData).
// 2. Generated when read again before ExtensionAPI.onUpdate (ExtensionData).
// 3. Generated when the extension actually runs (Extension).
equal(permissionWarnings.length, 3, "Expected number of permission warnings");
});
add_task(async function test_install_and_update_privileged_ext() {
let { messages } = await promiseConsoleOutput(async () => {
await runInstallAndUpdate({
extensionId: "privileged@ext",
expectPrivileged: true,
async installExtensionData(extData) {
let extension = ExtensionTestUtils.loadExtension(extData);
await extension.startup();
return extension;
},
});
});
AddonTestUtils.checkMessages(messages, {
expected: [
// First installation.
{ message: /Starting install of privileged@ext / },
// Second installation (update).
{ message: /Starting install of privileged@ext / },
],
forbidden: [{ message: /Invalid extension permission: mozillaAddons/ }],
});
});