diff --git a/toolkit/components/extensions/ExtensionCommon.jsm b/toolkit/components/extensions/ExtensionCommon.jsm
index 76a1457f50c4..1dffb69c1b6c 100644
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -1804,10 +1804,11 @@ class EventManager {
let primed = {pendingEvents: []};
listener.primed = primed;
- let wakeup = () => new Promise(resolve => {
- extension.once("startup", resolve);
+ let bgStartupPromise = new Promise(r => extension.once("startup", r));
+ let wakeup = () => {
extension.emit("background-page-event");
- });
+ return bgStartupPromise;
+ };
let fireEvent = (...args) => new Promise((resolve, reject) => {
primed.pendingEvents.push({args, resolve, reject});
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_messaging_startup.js b/toolkit/components/extensions/test/xpcshell/test_ext_messaging_startup.js
index 2b9e9742a460..19fcd9d7617a 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_messaging_startup.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_messaging_startup.js
@@ -17,6 +17,8 @@ let {
Services.prefs.setBoolPref("extensions.webextensions.background-delayed-startup", true);
+const PAGE_HTML = ``;
+
function trackEvents(wrapper) {
let events = new Map();
for (let event of ["background-page-event", "start-background-page"]) {
@@ -42,7 +44,7 @@ async function test(what, background, script) {
},
files: {
- "page.html": ``,
+ "page.html": PAGE_HTML,
"script.js": script,
},
@@ -121,8 +123,8 @@ async function test(what, background, script) {
await page.close();
await extension.unload();
- ExtensionParent._resetStartupPromises();
await promiseShutdownManager();
+ ExtensionParent._resetStartupPromises();
}
add_task(function test_onMessage() {
@@ -174,3 +176,55 @@ add_task(function test_onConnect() {
return test("onConnect", background, script);
});
+
+// Test that messaging works if the background page is started before
+// any messages are exchanged. (See bug 1467136 for an example of how
+// this broke at one point).
+add_task(async function test_other_startup() {
+ await promiseStartupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+
+ async background() {
+ browser.runtime.onMessage.addListener(msg => {
+ browser.test.notifyPass("startup");
+ });
+
+ // addListener() returns right away but make a round trip to the
+ // main process to ensure the persistent onMessage listener is recorded.
+ await browser.runtime.getBrowserInfo();
+ browser.test.sendMessage("bg-ran");
+ },
+
+ files: {
+ "page.html": PAGE_HTML,
+ "script.js"() {
+ browser.runtime.sendMessage("ping");
+ },
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("bg-ran");
+
+ await promiseRestartManager();
+ await extension.awaitStartup();
+
+ // Start the background page. No message have been sent at this point.
+ Services.obs.notifyObservers(null, "sessionstore-windows-restored");
+ await extension.awaitMessage("bg-ran");
+
+ // Now that the background page is fully started, load a new page that
+ // sends a message to the background page.
+ let url = extension.extension.baseURI.resolve("page.html");
+ let page = await ExtensionTestUtils.loadContentPage(url, {extension});
+
+ await extension.awaitFinish("startup");
+
+ await page.close();
+ await extension.unload();
+
+ await promiseShutdownManager();
+ ExtensionParent._resetStartupPromises();
+});