Yu'an Zhou

Building a Chrome extension that saves items from arbitrary third-party sites means every site is a hostile, unstable environment. Here is what actually broke, and what held.

Manifest V3 changes the shape of everything

The persistent background page is gone. Service workers terminate — aggressively, after roughly 30 seconds of inactivity — and any in-memory state dies with them.

Anything that must survive goes into chrome.storage:

// ❌ dies with the worker
let cache = {}

// ✅ survives
await chrome.storage.local.set({ cache })

Enter fullscreen mode Exit fullscreen mode

This is the single most common source of "works when I test it, fails in the wild" bugs. Your worker is alive during active testing and dead during real usage patterns.

Content script isolation cuts both ways

Content scripts run in an isolated world: you see the DOM, not the page's JavaScript. You cannot read the page's variables, and the page cannot see yours.

Good for safety, awkward when the data you want lives in a framework's state rather than the DOM. Options are injecting into the main world (more risk, more capability) or scraping rendered DOM (fragile but isolated). I default to DOM scraping and accept the fragility.

Selectors rot, so degrade instead of breaking

Third-party markup changes without warning. A selector working today breaks next week with no notice and no error you will see.

What helps:

  1. Layered fallbacks — semantic markup first (article, [itemprop], OpenGraph tags), site-specific selectors only as a last resort
  2. Fail visibly, not silently — if extraction fails, tell the user rather than saving an empty record
  3. Never assume shapeel?.textContent?.trim() ?? '' everywhere; a null deref in a content script can break the host page, which is much worse than your feature not working

Permissions: ask for less

<all_urls> triggers heavier review and scares users at install time. activeTab plus optional_host_permissions requested at first use is a better trade — fewer install-time objections, and review goes faster.

Store review realities

  • Justify every permission in the listing, specifically. Vague justifications get rejected.
  • Privacy policy required if you touch user data at all.
  • First review is slow; updates are usually much faster.
  • A permission added later triggers full re-review — worth batching permission changes rather than shipping them one at a time.

I shipped one of these for BabyNameAi — cross-site saving, a daily classical-poetry card, and offline lookup.

Caveat

Extension APIs are still shifting under MV3. Anything you read from before 2023 — including plenty of still-top-ranked results — may describe APIs that no longer exist.