Skip to content
ClipView
Engineering

Why clipboard extensions miss copies (and how to catch them all)

A Chrome extension cannot catch every copy with one API. Here are the three separate paths a clipboard manager needs — the copy event, an offscreen reader, and a focus fallback — and why each one exists.

If you have used a clipboard manager and noticed it mostly works — catching copies from web pages but silently missing others — this is why.

There is no single Chrome API that sees every copy. Getting full coverage means wiring up three separate mechanisms, each one covering a case the others cannot.

Path 1: the copy event

The obvious one. A content script listens for copy on the page:

document.addEventListener('copy', (event) => {
  const text = window.getSelection()?.toString();
  // hand it to the background worker
});

What it covers: any copy made inside a web page — Ctrl/Cmd + C, a copy button, a context menu inside the page.

Why it is the best path when it applies: the event carries the data synchronously. No permission prompt, no focus requirement. It works on a tab the user has never clicked into.

What it misses: everything that does not happen inside a page.

Path 2: copies from the address bar

Select the URL in the omnibox, hit Cmd + C. No page was involved, so no copy event fires anywhere your content script can hear it.

The instinct is to reach for navigator.clipboard.readText(). That does not work either, because the Clipboard API requires the reading document to be focused — and while the user is interacting with the omnibox, no page has focus. By the time a page regains focus, you have missed the moment.

The way through is an offscreen document: a hidden page the extension owns, which can hold focus independently of any tab and poll the clipboard.

await chrome.offscreen.createDocument({
  url: 'offscreen.html',
  reasons: ['CLIPBOARD'],
  justification: 'Read copies made outside any page',
});

This is the only path that sees address-bar copies. It is also the reason a clipboard manager needs the clipboardRead permission at all.

Path 3: right-click → Copy

Select text on a page, right-click, choose Copy from Chrome’s native context menu. Surprisingly, this fires no copy event in the page. The browser performs the copy itself, outside the page’s event model.

The fallback is to check the clipboard when the page regains focus or visibility — after a context menu closes, focus returns, and that is the moment to compare the clipboard against the last thing recorded.

Putting it together

All three feed the same handler, which is where the real work happens:

Deduplication. The paths overlap. An in-page copy may be seen by the copy event and the poller a moment later. Matching on text content and bumping the existing entry — rather than appending a duplicate — keeps the list clean.

Self-copy suppression. When the user copies an item out of the manager to paste elsewhere, that is a clipboard write the extension caused. Without an explicit signal, it gets re-captured as a brand new item, and the history fills with copies of itself.

Size limits. Every stored item lives in the panel’s UI state and is re-rendered on each capture and keystroke. One pathological copy — a minified bundle, a huge spreadsheet — costs far more than its bytes on disk. A per-item cap (ClipView skips anything over 512 KB) matters more than it sounds like it should, because a history cap counts items, not size.

Write ordering. A burst of copies can race each other into storage. Serialising writes through a queue avoids losing entries to a lost update.

Why this is worth knowing

If you are evaluating a clipboard manager, try all three cases explicitly:

  1. Copy from inside a page
  2. Copy the URL from the address bar
  3. Select text, right-click, Copy

If any of them does not show up, the extension is only implementing part of the picture. ClipView wires up all three — it is most of the reason the extension needs the permissions it does.