Describe the bug
In MV3 development mode, WXT dynamically registers content scripts from the background service worker instead of putting them in manifest.content_scripts.
When a content script is defined with matchOriginAsFallback: true, the production manifest preserves the option as match_origin_as_fallback: true, but the dev-mode dynamic registration drops it. As a result, dev mode behaves differently from production for about:blank / about:srcdoc iframes that depend on origin fallback.
Reproduction
Define a content script like this:
export default defineContentScript({
matches: ["*://*/*", "file:///*"],
allFrames: true,
matchAboutBlank: true,
matchOriginAsFallback: true,
async main() {
// ...
},
})
Run WXT in MV3 dev mode:
Then inspect the registered content scripts from the extension service worker:
await chrome.scripting.getRegisteredContentScripts()
Actual behavior
The dynamically registered content script has matchOriginAsFallback: false:
{
"id": "wxt:content-scripts/node-trigger.js",
"js": ["content-scripts/node-trigger.js"],
"matches": ["*://*/*", "file:///*"],
"allFrames": true,
"matchOriginAsFallback": false,
"runAt": "document_idle",
"world": "ISOLATED"
}
This prevents the content script from running in a TinyMCE editor iframe whose document is about:srcdoc, even though that same script works in production.
Expected behavior
The dev-mode dynamic registration should preserve matchOriginAsFallback: true:
{
"id": "wxt:content-scripts/node-trigger.js",
"js": ["content-scripts/node-trigger.js"],
"matches": ["*://*/*", "file:///*"],
"allFrames": true,
"matchOriginAsFallback": true,
"runAt": "document_idle",
"world": "ISOLATED"
}
Production build already emits the expected static manifest fields:
{
"matches": ["*://*/*", "file:///*"],
"all_frames": true,
"match_about_blank": true,
"js": ["content-scripts/node-trigger.js"],
"match_origin_as_fallback": true
}
Likely cause
In mapWxtOptionsToContentScript, WXT maps:
match_origin_as_fallback: options.matchOriginAsFallback
But mapWxtOptionsToRegisteredContentScript does not map the equivalent dynamic registration field:
function mapWxtOptionsToRegisteredContentScript(options, js, css) {
return {
allFrames: options.allFrames,
excludeMatches: options.excludeMatches,
matches: options.matches,
runAt: options.runAt,
js,
css,
world: options.world
};
}
Adding matchOriginAsFallback: options.matchOriginAsFallback to the dynamic registration payload fixes the dev-mode behavior in my testing.
Notes
In my Edge MV3 dev testing, chrome.scripting.registerContentScripts accepted matchOriginAsFallback, and that was enough for the about:srcdoc iframe case. Passing matchAboutBlank to dynamic registration appeared to make registration fail, so this report is specifically about preserving matchOriginAsFallback for dev-mode dynamic content script registration.
Environment
- WXT:
0.20.25
- Browser target:
edge-mv3
- Mode: development (
wxt -b edge)
*** End Patch
Describe the bug
In MV3 development mode, WXT dynamically registers content scripts from the background service worker instead of putting them in
manifest.content_scripts.When a content script is defined with
matchOriginAsFallback: true, the production manifest preserves the option asmatch_origin_as_fallback: true, but the dev-mode dynamic registration drops it. As a result, dev mode behaves differently from production forabout:blank/about:srcdociframes that depend on origin fallback.Reproduction
Define a content script like this:
Run WXT in MV3 dev mode:
Then inspect the registered content scripts from the extension service worker:
Actual behavior
The dynamically registered content script has
matchOriginAsFallback: false:{ "id": "wxt:content-scripts/node-trigger.js", "js": ["content-scripts/node-trigger.js"], "matches": ["*://*/*", "file:///*"], "allFrames": true, "matchOriginAsFallback": false, "runAt": "document_idle", "world": "ISOLATED" }This prevents the content script from running in a TinyMCE editor iframe whose document is
about:srcdoc, even though that same script works in production.Expected behavior
The dev-mode dynamic registration should preserve
matchOriginAsFallback: true:{ "id": "wxt:content-scripts/node-trigger.js", "js": ["content-scripts/node-trigger.js"], "matches": ["*://*/*", "file:///*"], "allFrames": true, "matchOriginAsFallback": true, "runAt": "document_idle", "world": "ISOLATED" }Production build already emits the expected static manifest fields:
{ "matches": ["*://*/*", "file:///*"], "all_frames": true, "match_about_blank": true, "js": ["content-scripts/node-trigger.js"], "match_origin_as_fallback": true }Likely cause
In
mapWxtOptionsToContentScript, WXT maps:But
mapWxtOptionsToRegisteredContentScriptdoes not map the equivalent dynamic registration field:Adding
matchOriginAsFallback: options.matchOriginAsFallbackto the dynamic registration payload fixes the dev-mode behavior in my testing.Notes
In my Edge MV3 dev testing,
chrome.scripting.registerContentScriptsacceptedmatchOriginAsFallback, and that was enough for theabout:srcdociframe case. PassingmatchAboutBlankto dynamic registration appeared to make registration fail, so this report is specifically about preservingmatchOriginAsFallbackfor dev-mode dynamic content script registration.Environment
0.20.25edge-mv3wxt -b edge)*** End Patch