-
Notifications
You must be signed in to change notification settings - Fork 32
Manifest V3
Save in 4.0.0 is a Manifest V3 extension on both Firefox and Chrome, from a single manifest.json.
Dear developer,
We’re concluding the transition to Chrome Manifest V3 with the removal of all remaining Manifest V2 extensions from the Chrome Web Store on August 31, 2026.
(…)
– The Chrome Web Store Team
- Chrome removed Manifest V2. Chrome disabled MV2 extensions, so an MV3 build is required to stay on the Chrome Web Store. This was the forcing reason.
-
One manifest for both browsers. Firefox supports MV3 too, so a single MV3
manifest.jsonnow serves Firefox and Chrome instead of a per-browser setup — less divergence to maintain. - AI got good enough to maintain unmaintainable legacy software — I wouldn't have had the time to untangle this mess otherwise.
-
Firefox 121+ — runs the background as an event page (
background.scripts). -
Chrome / Edge 121+ — runs the background as a service worker (
background.service_worker).
Older browsers are not supported. If downloads stop working after an update, check your browser version first.
- One MV3 manifest serves both browsers; each ignores the other's
backgroundkey. - The background is non-persistent on both — it sleeps when idle and wakes on events. In-flight downloads, the pending counter, and the recovery filename are persisted to session storage so a mid-download restart still fires notifications and keeps the routed name.
- Feature detection, not browser sniffing, picks each mechanism:
| Feature | Firefox (event page) | Chrome (service worker) |
|---|---|---|
| Referer header |
declarativeNetRequest session rule |
declarativeNetRequest session rule (same) |
| Blob downloads |
URL.createObjectURL (event pages have a DOM) |
createObjectURL in an offscreen document; base64 data: URL if unavailable |
See Firefox vs Chrome for the full per-browser breakdown.
WebExtension features removed or changed in Manifest V3, and how Save in adapts:
| Removed / changed | Replacement | In Save in |
|---|---|---|
| Persistent background page | Non-persistent service worker (Chrome) / event page (Firefox) | Download-tracking state persisted to session storage |
Blocking webRequest (Chrome only) |
declarativeNetRequest |
Referer header uses a declarativeNetRequest session rule on both browsers; webRequest/webRequestBlocking are no longer requested (Chrome MV3 restricts webRequestBlocking to policy-installed extensions) |
runtime.getBackgroundPage() |
Messaging (runtime.sendMessage) |
The options page talks to the background over messages |
window / document / URL.createObjectURL in the background (Chrome) |
offscreen document; data-URL fallback | Large fetched files get a blob URL from an offscreen document; base64 data: URL if it is unavailable |
Cross-origin fetch from content scripts |
Background fetch (host permission) |
Fetch via content script deprecated → use Fetch via Fetch API |
| Remotely hosted code | Bundled only | N/A — all sources are first-party and unminified |
Manifest V3 changes how Firefox treats host permissions — the sites an extension may access (Save in requests <all_urls>, needed for the Referer feature and click-to-save). Under MV2 a required host permission was granted at install and could not be revoked without uninstalling. Under MV3 host permissions are opt-in and revocable at any time from about:addons. Early Firefox MV3 (before Firefox 127) did not even show them in the install prompt, so a freshly installed extension could sit silently unable to access pages. Since Firefox 127 (June 2024) host permissions are shown in the install prompt and granted when the user accepts.
Upgrading from the last MV2 release (3.7.3) does not lose host access. When Firefox updates an extension it preserves the existing permission state and migrates a previously-granted MV2 required host permission into MV3's granted-origins store. The update applies with no prompt and the extension stays enabled — nothing to re-grant.
This was verified directly against Firefox's own permission store: installing 3.7.3 (MV2) and then updating to the MV3 build (same add-on ID) leaves <all_urls> granted, with the add-on active and no permission prompt shown. A control confirmed the update path preserves rather than re-grants — revoking <all_urls> and then updating to a higher version keeps it revoked — so the carried-over grant is a genuine migration of the prior MV2 permission, not an install-time default. (A brand-new install on a pre-127 Firefox is the only case that starts out un-granted; it does not affect anyone upgrading from a current version.)
Chrome does not use Firefox's opt-in model: host permissions declared in the manifest are granted by default. A user can optionally narrow site access (Chrome's runtime host permissions — "on all sites" / "on specific sites" / "on click"), but an extension that requests <all_urls> defaults to full access. There is no "disabled by default" state to work around.
The upgrade risk on Chrome is instead permission-warning escalation: when a Web Store update requests a set of warning-triggering permissions that is a superset of the previous version's, Chrome disables the extension until the user re-approves. If the warnings are the same or a subset, the update applies silently. The MV2 → MV3 change does not escalate:
| Permission | MV2 3.7.3 | MV3 4.0.0 | Warning change |
|---|---|---|---|
<all_urls> |
permissions |
host_permissions |
same "all sites" warning — only the manifest key moved |
tabs, downloads
|
warned | warned | none |
declarativeNetRequestWithHostAccess |
— | added | none (uses existing host access) |
offscreen |
— | added | none |
So the update applies without disabling and the existing site-access grant carries over. (Chrome force-disabled the MV2 build during the MV2 sunset; publishing MV3 re-enables it on auto-update.)
Unlike Firefox, this can't be reproduced locally with fidelity — Chrome's update-permission behavior lives only in the Web Store pipeline (unpacked/CDP loads grant everything and skip the escalation logic). What is verified locally is that the MV3 build loads and runs on Chrome (the chrome end-to-end suite). The authoritative check for the update itself is the Chrome Web Store Developer Dashboard, which flags at upload time if a new version adds permissions that would trigger re-approval.
- Nothing to reconfigure — settings and menus carry over.
- The Referer feature uses different engine internals per browser but the same option and site whitelist.
- Downloads via the Fetch API fallback go through an offscreen document on Chrome — it has a DOM and
createObjectURL, so large fetched files become a blob URL instead of being base64-buffered into adata:URL. If the offscreen document can't be created, it falls back to thedata:URL path. Firefox event pages havecreateObjectURLdirectly, so no offscreen document is needed.