Description
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
matchesfrommanifest.jsoncontent_scripts - Add them to
optional_host_permissionsinstead ofhost_permissions- Would be nice if this was deduped if user already had urls in
optional_host_permissions. example if I had*.app.cominoptional_host_permissionstheres no need to putauth.app.combecause its covered by the existing one
- Would be nice if this was deduped if user already had urls in
- 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:resolvedto 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.matchesas a permission escalation, regardless of whether the host is already inoptional_host_permissions optional_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- This is a common pattern for extensions that progressively support new domains without disrupting existing users