Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'win' && socketprocess_networking && fission
- Manifest: toolkit/components/extensions/test/xpcshell/xpcshell-remote.toml includes toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
- Manifest: toolkit/components/extensions/test/xpcshell/xpcshell.toml includes toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
"use strict";
const { Management } = ChromeUtils.importESModule(
"resource://gre/modules/Extension.sys.mjs"
);
// This test task quits in the middle of the test - do NOT add more tests below
// this, unless you explicitly want to verify the behavior after quitting.
//
//
// This is similar to test_ext_background_early_quit2.js, except here we load
// another extension first (which ensures that HiddenXULWindow is initialized),
// and then initiate quit while the second extension starts. This verifies that
// `createBrowserElement` in `HiddenXULWindow` settles without continuing to
// create a background page (or worse, gets stuck forever, e.g. waiting for
// "XULFrameLoaderCreated").
add_task(async function test_quit_while_background_starts_after_another_ext() {
let unrelatedExtension = ExtensionTestUtils.loadExtension({
background() {
// Nothing here, when startup() resolves we have loaded.
},
});
await unrelatedExtension.startup();
let extension = ExtensionTestUtils.loadExtension({
// If we somehow fail to make progress, then we should notice that.
delayedStartup: false,
background() {
browser.test.fail(
"Unexpected background page execution. eForceQuit should have aborted createBrowserElement in HiddenXULWindow"
);
},
});
info("Waiting for extension to start up");
let startupCount = 0;
let runManifestCount = 0;
// Verifies that background startup is interrupted; "startup" is emitted
// before runManifest invokes the background initialization logic that
// initializes HiddenXULWindow.
Management.once("startup", (eventName, ext) => {
++startupCount;
const origRunManifest = ext.runManifest;
ext.runManifest = function () {
++runManifestCount;
return Reflect.apply(origRunManifest, this, arguments);
};
// The Quit() call below calls ExitLastWindowClosingSurvivalArea() at
// which expects an EnterLastWindowClosingSurvivalArea() to have called
// before, or else the following assertion will be triggered at:
// ASSERTION: consider quit stopper out of bounds: 'mConsiderQuitStopper > 0
//
// During normal (non-xpcshell) execution, nsAppStartup::Run() runs, which
// calls EnterLastWindowClosingSurvivalArea(). In xpcshell tests, this is
// not called, and we need to call it here:
Services.startup.enterLastWindowClosingSurvivalArea();
Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
});
const { messages } = await promiseConsoleOutput(async () => {
await extension.startup();
});
equal(startupCount, 1, "Observed one extension startup");
equal(runManifestCount, 1, "Observed runManifest call after emit startup");
equal(extension.extension.backgroundState, "stopped", "backgroundState");
equal(
messages.filter(m =>
m.message.includes("Cannot create hidden browser past shutdown")
).length,
1,
"Found message when HiddenXULWindow.createBrowserElement was aborted"
);
// To be most realistic, set the APP_SHUTDOWN flag when shutdown() is called
// via extension.unload(). Otherwise we would run logic that ordinarily does
// not run when extensions unload on browser shutdown.
// E.g. ServiceWorkerCleanUp.removeFromPrincipal at
// throws NS_ERROR_XPC_GS_RETURNED_FAILURE upon calling
// ServiceManager::GetService via unregisterServiceWorkersMatching.
let shutdownCount = 0;
const Extension_prototype = Object.getPrototypeOf(extension.extension);
const origShutdown = Extension_prototype.shutdown;
Extension_prototype.shutdown = function () {
++shutdownCount;
return origShutdown.call(this, "APP_SHUTDOWN");
};
await extension.unload();
await unrelatedExtension.unload();
equal(shutdownCount, 2, "extension.shutdown() was called twice");
});