MV3 dev dynamic content script registration drops matchOriginAsFallback
#2335 opened on May 4, 2026
Description
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:
wxt -b edge
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