From cd78b3aecf3057b204a5d445ec9fe2d1fdbb3f5a Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Tue, 17 Mar 2026 18:30:38 -0700 Subject: [PATCH 1/5] fix: page_action on firefox mv3 --- packages/wxt/src/core/utils/manifest.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/wxt/src/core/utils/manifest.ts b/packages/wxt/src/core/utils/manifest.ts index a0e3b0592..d9d3a10d7 100644 --- a/packages/wxt/src/core/utils/manifest.ts +++ b/packages/wxt/src/core/utils/manifest.ts @@ -699,6 +699,8 @@ function stripKeys(manifest: Browser.runtime.Manifest): void { keysToRemove.push(...firefoxMv3OnlyKeys); } else { keysToRemove.push(...mv2OnlyKeys); + if (wxt.config.browser === 'chrome') + keysToRemove.push(...chromeMv2OnlyKeys); } keysToRemove.forEach((key) => { @@ -707,7 +709,6 @@ function stripKeys(manifest: Browser.runtime.Manifest): void { } const mv2OnlyKeys = [ - 'page_action', 'browser_action', 'automation', 'content_capabilities', @@ -732,6 +733,7 @@ const mv3OnlyKeys = [ 'optional_host_permissions', 'side_panel', ]; +const chromeMv2OnlyKeys = ['page_action']; const firefoxMv3OnlyKeys = ['host_permissions']; const DEFAULT_MV3_EXTENSION_PAGES_CSP = From 04b4fbaad195690ffc45f7c4d20637d99e99f83f Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:48:28 -0700 Subject: [PATCH 2/5] update: manifest.test.ts and flip logic --- packages/wxt/src/core/utils/__tests__/manifest.test.ts | 6 +++++- packages/wxt/src/core/utils/manifest.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/wxt/src/core/utils/__tests__/manifest.test.ts b/packages/wxt/src/core/utils/__tests__/manifest.test.ts index 5775be31c..0ef91a3c9 100644 --- a/packages/wxt/src/core/utils/__tests__/manifest.test.ts +++ b/packages/wxt/src/core/utils/__tests__/manifest.test.ts @@ -1464,7 +1464,11 @@ describe('Manifest Utils', () => { ['chrome', 2, { ...mv2Manifest, ...hostPermissionsManifest }], ['safari', 2, { ...mv2Manifest, ...hostPermissionsManifest }], ['edge', 2, { ...mv2Manifest, ...hostPermissionsManifest }], - ['firefox', 3, { ...mv3Manifest, ...hostPermissionsManifest }], + [ + 'firefox', + 3, + { ...mv3Manifest, ...hostPermissionsManifest, page_action: {} }, + ], ['chrome', 3, { ...mv3Manifest, ...hostPermissionsManifest }], ['safari', 3, { ...mv3Manifest, ...hostPermissionsManifest }], ['edge', 3, { ...mv3Manifest, ...hostPermissionsManifest }], diff --git a/packages/wxt/src/core/utils/manifest.ts b/packages/wxt/src/core/utils/manifest.ts index d9d3a10d7..ccd23fb44 100644 --- a/packages/wxt/src/core/utils/manifest.ts +++ b/packages/wxt/src/core/utils/manifest.ts @@ -699,7 +699,7 @@ function stripKeys(manifest: Browser.runtime.Manifest): void { keysToRemove.push(...firefoxMv3OnlyKeys); } else { keysToRemove.push(...mv2OnlyKeys); - if (wxt.config.browser === 'chrome') + if (wxt.config.browser !== 'firefox') keysToRemove.push(...chromeMv2OnlyKeys); } From 372aa046cbb8da725ce7289d5924f023a739b757 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 18 Mar 2026 09:09:59 -0500 Subject: [PATCH 3/5] Add another test --- .../src/core/utils/__tests__/manifest.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/wxt/src/core/utils/__tests__/manifest.test.ts b/packages/wxt/src/core/utils/__tests__/manifest.test.ts index 0ef91a3c9..a82a73e3f 100644 --- a/packages/wxt/src/core/utils/__tests__/manifest.test.ts +++ b/packages/wxt/src/core/utils/__tests__/manifest.test.ts @@ -107,6 +107,29 @@ describe('Manifest Utils', () => { }, ); + it('should allow page_action for Firefox MV3', async () => { + const popup = popupEntrypoint('page_action'); + const buildOutput = fakeBuildOutput(); + setFakeWxt({ + config: { + manifestVersion: 3, + outDir, + }, + }); + const expected = { + default_icon: popup.options.defaultIcon, + default_title: popup.options.defaultTitle, + default_popup: 'popup.html', + }; + + const { manifest: actual } = await generateManifest( + [popup], + buildOutput, + ); + + expect(actual.page_action).toEqual(expected); + }); + it('should include default_area for Firefox in mv3', async () => { const popup = fakePopupEntrypoint({ options: { From 2f7afeba50daf056f55230e6fbda0ff064688917 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 21 Mar 2026 08:37:45 -0500 Subject: [PATCH 4/5] Implement correct manifest generation logic for one action --- .../src/core/utils/__tests__/manifest.test.ts | 2 +- packages/wxt/src/core/utils/manifest.ts | 27 +++++++++---------- packages/wxt/src/types.ts | 8 +++++- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/wxt/src/core/utils/__tests__/manifest.test.ts b/packages/wxt/src/core/utils/__tests__/manifest.test.ts index a82a73e3f..5cad2464a 100644 --- a/packages/wxt/src/core/utils/__tests__/manifest.test.ts +++ b/packages/wxt/src/core/utils/__tests__/manifest.test.ts @@ -107,7 +107,7 @@ describe('Manifest Utils', () => { }, ); - it('should allow page_action for Firefox MV3', async () => { + it('should allow converting action to page_action for Firefox MV3', async () => { const popup = popupEntrypoint('page_action'); const buildOutput = fakeBuildOutput(); setFakeWxt({ diff --git a/packages/wxt/src/core/utils/manifest.ts b/packages/wxt/src/core/utils/manifest.ts index ccd23fb44..e93498607 100644 --- a/packages/wxt/src/core/utils/manifest.ts +++ b/packages/wxt/src/core/utils/manifest.ts @@ -284,20 +284,19 @@ function addEntrypoints( if (popup.options.themeIcons) // @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox options.theme_icons = popup.options.themeIcons; - if (manifest.manifest_version === 3) { - manifest.action = { - ...manifest.action, - ...options, - default_popup, - }; - } else { - const key = popup.options.mv2Key ?? 'browser_action'; - manifest[key] = { - ...manifest[key], - ...options, - default_popup, - }; - } + + const actionKey = + manifest.manifest_version === 2 + ? (popup.options.mv2Key ?? 'browser_action') + : wxt.config.browser === 'firefox' + ? (popup.options.mv2Key ?? 'action') + : 'action'; + + manifest[actionKey] = { + ...manifest[actionKey], + ...options, + default_popup, + }; } if (devtools) { diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 9a9723a3f..e903e90be 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -721,7 +721,13 @@ export interface ThemeIcon { } export interface PopupEntrypointOptions extends BaseEntrypointOptions { - /** Defaults to "browser_action" to be equivalent to MV3's "action" key */ + /** + * Defaults to "browser_action" to be equivalent to MV3's "action" key. + * + * This option name is bad - it is also respected for Firefox MV3 (though + * `browser_action` is converted to `action`) to allow using a `page_action` + * in MV3. + */ mv2Key?: PerBrowserOption<'browser_action' | 'page_action'>; defaultIcon?: Record; defaultTitle?: PerBrowserOption; From fae33bd6a7a7c73992fb3f9d6c34315221e36ed5 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 21 Mar 2026 09:55:01 -0500 Subject: [PATCH 5/5] My test was flakey... fixed --- packages/wxt/src/core/utils/__tests__/manifest.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wxt/src/core/utils/__tests__/manifest.test.ts b/packages/wxt/src/core/utils/__tests__/manifest.test.ts index 5cad2464a..7c7f4c03f 100644 --- a/packages/wxt/src/core/utils/__tests__/manifest.test.ts +++ b/packages/wxt/src/core/utils/__tests__/manifest.test.ts @@ -114,6 +114,7 @@ describe('Manifest Utils', () => { config: { manifestVersion: 3, outDir, + browser: 'firefox', }, }); const expected = {