Surfaces & calling the API
Backgrounds, popups, options, side panels, content scripts — and how to call the API and handle errors.
An extension can present several surfaces, all served over the privileged
connect-extension://<id>/ origin, each in the extension's own per-profile session.
Surfaces
| Surface | Manifest | What it is |
|---|---|---|
| Background | background.entry | A headless page with window.connect — the long-lived logic. Honors its lifecycle. |
| Action popup | ui.action.entry | The panel under the toolbar button. Tab-scoped: closes on tab switch. |
| Options | ui.options.entry | A modal settings surface. |
| Side panel | ui.sidePanel.entry | A docked panel; every extension with one gets a tab on the right-edge panel rail so users can open it. Also open/close from code via ui.sidePanel. |
| New-tab page | ui.newTab | Replaces the profile's new-tab page (needs newTab). |
| History view | ui.historyPage | Replaces the History popup's content (needs historyPage). |
| Favorites view | ui.bookmarksPage | Replaces the Favorites popup's content (needs bookmarksPage). |
| Content scripts | contentScripts | Code injected into pages you're granted host access to — see connect.scripting. |
All extension-page surfaces get the full window.connect. Content scripts get a
restricted subset (runtime + storage) — see
connect.scripting.
Calling the API
Every method returns a Promise. A denied capability rejects with a clean error whose
.code you can branch on:
try {
await connect.history.list();
} catch (err) {
if (err.code === 'CAPABILITY_DENIED') {
const { granted } = await connect.capabilities.request(['history.read']);
if (granted.includes('history.read')) return connect.history.list();
}
throw err;
}Prefer capabilities.contains to branch before
calling, so the expected "not granted" path doesn't throw.
Escape hatches
For anything not yet wrapped (or a new action you're entitled to), two primitives back the whole API:
connect.call(ns, action, payload?, opts?); // dispatch any connect.<ns>.<action>
connect.onEvent(ns, action, cb); // subscribe to any connect.<ns>.on<Event>Everything in the API reference is sugar over these two.
Recipes
A few end-to-end patterns:
- Ad/content blocker —
net.block+ declarative rules; a popup with the master toggle and per-site tier. - Page theming / dark mode — a
documentStartcontent script reading a per-host pref from the origin'slocalStorage(zero flash), plus a popup that pushes changes withtabs.sendMessage. Per-host truth instorage. - Tab session manager —
tabs.query+storageto save/restore sets; live-refresh the popup ontabs.onActivated/onUpdated; bookmark all tabs withbookmarks.*. - Cross-extension coordination — two extensions share a
{ channel }grant and exchange state withchannel.publish/subscribe. - Background that reacts to browsing — a
suspendablebackground subscribing totabs.onUpdated/webNavigation, re-arming fromruntime.onGrantsChanged, usingalarmsfor periodic work so it survives suspension.