All posts

Reopening a page where you were reading, without a permission

A scroll offset needs a script in the reopened tab, and access to every site you visit. A text fragment needs neither, and survives a reflow an offset does not.

A tab is three things: a URL, an intention, and a position. Latr replaced the first two from the beginning — the address and the time you meant to come back. Page thumbnails replaced what the tab looked like. The position was the one still missing, and reopening a long article at the top is reopening the page rather than reopening the tab.

The obvious implementation is to store a scroll offset and restore it. We did not, for two independent reasons, and the second one is the more interesting.

The permission problem

Restoring a pixel offset means running code inside the reopened page. The reopen happens from a notification click, which calls chrome.tabs.create(). That is not a user gesture on the new tab, so activeTab does not cover it. Covering it means asking for <all_urls> in the manifest — read and change all your data on all websites, shown at install time, on a product whose landing page says nothing leaves your device.

That is a worse install prompt than the one we had already demoted bookmarks out of. It was not going to happen for a scroll position.

The better reason

A pixel offset is also just wrong, on the merits.

It is wrong at a different window size. It is wrong again once lazy images have loaded and reflowed the article — which they always have, because a reminder fires days later on a fresh page load. It is wrong when the site ships a redesign. An offset encodes a fact about one rendering of a document, and you are trying to restore a fact about the document.

The thing that survives all of that is the text you were reading.

Text fragments

url#:~:text=start,end is a URL fragment the browser itself resolves: it finds the passage, scrolls to it and highlights it, during a navigation the product was making anyway.

So Latr stores the line of text that was at the top of the viewport when you saved, and reopens with it as a text fragment. No script in the reopened page, no host permission, no restore step at all — the browser does the work.

Capture is the free half. All four ways of saving a reminder are user gestures, so activeTab already permits reading the page at that moment, and scripting adds no warning of its own to the install prompt.

Four things that failed silently before live testing found them

Each of these was shipped-and-wrong at some point, and none of them threw.

Document order is not reading order. Wikipedia’s table of contents is sticky, sits in the markup before the article, and is always on screen. The first build anchored every Wikipedia reminder to the word Contents. The reader now skips fixed and sticky boxes, and nav, header, footer and aside.

Both ends of the directive must sit on a word boundary. A 200-character cap landed one end on problem of TC — and TC inside TCP never matches, so the fragment silently resolved to nothing. Capping is now one function, applied worker-side. A copy of that rule inside the injected function is a copy that drifts.

Neither end may carry whitespace. Same class of failure, same silence.

The reminder must never be worse off for trying. A missed match, a Firefox install, a file:// URL, a page that renders late — every one of them degrades to opening the page exactly as it did before the feature existed. And no anchor is stored at all for a page saved within a short distance of the top, which is most saves.

What to take from it

When you need to restore a position in a document, ask what the position is of. If the answer is “the layout”, you are storing something that expires. If the answer is “the content”, there is often a way to express it that the platform will resolve for you — and platform-resolved beats round-tripped, because it keeps working when the page changes underneath you.

It is also worth noticing how often a permission prompt is a design signal. The version of this feature that needed <all_urls> was the version that was going to be wrong anyway.

Stop losing the things you meant to come back to

Free, no account, and nothing leaves your device. About ten seconds to install, and one keystroke to use.

Also on Brave, Vivaldi, Arc and Opera — straight from the Chrome Web Store listing.