Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

"use strict";
// Delay loading until createAppInfo is called and setup.
ChromeUtils.defineESModuleGetters(this, {
});
AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
// The app and platform version here should be >= of the version set in the extensions.webExtensionsMinPlatformVersion preference,
// otherwise test_persistent_listener_after_staged_update will fail because no compatible updates will be found.
AddonTestUtils.createAppInfo(
"xpcshell@tests.mozilla.org",
"XPCShell",
"42",
"42"
);
let { promiseShutdownManager, promiseStartupManager, promiseRestartManager } =
AddonTestUtils;
const server = createHttpServer({ hosts: ["example.com"] });
server.registerDirectory("/data/", do_get_file("data"));
let scopes = AddonManager.SCOPE_PROFILE | AddonManager.SCOPE_APPLICATION;
Services.prefs.setIntPref("extensions.enabledScopes", scopes);
function trackEvents(wrapper) {
let events = new Map();
for (let event of ["background-script-event", "start-background-script"]) {
events.set(event, false);
wrapper.extension.once(event, () => events.set(event, true));
}
return events;
}
/**
* That that we get the expected events
*
* @param {Extension} extension
* @param {Map} events
* @param {object} expect
* @param {boolean} expect.background delayed startup event expected
* @param {boolean} expect.started background has already started
* @param {boolean} expect.delayedStart startup is delayed, notify start and
* expect the starting event
* @param {boolean} expect.request wait for the request event
*/
async function testPersistentRequestStartup(extension, events, expect = {}) {
equal(
events.get("background-script-event"),
!!expect.background,
"Should have gotten a background script event"
);
equal(
events.get("start-background-script"),
!!expect.started,
"Background script should be started"
);
if (!expect.started) {
AddonTestUtils.notifyEarlyStartup();
await ExtensionParent.browserPaintedPromise;
equal(
events.get("start-background-script"),
!!expect.delayedStart,
"Should have gotten start-background-script event"
);
}
if (expect.request) {
await extension.awaitMessage("got-request");
ok(true, "Background page loaded and received webRequest event");
}
}
// Test that a non-blocking listener does not start the background on
// startup, but that it does work after startup.
add_task(async function test_nonblocking() {
await promiseStartupManager();
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
permissions: ["webRequest", "http://example.com/"],
},
background() {
browser.webRequest.onBeforeRequest.addListener(
() => {
browser.test.sendMessage("got-request");
},
);
browser.test.sendMessage("ready");
},
});
// First install runs background immediately, this sets persistent listeners
await extension.startup();
await extension.awaitMessage("ready");
// Restart to get APP_STARTUP, the background should not start
await promiseRestartManager({ lateStartup: false });
await extension.awaitStartup();
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
});
// Test an early startup event
let events = trackEvents(extension);
await ExtensionTestUtils.fetch(
);
await testPersistentRequestStartup(extension, events, {
background: false,
delayedStart: false,
request: false,
});
AddonTestUtils.notifyLateStartup();
await extension.awaitMessage("ready");
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
});
// Test an event after startup
await ExtensionTestUtils.fetch(
);
await testPersistentRequestStartup(extension, events, {
background: false,
started: true,
request: true,
});
await extension.unload();
await promiseShutdownManager();
});
// Test that a non-blocking listener does not start the background on
// startup, but that it does work after startup.
add_task(async function test_eventpage_nonblocking() {
Services.prefs.setBoolPref("extensions.eventPages.enabled", true);
await promiseStartupManager();
let id = "event-nonblocking@test";
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
browser_specific_settings: { gecko: { id } },
permissions: ["webRequest", "http://example.com/"],
background: { persistent: false },
},
background() {
browser.webRequest.onBeforeRequest.addListener(
() => {
browser.test.sendMessage("got-request");
},
);
},
});
// First install runs background immediately, this sets persistent listeners
await extension.startup();
// Restart to get APP_STARTUP, the background should not start
await promiseRestartManager({ lateStartup: false });
await extension.awaitStartup();
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
});
// Test an early startup event
let events = trackEvents(extension);
await ExtensionTestUtils.fetch(
);
await testPersistentRequestStartup(extension, events);
await AddonTestUtils.notifyLateStartup();
// After late startup, event page listeners should be primed.
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: true,
});
// We should not have seen any events yet.
await testPersistentRequestStartup(extension, events);
// Test an event after startup
await ExtensionTestUtils.fetch(
);
// Now the event page should be started and we'll see the request.
await testPersistentRequestStartup(extension, events, {
background: true,
started: true,
request: true,
});
await extension.unload();
await promiseShutdownManager();
Services.prefs.setBoolPref("extensions.eventPages.enabled", false);
});
// Tests that filters are handled properly: if we have a blocking listener
// with a filter, a request that does not match the filter does not get
// suspended and does not start the background page.
add_task(async function test_persistent_blocking() {
await promiseStartupManager();
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
permissions: [
"webRequest",
"webRequestBlocking",
],
},
background() {
browser.webRequest.onBeforeRequest.addListener(
() => {
browser.test.fail("Listener should not have been called");
},
["blocking"]
);
},
});
await extension.startup();
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
});
await promiseRestartManager({ lateStartup: false });
await extension.awaitStartup();
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: true,
});
let events = trackEvents(extension);
await ExtensionTestUtils.fetch(
);
await testPersistentRequestStartup(extension, events, {
background: false,
delayedStart: false,
request: false,
});
AddonTestUtils.notifyLateStartup();
await extension.unload();
await promiseShutdownManager();
});
// Tests that moving permission to optional retains permission and that the
// persistent listeners are used as expected.
add_task(async function test_persistent_listener_after_sideload_upgrade() {
let id = "permission-sideload-upgrade@test";
let extensionData = {
useAddonManager: "permanent",
manifest: {
version: "1.0",
browser_specific_settings: { gecko: { id } },
permissions: ["webRequest", "webRequestBlocking", "http://example.com/"],
},
background() {
browser.webRequest.onBeforeRequest.addListener(
() => {
browser.test.sendMessage("got-request");
},
["blocking"]
);
},
};
let xpi = AddonTestUtils.createTempWebExtensionFile(extensionData);
let extension = ExtensionTestUtils.expectExtension(id);
await AddonTestUtils.manuallyInstall(xpi);
await promiseStartupManager();
await extension.awaitStartup();
// Sideload install does not prime listeners
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
});
await ExtensionTestUtils.fetch(
);
await extension.awaitMessage("got-request");
await promiseShutdownManager();
// Prepare a sideload update for the extension.
extensionData.manifest.version = "2.0";
extensionData.manifest.permissions = ["http://example.com/"];
extensionData.manifest.optional_permissions = [
"webRequest",
"webRequestBlocking",
];
xpi = AddonTestUtils.createTempWebExtensionFile(extensionData);
await AddonTestUtils.manuallyInstall(xpi);
await promiseStartupManager();
await extension.awaitStartup();
// Upgrades start the background when the extension is loaded, so
// primed listeners are cleared already and background events are
// already completed.
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
persisted: true,
});
await extension.unload();
await promiseShutdownManager();
});
// Utility to install builtin addon
async function installBuiltinExtension(extensionData) {
let xpi = await AddonTestUtils.createTempWebExtensionFile(extensionData);
// The built-in location requires a resource: URL that maps to a
// jar: or file: URL. This would typically be something bundled
// into omni.ja but for testing we just use a temp file.
let base = Services.io.newURI(`jar:file:${xpi.path}!/`);
let resProto = Services.io
.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
resProto.setSubstitution("ext-test", base);
return AddonManager.installBuiltinAddon("resource://ext-test/");
}
function promisePostponeInstall(install) {
return new Promise((resolve, reject) => {
let listener = {
onInstallFailed: () => {
install.removeListener(listener);
reject(new Error("extension installation should not have failed"));
},
onInstallEnded: () => {
install.removeListener(listener);
reject(
new Error(
`extension installation should not have ended for ${install.addon.id}`
)
);
},
onInstallPostponed: () => {
install.removeListener(listener);
resolve();
},
};
install.addListener(listener);
install.install();
});
}
// Tests that moving permission to optional retains permission and that the
// persistent listeners are used as expected.
add_task(
async function test_persistent_listener_after_builtin_location_upgrade() {
let id = "permission-builtin-upgrade@test";
let extensionData = {
useAddonManager: "permanent",
manifest: {
version: "1.0",
browser_specific_settings: { gecko: { id } },
permissions: [
"webRequest",
"webRequestBlocking",
],
},
async background() {
browser.runtime.onUpdateAvailable.addListener(() => {
browser.test.sendMessage("postponed");
});
browser.webRequest.onBeforeRequest.addListener(
() => {
browser.test.sendMessage("got-request");
},
["blocking"]
);
},
};
await promiseStartupManager();
// If we use an extension wrapper via ExtensionTestUtils.expectExtension
// it will continue to handle messages even after the update, resulting
// in errors when it receives additional messages without any awaitMessage.
let promiseExtension = AddonTestUtils.promiseWebExtensionStartup(id);
await installBuiltinExtension(extensionData);
let extv1 = await promiseExtension;
assertPersistentListeners(
{ extension: extv1 },
"webRequest",
"onBeforeRequest",
{
primed: false,
}
);
// Prepare an update for the extension.
extensionData.manifest.version = "2.0";
let xpi = AddonTestUtils.createTempWebExtensionFile(extensionData);
let install = await AddonManager.getInstallForFile(xpi);
// Install the update and wait for the onUpdateAvailable event to complete.
let promiseUpdate = new Promise(resolve =>
extv1.once("test-message", (kind, msg) => {
if (msg == "postponed") {
resolve();
}
})
);
await Promise.all([promisePostponeInstall(install), promiseUpdate]);
await promiseShutdownManager();
// restarting allows upgrade to proceed
let extension = ExtensionTestUtils.expectExtension(id);
await promiseStartupManager();
await extension.awaitStartup();
// Upgrades start the background when the extension is loaded, so
// primed listeners are cleared already and background events are
// already completed.
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
persisted: true,
});
await extension.unload();
// remove the builtin addon which will have restarted now.
let addon = await AddonManager.getAddonByID(id);
await addon.uninstall();
await promiseShutdownManager();
}
);
// Tests that moving permission to optional during a staged upgrade retains permission
// and that the persistent listeners are used as expected.
add_task(async function test_persistent_listener_after_staged_upgrade() {
AddonManager.checkUpdateSecurity = false;
let id = "persistent-staged-upgrade@test";
// register an update file.
AddonTestUtils.registerJSON(server, "/test_update.json", {
addons: {
"persistent-staged-upgrade@test": {
updates: [
{
version: "2.0",
update_link:
},
],
},
},
});
let extensionData = {
useAddonManager: "permanent",
manifest: {
version: "2.0",
browser_specific_settings: {
gecko: { id, update_url: `http://example.com/test_update.json` },
},
permissions: ["http://example.com/"],
optional_permissions: ["webRequest", "webRequestBlocking"],
},
background() {
browser.webRequest.onBeforeRequest.addListener(
() => {
browser.test.sendMessage("got-request");
},
["blocking"]
);
browser.webRequest.onSendHeaders.addListener(
() => {
browser.test.sendMessage("got-sendheaders");
},
);
// Force a staged updated.
browser.runtime.onUpdateAvailable.addListener(async details => {
if (details && details.version) {
// This should be the version of the pending update.
browser.test.assertEq("2.0", details.version, "correct version");
browser.test.sendMessage("delay");
}
});
},
};
// Prepare the update first.
server.registerFile(
`/addons/test_settings_staged_restart.xpi`,
AddonTestUtils.createTempWebExtensionFile(extensionData)
);
// Prepare the extension that will be updated.
extensionData.manifest.version = "1.0";
extensionData.manifest.permissions = [
"webRequest",
"webRequestBlocking",
];
delete extensionData.manifest.optional_permissions;
extensionData.background = function () {
browser.webRequest.onBeforeRequest.addListener(
() => {
browser.test.sendMessage("got-request");
},
["blocking"]
);
browser.webRequest.onBeforeSendHeaders.addListener(
() => {
browser.test.sendMessage("got-beforesendheaders");
},
);
browser.webRequest.onSendHeaders.addListener(
() => {
browser.test.sendMessage("got-sendheaders");
},
);
// Force a staged updated.
browser.runtime.onUpdateAvailable.addListener(async details => {
if (details && details.version) {
// This should be the version of the pending update.
browser.test.assertEq("2.0", details.version, "correct version");
browser.test.sendMessage("delay");
}
});
};
await promiseStartupManager();
let extension = ExtensionTestUtils.loadExtension(extensionData);
await extension.startup();
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
});
assertPersistentListeners(extension, "webRequest", "onBeforeSendHeaders", {
primed: false,
});
assertPersistentListeners(extension, "webRequest", "onSendHeaders", {
primed: false,
});
await ExtensionTestUtils.fetch(
);
await extension.awaitMessage("got-request");
await extension.awaitMessage("got-beforesendheaders");
await extension.awaitMessage("got-sendheaders");
ok(true, "Initial version received webRequest event");
let addon = await AddonManager.getAddonByID(id);
Assert.equal(addon.version, "1.0", "1.0 is loaded");
let update = await AddonTestUtils.promiseFindAddonUpdates(addon);
let install = update.updateAvailable;
Assert.ok(install, `install is available ${update.error}`);
await AddonTestUtils.promiseCompleteAllInstalls([install]);
Assert.equal(
install.state,
AddonManager.STATE_POSTPONED,
"update is staged for install"
);
await extension.awaitMessage("delay");
await promiseShutdownManager();
// restarting allows upgrade to proceed
await promiseStartupManager();
await extension.awaitStartup();
// Upgrades start the background when the extension is loaded, so
// primed listeners are cleared already and background events are
// already completed.
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
persisted: true,
});
// this was removed in the upgrade background, should not be persisted.
assertPersistentListeners(extension, "webRequest", "onBeforeSendHeaders", {
primed: false,
persisted: false,
});
assertPersistentListeners(extension, "webRequest", "onSendHeaders", {
primed: false,
persisted: true,
});
await extension.unload();
await promiseShutdownManager();
AddonManager.checkUpdateSecurity = true;
});
// Tests that removing the permission releases the persistent listener.
add_task(async function test_persistent_listener_after_permission_removal() {
AddonManager.checkUpdateSecurity = false;
let id = "persistent-staged-remove@test";
// register an update file.
AddonTestUtils.registerJSON(server, "/test_remove.json", {
addons: {
"persistent-staged-remove@test": {
updates: [
{
version: "2.0",
update_link:
},
],
},
},
});
let extensionData = {
useAddonManager: "permanent",
manifest: {
version: "2.0",
browser_specific_settings: {
gecko: { id, update_url: `http://example.com/test_remove.json` },
},
permissions: ["tabs", "http://example.com/"],
},
background() {
browser.test.sendMessage("loaded");
},
};
// Prepare the update first.
server.registerFile(
`/addons/test_settings_staged_remove.xpi`,
AddonTestUtils.createTempWebExtensionFile(extensionData)
);
await promiseStartupManager();
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
version: "1.0",
browser_specific_settings: {
gecko: { id, update_url: `http://example.com/test_remove.json` },
},
permissions: ["webRequest", "webRequestBlocking", "http://example.com/"],
},
background() {
browser.webRequest.onBeforeRequest.addListener(
() => {
browser.test.sendMessage("got-request");
},
["blocking"]
);
// Force a staged updated.
browser.runtime.onUpdateAvailable.addListener(async details => {
if (details && details.version) {
// This should be the version of the pending update.
browser.test.assertEq("2.0", details.version, "correct version");
browser.test.sendMessage("delay");
}
});
},
});
await extension.startup();
await ExtensionTestUtils.fetch(
);
await extension.awaitMessage("got-request");
ok(true, "Initial version received webRequest event");
let addon = await AddonManager.getAddonByID(id);
Assert.equal(addon.version, "1.0", "1.0 is loaded");
let update = await AddonTestUtils.promiseFindAddonUpdates(addon);
let install = update.updateAvailable;
Assert.ok(install, `install is available ${update.error}`);
await AddonTestUtils.promiseCompleteAllInstalls([install]);
Assert.equal(
install.state,
AddonManager.STATE_POSTPONED,
"update is staged for install"
);
await extension.awaitMessage("delay");
await promiseShutdownManager();
// restarting allows upgrade to proceed
await promiseStartupManager({ lateStartup: false });
await extension.awaitStartup();
await extension.awaitMessage("loaded");
// Upgrades start the background when the extension is loaded, so
// primed listeners are cleared already and background events are
// already completed.
assertPersistentListeners(extension, "webRequest", "onBeforeRequest", {
primed: false,
persisted: false,
});
await extension.unload();
await promiseShutdownManager();
AddonManager.checkUpdateSecurity = true;
});