Popup and storage
Identify the active page
Read the current tab URL after a user gesture and turn it into a stable key for one page-specific note.
Page Notes needs one thing from the browser: the URL of the page the user is looking at. Not the history, not every open tab. Just the active one.
The activeTab permission is built for this. When the user clicks the toolbar icon, Chrome grants the extension temporary access to that tab, including its URL. No install-time warning about reading browsing data.
How long the grant lasts
The grant is scoped to that tab and to the origin it’s on. It survives while the user stays on the same origin. It’s revoked when the tab closes or navigates to a different origin.
Three gestures qualify: clicking the extension action, picking its context-menu item, or pressing a declared keyboard command. Nothing else does. A timer in the service worker doesn’t count.
So it’s a temporary capability, not a standing one. Every result we get from it still needs checking.
Reading the tab
Query the active tab in the current window, then reject pages where extensions can’t run. Chrome’s own pages like chrome://extensions are off limits, and so is the Web Store.
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
if (!tab.url?.startsWith('http')) {
throw new Error('Page Notes works on web pages')
}
const key = `note:${new URL(tab.url).href}`
tab.url is only populated because activeTab granted access. On a restricted page it’s still there, but we can’t inject anything into it, so we stop early. The note: prefix keeps our keys separate from anything else we might store later.
What counts as the same page
We touched this in lesson one, and now we have to answer it. Two URLs that differ only by #section-2 usually point at the same document. ?utm_source=newsletter is noise. But ?q=my+medical+question is private data we might not want on disk at all.
Put the decision in one small pure function, keyForUrl(url), and document what it strips. Saving and loading must both go through it, otherwise you’ll save under one key and look up another, and the note will seem to vanish.
Tabs move
Between tabs.query() and your next call, the user can close the tab or navigate away. Check that tab.id exists, catch rejected promises, and show a clear “not available on this page” state instead of a form that silently does nothing.
Test with a normal HTTPS page, the same page with a different query string, the same page with a fragment, chrome://extensions, and a tab you close while the popup is still open. Each one should produce either a valid key or a clear message. Never a dead form.
Lesson completed