Support new content script registration `optional`
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 10.5k
- Forks
- 564
- PR merge metrics
- No merged PRs in 30d
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the existing registration: 'runtime' codepath and the manifest-generation flow, including the entrypoints:resolved, prepare:types, and build:manifestGenerated hooks described in the issue. Compare how content-script matches reach host_permissions, then define the corresponding optional_host_permissions and background-script registration behavior. Done means optional registrations wait for granted permissions and avoid unnecessary permission escalation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- build-system, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100