Back to articles

Safari 27's four web extension upgrades — and our forecast missed them

Safari 27 web extensions get runtime.getDocumentId(), user-gesture propagation, a tabId for windows.create and error reporting. What landed, what to do.

Maxim Kosterin
6 min read
A minimal hairline compass ring with a slender charcoal needle on paper-white; four small breaks in the ring are mended with short strokes of orange watercolor.
A minimal hairline compass ring with a slender charcoal needle on paper-white; four small breaks in the ring are mended with short strokes of orange watercolor.

Apple shipped Safari 27.0 on September 14. Three days later, WebKit published its Safari 27.0 feature roundup. Between the CSS and JavaScript headlines sat four changes for Safari 27 web extensions. If you ship a Safari build, all four deserve a look.

Here's the embarrassing part. When Chrome 154 shipped its async-listener fix last month, I could name the exact Chromium CL and the day our signal feed caught it. Safari 27 went the other way: our forecast pipeline first logged it on September 28, eleven days after the WebKit post. It never made a prediction (first_forecast_at was never set).

It wasn't hidden, either — Safari 27 had been in beta since June. My pipeline just doesn't watch Apple's release notes directly. That's a gap in my tooling, not proof that nobody saw it coming.

So here's the retrospective: what landed, how it compares, and what to do now.

What landed for Safari 27 web extensions

Per WebKit's post and Apple's Safari 27 release notes, Safari 27.0 adds:

  • runtime.getDocumentId() — returns the UUID of the document behind a window or a frame element (iframe, frame, embed, object).
  • Error reporting for extension scripts — Safari now reports uncaught exceptions and unhandled promise rejections from extension scripts.
  • User-activation propagation — a user gesture now carries through sendMessage(), connect(), postMessage(), and executeScript(). Gesture-gated actions, like media playback, can now work on the far side of a message hop.
  • tabId in windows.create() — you can move an existing tab into a new window in one call.

The release also brings platform changes extension code can use: a rewritten ES module loader that fixes top-level await, ReadableStream async iteration and transfer via postMessage(), maxAge in the Cookie Store API, and Secure cookies on plain-HTTP loopback hosts.

Mostly catch-up, with one surprise

Two of the four close gaps you can see in MDN's compatibility data. The other two change how extensions behave and how you debug them.

windows.create({tabId}) is the classic case. MDN's compat data lists tabId in windows.create() for Chrome since version 10 and Firefox since 45. It's the obvious call for any tab manager that pops a tab out into its own window. On Safari, that flow needed its own code path until now.

runtime.getDocumentId() is the surprise, because it's new. Firefox only added it in Firefox 153, released July 21, 2026. Chrome doesn't have it: its runtime API reference exposes document IDs on objects like MessageSender, but no lookup method. MDN's browser-compat-data lists Chrome as unsupported.

So Safari is the second browser to ship it, two months after Firefox. For once, Safari isn't last.

User activation is the one I'd call a real bug-class killer. Before 27.0, Safari didn't carry a gesture through those messaging calls. So "click the popup, play media from the content script" could fail on Safari. You can't feature-detect that, so the usual fix was a weaker Safari build.

And error reporting? Small bullet, real daily impact: a script that throws without a trace is the worst bug to chase.

What our catalog can and can't count

Careful here, because this number is easy to misread. Our catalog tracks the Chrome Web Store, Firefox Add-ons and Edge Add-ons — not Safari. So I can't count the Safari extensions these changes touch.

What I can count is the Manifest V3 pool on those three stores: 336,628 MV3 listings with a combined ~3.78 billion store-reported users. That's context, not an "affected" count. A listing on two stores counts twice. Users are summed per listing, so one person with five extensions counts five times.

The largest listings in that pool include uBlock Origin Lite, AdGuard AdBlocker, Adblock Plus, Grammarly, Google Translate, Microsoft Single Sign-On, McAfee WebAdvisor and Claude. Our data doesn't show which of them ship a Safari build. If yours does, you may get to delete some Safari-only branches.

One more number: 20,002 of those listings (~230M combined users) were already flagged unavailable in their store at our last scan. No Safari API helps an extension that's gone. Worth a quick check of your own toolbar (browse the catalog).

The honest caveat

Four platform changes don't add up to a Safari audience. Shipping to Safari still means the conversion toolchain, Xcode, Apple's review process and a separate distribution channel. None of that got cheaper with 27.0. What got cheaper is the code: fewer if (isSafari) { degrade() } branches, fewer stripped features, fewer "works in Chrome" tickets.

My read: API friction was one reason developers skipped Safari, and some of it just left the building.

What to do now

  1. Feature-detect, don't version-sniff. typeof globalThis.browser?.runtime?.getDocumentId === "function" is the safe check. Chrome doesn't ship the method, and older Safari versions stay in the wild.
  2. Already ship a Safari build? Test it on Safari 27.0 this week. Gesture-gated flows that used to fail may just work, and the new error reporting may surface bugs that were always there.
  3. Never shipped to Safari? Re-run the ROI math. The API gaps are smaller now; what's left is mostly packaging and review cost.
  4. Starting a fresh MV3 manifest? My free Manifest V3 generator checks it against Chrome, Firefox and Edge store policy as you type. Safari isn't one of its targets, so you'd still run the result through Apple's converter. The @extenshi/cli scanner runs a pre-publish security scan before you upload — 3 free scans with a free account, one-time, then prepaid packs.

See where your extension stands in the catalog → — its manifest version and store status on Chrome, Firefox and Edge.

Sources


Catalog numbers come from Extenshi's scans of MV3 listings and their store availability on the Chrome Web Store, Firefox Add-ons and Edge Add-ons, as of October 2026; counts shift as we re-scan. We don't track Safari. Technical claims come from the cited sources, which we don't independently verify. Naming an extension reflects its size in our catalog, not an accusation of wrongdoing. Spot an error? Tell us at [email protected].

Related Articles