From efbc1f62aace61ce5e20ee6aa25677a901b0d9af Mon Sep 17 00:00:00 2001 From: Andrew Swan Date: Wed, 6 Jun 2018 13:58:43 -0700 Subject: [PATCH] Bug 1467136 Ensure extension messaging cannot miss the startup event r=kmag When a background page has a messaging listener but the background page is started in response to something other than an extension message, we were missing the background "startup" event. Fix that by setting up the listener earlier. MozReview-Commit-ID: Cr58EyCoY6W --HG-- extra : rebase_source : 0d309e01be35e2ef7ab5e489726d0191571ca371 --- .../components/extensions/ExtensionCommon.jsm | 7 ++- .../xpcshell/test_ext_messaging_startup.js | 58 ++++++++++++++++++- 2 files changed, 60 insertions(+), 5 deletions(-) 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(); +});