From 33f6843dbd8dadcdda117658c4e83e6613319f65 Mon Sep 17 00:00:00 2001 From: Luca Greco Date: Fri, 25 Mar 2022 20:38:36 +0000 Subject: [PATCH] Bug 1748546 - Add support for persistent events to ext-pageAction.js. r=mixedpuppy Depends on D139786 Differential Revision: https://phabricator.services.mozilla.com/D141664 --- .../extensions/parent/ext-pageAction.js | 49 ++++-- .../browser_ext_pageAction_click_types.js | 139 ++++++++++++++---- .../components/extensions/ext-pageAction.js | 43 ++++-- 3 files changed, 170 insertions(+), 61 deletions(-) diff --git a/browser/components/extensions/parent/ext-pageAction.js b/browser/components/extensions/parent/ext-pageAction.js index 7f3d99815dbb..c02d1a91c806 100644 --- a/browser/components/extensions/parent/ext-pageAction.js +++ b/browser/components/extensions/parent/ext-pageAction.js @@ -62,7 +62,7 @@ class PageAction extends PageActionBase { } } -this.pageAction = class extends ExtensionAPI { +this.pageAction = class extends ExtensionAPIPersistent { static for(extension) { return pageActionMap.get(extension); } @@ -340,9 +340,36 @@ this.pageAction = class extends ExtensionAPI { } } + PERSISTENT_EVENTS = { + onClicked({ context, fire }) { + const { extension } = this; + const { tabManager } = extension; + + let listener = async (_event, tab, clickInfo) => { + if (fire.wakeup) { + await fire.wakeup(); + } + // TODO: we should double-check if the tab is already being closed by the time + // the background script got started and we converted the primed listener. + context?.withPendingBrowser(tab.linkedBrowser, () => + fire.sync(tabManager.convert(tab), clickInfo) + ); + }; + + this.on("click", listener); + return { + unregister: () => { + this.off("click", listener); + }, + convert(newFire, extContext) { + fire = newFire; + context = extContext; + }, + }; + }, + }; + getAPI(context) { - const { extension } = context; - const { tabManager } = extension; const { action } = this; return { @@ -351,20 +378,10 @@ this.pageAction = class extends ExtensionAPI { onClicked: new EventManager({ context, - name: "pageAction.onClicked", + module: "pageAction", + event: "onClicked", inputHandling: true, - register: fire => { - let listener = (evt, tab, clickInfo) => { - context.withPendingBrowser(tab.linkedBrowser, () => - fire.sync(tabManager.convert(tab), clickInfo) - ); - }; - - this.on("click", listener); - return () => { - this.off("click", listener); - }; - }, + extensionApi: this, }).api(), openPopup: () => { diff --git a/browser/components/extensions/test/browser/browser_ext_pageAction_click_types.js b/browser/components/extensions/test/browser/browser_ext_pageAction_click_types.js index a2a92a642e02..dc101e06b2d4 100644 --- a/browser/components/extensions/test/browser/browser_ext_pageAction_click_types.js +++ b/browser/components/extensions/test/browser/browser_ext_pageAction_click_types.js @@ -17,27 +17,33 @@ add_task(async function setup() { }); }); -add_task(async function test_clickData() { +async function test_clickData(testAsNonPersistent = false) { let extension = ExtensionTestUtils.loadExtension({ manifest: { page_action: {}, + background: { + persistent: !testAsNonPersistent, + scripts: ["background.js"], + }, }, - async background() { - function onClicked(tab, info) { - let button = info.button; - let modifiers = info.modifiers; - browser.test.sendMessage("onClick", { button, modifiers }); - } + files: { + "background.js": async function background() { + function onClicked(_tab, info) { + let button = info.button; + let modifiers = info.modifiers; + browser.test.sendMessage("onClick", { button, modifiers }); + } - browser.pageAction.onClicked.addListener(onClicked); + browser.pageAction.onClicked.addListener(onClicked); - let [tab] = await browser.tabs.query({ - active: true, - currentWindow: true, - }); - await browser.pageAction.show(tab.id); - browser.test.sendMessage("ready"); + let [tab] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + await browser.pageAction.show(tab.id); + browser.test.sendMessage("ready"); + }, }, }); @@ -96,41 +102,67 @@ add_task(async function test_clickData() { await extension.startup(); await extension.awaitMessage("ready"); + if (testAsNonPersistent) { + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: false, + }); + info("Terminating the background event page"); + await extension.terminateBackground(); + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: true, + }); + } + + info("Clicking the pageAction"); await testClickPageAction(clickPageAction, triggerPageActionWithKeyboard); + + if (testAsNonPersistent) { + await extension.awaitMessage("ready"); + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: false, + }); + } + await testClickPageAction( clickPageActionInPanel, triggerPageActionWithKeyboardInPanel ); await extension.unload(); -}); +} -add_task(async function test_clickData_reset() { +async function test_clickData_reset(testAsNonPersistent = false) { let extension = ExtensionTestUtils.loadExtension({ manifest: { browser_action: {}, page_action: {}, + background: { + persistent: !testAsNonPersistent, + scripts: ["background.js"], + }, }, - async background() { - function onBrowserActionClicked(tab, info) { - // openPopup requires user interaction, such as a browser action click. - browser.pageAction.openPopup(); - } + files: { + "background.js": async function background() { + function onBrowserActionClicked(tab, info) { + // openPopup requires user interaction, such as a browser action click. + browser.pageAction.openPopup(); + } - function onPageActionClicked(tab, info) { - browser.test.sendMessage("onClick", info); - } + function onPageActionClicked(tab, info) { + browser.test.sendMessage("onClick", info); + } - browser.browserAction.onClicked.addListener(onBrowserActionClicked); - browser.pageAction.onClicked.addListener(onPageActionClicked); + browser.browserAction.onClicked.addListener(onBrowserActionClicked); + browser.pageAction.onClicked.addListener(onPageActionClicked); - let [tab] = await browser.tabs.query({ - active: true, - currentWindow: true, - }); - await browser.pageAction.show(tab.id); - browser.test.sendMessage("ready"); + let [tab] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + await browser.pageAction.show(tab.id); + browser.test.sendMessage("ready"); + }, }, }); @@ -149,8 +181,27 @@ add_task(async function test_clickData_reset() { await extension.startup(); await extension.awaitMessage("ready"); + if (testAsNonPersistent) { + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: false, + }); + info("Terminating the background event page"); + await extension.terminateBackground(); + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: true, + }); + } + + info("Clicking the pageAction"); await clickPageActionWithModifiers(); + if (testAsNonPersistent) { + await extension.awaitMessage("ready"); + assertPersistentListeners(extension, "pageAction", "onClicked", { + primed: false, + }); + } + await clickBrowserAction(extension); assertInfoReset(await extension.awaitMessage("onClick")); @@ -160,4 +211,28 @@ add_task(async function test_clickData_reset() { assertInfoReset(await extension.awaitMessage("onClick")); await extension.unload(); +} + +add_task(function test_clickData_MV2() { + return test_clickData(/* testAsNonPersistent */ false); +}); + +add_task(async function test_clickData_MV2_eventPage() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + await test_clickData(/* testAsNonPersistent */ true); + await SpecialPowers.popPrefEnv(); +}); + +add_task(function test_clickData_reset_MV2() { + return test_clickData_reset(/* testAsNonPersistent */ false); +}); + +add_task(async function test_clickData_reset_MV2_eventPage() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.eventPages.enabled", true]], + }); + await test_clickData_reset(/* testAsNonPersistent */ true); + await SpecialPowers.popPrefEnv(); }); diff --git a/mobile/android/components/extensions/ext-pageAction.js b/mobile/android/components/extensions/ext-pageAction.js index f03fab31a18e..451577cbe6fa 100644 --- a/mobile/android/components/extensions/ext-pageAction.js +++ b/mobile/android/components/extensions/ext-pageAction.js @@ -75,7 +75,7 @@ class PageAction extends PageActionBase { } } -this.pageAction = class extends ExtensionAPI { +this.pageAction = class extends ExtensionAPIPersistent { async onManifestEntry(entryName) { const { extension } = this; const action = new PageAction(extension, this); @@ -98,9 +98,33 @@ this.pageAction = class extends ExtensionAPI { GeckoViewWebExtension.pageActions.delete(extension); } + PERSISTENT_EVENTS = { + onClicked({ fire }) { + const { extension } = this; + const { tabManager } = extension; + + const listener = async (_event, tab) => { + if (fire.wakeup) { + await fire.wakeup(); + } + // TODO: we should double-check if the tab is already being closed by the time + // the background script got started and we converted the primed listener. + fire.async(tabManager.convert(tab)); + }; + + this.on("click", listener); + return { + unregister: () => { + this.off("click", listener); + }, + convert(newFire, _extContext) { + fire = newFire; + }, + }; + }, + }; + getAPI(context) { - const { extension } = context; - const { tabManager } = extension; const { action } = this; return { @@ -109,16 +133,9 @@ this.pageAction = class extends ExtensionAPI { onClicked: new EventManager({ context, - name: "pageAction.onClicked", - register: fire => { - const listener = (event, tab) => { - fire.async(tabManager.convert(tab)); - }; - this.on("click", listener); - return () => { - this.off("click", listener); - }; - }, + module: "pageAction", + event: "onClicked", + extensionApi: this, }).api(), openPopup() {