"Install with the minimum. Earn the rest with intent. Track 6 ends with two opt-in flows wired end to end: Export Clips asks for downloads when the user clicks it, and Enable on This Site asks for the host permission when the user wants ClipDeck on a previously-excluded URL."
The Two Flows
Track 5 left ClipDeck with downloads and optional_host_permissions declared but unrequested. This lesson lights both:
- Export Clips — popup or side-panel button. On click, request
downloadsif not granted, then trigger the actual export through the SW. - Enable on This Site — popup button that appears only when content script ISN'T running on the current tab (because the URL didn't match content_scripts.matches or it was in exclude_matches). On click, request
https://<current-host>/*as a host permission, then programmatically inject the content script.
Both follow the same shape: detect → ask → run → fall back gracefully if denied. The shape is so consistent it's worth abstracting into a small helper.
The ensurePermission Helper
One function the popup and side panel can both use:
async function ensurePermission(req) {
const has = await chrome.permissions.contains(req);
if (has) return true;
return chrome.permissions.request(req);
}
It collapses the contains-then-request dance into one call. Callers branch only on the boolean result.
The 'Enable on This Site' Flow
This is the more interesting of the two because it changes the extension's reach at runtime. The flow:
- Popup opens. Read current tab's URL.
- Check if content script is already injected: send a ping message, catch the failure if no listener exists.
- If running, the button reads "ClipDeck is active on this site." If not, the button reads "Enable ClipDeck on this site."
- On click, request the host permission for
https://<host>/*. - If granted, ask the SW to inject the content script via
chrome.scripting.executeScript({ target: { tabId }, files: ['content.js'] }). Now ClipDeck works on this tab. - For persistent injection across reloads, also add a dynamic content script via
chrome.scripting.registerContentScriptsso future loads of the same host auto-inject.
Persistent Dynamic Content Scripts
chrome.scripting.registerContentScripts (Chrome 96+) registers a content script that survives SW evictions and browser restarts, scoped to URLs you grant host permission to. The shape:
await chrome.scripting.registerContentScripts([{
id: 'clipdeck-dynamic-acme',
matches: ['https://acme.com/*'],
js: ['content.js'],
runAt: 'document_idle',
}]);
List existing ones with getRegisteredContentScripts; remove with unregisterContentScripts({ ids: ['...'] }). The 'Disable on this site' counterpart unregisters AND calls chrome.permissions.remove({ origins: ['https://acme.com/*'] }).
The Privacy Trust Story
By the end of this lesson ClipDeck installs with:
- One install warning (the narrowed content_scripts.matches), one moderate warning (tabs), one silent set (storage/sidePanel/contextMenus/scripting/activeTab/alarms).
- Export, notifications, and "enable on additional sites" all gated behind per-feature in-product prompts.
- A privacy-minded user can use the entire core feature set without ever granting downloads or additional hosts; they pay only for the upgrades they actually want.
This is the bar Chrome Web Store reviewers look for: "is the install warning proportional to what the extension actually does at install?" An extension that asks for everything upfront fails the bar even if technically it would use everything. An extension that defers asks to in-product moments passes.
Wrapping Track 6
Six tracks down. ClipDeck has:
- An MV3 manifest with clean permission categories.
- A service worker, a popup, a side panel, content scripts, context menus, keyboard shortcuts, omnibox, badge.
- Full CRUD-C (Track 3) and CRUD-R (Track 4).
- Per-tab pause (Track 5).
- Just-in-time downloads and host permissions (this track).
Track 7 adds the rest of CRUD — Update and Delete — and the larger DOM-handling toolkit ClipDeck needs to make the existing reach work on the messy, framework-heavy sites people actually use.