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:
- Hooks into
entrypoints:resolved to identify content scripts targeting optional hosts
- Generates a virtual module with the optional content script data via
prepare:types
- Strips them from the manifest in
build:manifestGenerated
- 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
Feature Request
Add a new
registration: 'optional'option for content scripts that moves theirmatchesintooptional_host_permissionsinstead ofhost_permissions, and registers them dynamically viabrowser.scripting.registerContentScripts()at runtime.Currently,
registration: 'runtime'moves matches tohost_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 useoptional_host_permissionsinstead.Proposed API:
Behavior:
matchesfrommanifest.jsoncontent_scriptsoptional_host_permissionsinstead ofhost_permissionsoptional_host_permissions. example if I had*.app.cominoptional_host_permissionstheres no need to putauth.app.combecause its covered by the existing onebrowser.scripting.registerContentScripts()in the background scriptIs your feature request related to a bug?
N/A — though this addresses a common pain point where adding new
content_scripts.matchesentries causes Chrome to disable the extension on update due to permission escalation, even when the hosts are already covered byoptional_host_permissions.What are the alternatives?
We built a custom WXT module that:
entrypoints:resolvedto identify content scripts targeting optional hostsprepare:typesbuild:manifestGeneratedbrowser.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_permissionsvshost_permissions).Additional context
content_scripts.matchesas a permission escalation, regardless of whether the host is already inoptional_host_permissionsoptional_host_permissionschanges alone do not trigger the escalation dialogbrowser.scripting.registerContentScripts()registrations persist across service worker restarts and extension updates, so they only need to be set up once