Skip to content

MV3 dev dynamic content script registration drops matchOriginAsFallback #2335

@mengxi-ream

Description

@mengxi-ream

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions