wxt-dev / wxt-dev/wxt

Support new content script registration `optional`

Open
#2,239 5 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

contribution welcome good first issue
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 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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.