Capabilities & consent
The permission model — the capability taxonomy, scoped grants, feature-detection, and runtime requests.
Extensions declare the capabilities they need in the manifest. On install (or when a
reload widens the set) the shell shows a grouped consent prompt; the user grants a
subset and can revoke per-capability later. Grants are per-profile and persisted,
and every call is enforced by a single gate — default-deny, fail-closed.
public/docs-img/extensions/consent-prompt.pngThe capability taxonomy
| Token | Grants |
|---|---|
tabs.read | See open tabs/windows and their changes. |
tabs.write | Open, close, reload, navigate, activate tabs. |
storage | Keep the extension's own private data on the device. |
scripting | Programmatically inject into pages it may access. |
net.observe | Read-only network / adblock status. |
net.block | Block/redirect requests; author declarative rules. |
history.read / history.write | Read / modify browsing history. |
bookmarks.read / bookmarks.write | Read / modify bookmarks & favorites. |
downloads | Start downloads and observe their progress. |
notifications | Show system notifications. |
cookies | Read/write cookies for permitted sites (needs a host grant too). |
contextMenus | Add items to the page right-click menu. |
alarms | Schedule timer tasks that survive suspension. |
idle | See active / idle / locked state. |
sidePanel | Own a side-panel surface. |
action | Own the toolbar button + popup. |
options | Own the options surface. |
newTab | Replace the page shown when a new tab opens. |
historyPage | Replace the History popup with the extension's view. |
bookmarksPage | Replace the Favorites popup with the extension's view. |
profile.read | Read Connect+ profile & device-rule data. |
proxy | Control proxy routing. |
Scoped (dynamic) capabilities
Some grants take an argument — declare them as objects in capabilities:
-
{ "host": "<match-pattern>" }— host access, Chrome-match-pattern shaped:https://*.example.com/*,*://example.com/*,<all_urls>. Host grants scope content-script injection,cookies, and programmaticscripting."capabilities": ["cookies", { "host": "https://*.example.com/*" }] -
{ "channel": "<name>" }— permission to talk on a cross-extension channel (names match[a-z][a-zA-Z0-9]*). See connect.channel.
Check before you use
The user may grant some requested capabilities and not others. Feature-detect with
capabilities.contains and skip the code paths whose permission wasn't granted, rather
than calling and catching a CAPABILITY_DENIED:
if (await connect.capabilities.contains('history.read')) {
render(await connect.history.list()); // allowed
} // else: run without history — no error thrown
// host- and multi-checks too:
if (await connect.capabilities.contains({ host: tab.url })) { /* inject */ }
if (await connect.capabilities.contains(['cookies', { host: url }])) { /* … */ }Request at runtime
An extension can ask for already-declared capabilities it doesn't yet have, without a reload:
const { granted } = await connect.capabilities.request(['history.read']);Only manifest-declared capabilities can be requested (no escalation). See connect.capabilities for the full API.
The boot-before-consent trap
A background that declares gated event subscriptions (e.g. tabs.onUpdated) may boot
before the user consents, so those subscriptions are silently denied. Re-arm them
from runtime.onGrantsChanged — subscribing is
idempotent, so calling it again after consent lands works. This applies to every
backgrounded extension with capability-gated events.
