Skip to content

Support new content script registration optional #2239

@marcellino-ornelas

Description

@marcellino-ornelas

Feature Request

Add a new registration: 'optional' option for content scripts that moves their matches into optional_host_permissions instead of host_permissions, and registers them dynamically via browser.scripting.registerContentScripts() at runtime.

Currently, registration: 'runtime' moves matches to host_permissions, which still triggers Chrome's permission escalation dialog when new hosts are added on extension update (disabling the extension until the user re-accepts). Extensions that want to support new hosts without disabling need to use optional_host_permissions instead.

Proposed API:

export default defineContentScript({
    matches: ['https://my-app.com/*'],
    registration: 'optional',
    // ...
});

Behavior:

  • Strip the content script's matches from manifest.json content_scripts
  • Add them to optional_host_permissions instead of host_permissions
    • Would be nice if this was deduped if user already had urls in optional_host_permissions. example if I had *.app.com in optional_host_permissions theres no need to put auth.app.com because its covered by the existing one
  • Nice to have: Register dynamically via browser.scripting.registerContentScripts() in the background script
  • Scripts run only after the user grants the optional host permission

Is your feature request related to a bug?

N/A — though this addresses a common pain point where adding new content_scripts.matches entries causes Chrome to disable the extension on update due to permission escalation, even when the hosts are already covered by optional_host_permissions.

What are the alternatives?

We built a custom WXT module that:

  1. Hooks into entrypoints:resolved to identify content scripts targeting optional hosts
  2. Generates a virtual module with the optional content script data via prepare:types
  3. Strips them from the manifest in build:manifestGenerated
  4. Registers them dynamically in the background script using browser.scripting.registerContentScripts()

This works but requires significant custom infrastructure that could be a first-class WXT feature. The registration: 'runtime' codepath already handles most of this — the main difference is the target permission type (optional_host_permissions vs host_permissions).

Additional context

  • Chrome treats any new entry in content_scripts.matches as a permission escalation, regardless of whether the host is already in optional_host_permissions
  • optional_host_permissions changes alone do not trigger the escalation dialog
  • browser.scripting.registerContentScripts() registrations persist across service worker restarts and extension updates, so they only need to be set up once
  • This is a common pattern for extensions that progressively support new domains without disrupting existing users

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions