What changed for extensions in Chrome, Edge and Firefox

92 platform changes from the past year that can break or change an extension without a line of its code changing — each with the version, the date, what to do and the source. We add new ones as they ship.

  1. Run it in your extension folder, or on a .zip, .crx or .xpi.
    npx @extenshi/cli@latest advise .
  2. It checks your code and manifest for the 38 changes marked extenshi advise below and prints the file and line of each match. It runs locally and needs no account.

Edge follows Chromium: Chrome entries apply to Edge too. List updated Oct 22, 2026 · also as JSON

92 of 92

November 2026

Breaks

DNR response-header value matching needs host permissions

  • Chrome 158 · scheduled for Nov 17, 2026

Scheduled for Chrome 158: DNR rules that match response header values (values/excludedValues in responseHeaders or excludedResponseHeaders) only work without host permissions for CORS-safelisted headers and Content-Encoding. Other rules are skipped for requests the extension has no host access to. Hidden headers are treated as absent in such conditions. The feature flag is enabled by default.

What to do: Request host permissions for sites whose response header values you match, or match only on header presence.

Behavior change

web_accessible_resources "*" no longer exposes _favicon and generated pages

  • Chrome 158 · scheduled for Nov 17, 2026

Scheduled for Chrome 158: generated paths such as _favicon and the generated background page are no longer matched by blanket wildcard web_accessible_resources entries, so web origins and content scripts can only load them if the extension declares them explicitly, for example "_favicon/*".

What to do: List "_favicon/*" in web_accessible_resources if content scripts or pages fetch favicons; stop using "*" wildcards.

Breaks

runtime.getPlatformInfo().arch returns 'arm64' instead of 'aarch64'

  • Firefox 160 · scheduled for Nov 10, 2026

On 64-bit ARM Firefox 160 returns arm64 for getPlatformInfo().arch and defines PlatformArch.ARM64 instead of AARCH64, matching Chrome. Code that checks for 'aarch64' (download selectors for native helpers, telemetry, architecture-specific wasm) stops matching on 64-bit ARM.

What to do: Accept both 'arm64' and 'aarch64' when picking binaries.

New API

runtime.MessageSender gets windowId (first browser to implement it)

  • Firefox 160 · scheduled for Nov 10, 2026

Firefox adds windowId to runtime.MessageSender (WECG #1077), so popups, side panels and devtools panels can learn the sender's window without going through sender.tab. When no window can be determined Firefox leaves the property absent.

What to do: Feature-detect with 'windowId' in sender; keep the sender.tab?.windowId fallback for other browsers.

New API

navigator.clipboard.writeText works in extension service workers

  • Chrome 157 · scheduled for Nov 3, 2026

Chrome 157 enables navigator.clipboard in extension service workers: a worker holding clipboardWrite can write to the clipboard, and revoking the permission applies to a running worker. Reads stay unsupported. This can replace an offscreen document created only for clipboard writes.

What to do: Test clipboard writes from the service worker and drop the offscreen document if it exists only for writing.

New API

protocol_handlers manifest key moves to the stable channel

  • Chrome 157 · scheduled for Nov 3, 2026

Chrome 157 enables ExtensionProtocolHandlers by default and changes the MV3 protocol_handlers manifest feature from dev to stable, so extensions can declare custom protocol handlers, including http(s) endpoints such as web mail or search.

What to do: Evaluate declarative protocol_handlers instead of runtime registration, and restrict handler URLs to your own resources or http(s).

New API

runtime.onExtensionLoaded reports why your extension became active

  • Chrome 157 · scheduled for Nov 3, 2026

Chrome 157 adds chrome.runtime.onExtensionLoaded, fired when the extension is added to the active set with a reason (install, update, browser_update, enable, startup, reload) and previousVersion on update. It unifies onInstalled, onStartup and the new onEnabled.

What to do: Use it for a single initialization hook, feature-detected; the reference page lists it as pending.

Breaks

X-Chrome-Manage-Accounts is hidden from extensions on Google account URLs

  • Chrome 157 · scheduled for Nov 3, 2026

From Chrome 157 the X-Chrome-Manage-Accounts response header on Gaia (accounts.google.com) URLs joins X-Chrome-ID-Consistency-Response in the hidden-header list. Extensions can no longer read, modify or inject it through webRequest or declarativeNetRequest, even with host permissions for accounts.google.com. Nothing errors: the header is simply not there.

What to do: Stop reading, injecting or modifying X-Chrome-Manage-Accounts on Google account URLs: the header drives Chrome's own account-management UI, and Chrome offers no extension API that replaces it. Test any flow that depends on it on Chrome 157 beta before stable on November 3, 2026.

Behavior change

webRequest and DNR miss navigations started from sites the user blocked extensions on

  • Chrome 157 · scheduled for Nov 3, 2026

From Chrome 157, navigations initiated by a site the user put under "block all extensions on this site" (including link clicks from it to allowed sites) are invisible to webRequest listeners and no longer match declarativeNetRequest rules, even permission-less block rules. No error is raised: the events never arrive. It only matters if your extension promises to see or block every navigation (blockers, parental controls, DLP, annotators).

What to do: Do not treat a missing event as a missing navigation. Test your webRequest and DNR flows on Chrome 157 beta against a user-restricted site, and adjust any product copy that promises full coverage before stable on November 3, 2026.

October 2026

Behavior change

DNR modifyHeaders cannot change the response headers Chrome hides from extensions

  • Chrome 156 · Oct 20, 2026

Chrome 156 makes declarativeNetRequest modifyHeaders follow the same hidden-header rule as webRequest: response-header modifications that target a header Chrome hides from extensions are dropped, while the rule's other modifications still apply. In Chrome 156 the hidden set is only the X-Chrome-ID-Consistency-Response (Dice) header on the Google accounts host; Chrome 157 adds X-Chrome-Manage-Accounts.

What to do: Do not rely on DNR to inject, overwrite or remove Google sign-in headers on accounts.google.com; other response-header rules are unaffected.

Behavior change

URL-scoped events respect user and policy blocked sites

  • Chrome 156 · Oct 20, 2026

Chrome 156 applies runtime host restrictions to events scoped to a URL, such as cookies.onChanged. Extensions with broad host permissions stop receiving those events, including HttpOnly cookie values, for hosts blocked by policy or by the user.

What to do: Do not assume cookies.onChanged covers every site; reconcile state after the user allows the site.

Breaks

webRequest redirects to file: URLs need file access

  • Chrome 156 · Oct 20, 2026

Chrome 156 rejects webRequest redirects to a file:// URL unless the user granted the extension access to file URLs. The redirect is refused and, if applicable, an extension with lower precedence may handle the request.

What to do: Do not redirect to file:// without the file-access permission; check and tell users to enable it.

Breaks

publicSuffix calls now error on invalid hostnames

  • Chrome 157 · scheduled for Nov 3, 2026 — hostnames with a leading dot (".example.com") are rejected
  • Firefox 158 · Oct 13, 2026 — isKnownSuffix() throws on any invalid hostname instead of returning false

Code that passes user- or URL-derived strings straight to publicSuffix now gets an error instead of a false or a best-effort answer. Firefox 158 throws on any invalid hostname (empty, leading dot, non-host strings); Chrome 157 rejects leading-dot hostnames such as cookie-domain values. An uncaught throw aborts the whole event handler it runs in.

What to do: Normalize and validate before calling: strip leading dots and reject empty or non-host strings. Or wrap every publicSuffix call in try/catch. One fix covers both browsers. Test on Firefox 158 now and on Chrome 157 beta before November 3, 2026.

New API

runtime.getVersion() replaces reading your own manifest.json

  • Chrome — implemented before Firefox (WECG implemented label, October 2025)
  • Firefox 158 · Oct 13, 2026
  • Safari — implemented before Firefox (WECG implemented label, October 2025)

With Firefox 158, runtime.getVersion() returns your extension's version in all three major browsers. Fetching your own manifest.json just to read the version is no longer needed for changelog popups, version-gated migrations or telemetry self-reporting.

What to do: Use runtime.getVersion() where your minimum browser versions allow it, and keep the manifest fetch as a fallback for older releases.

Behavior change

onMessage error responses no longer start with "Uncaught Error: "

  • Chrome 155 · Oct 6, 2026

Chrome 155 changes the error text delivered to a sender when a runtime.onMessage listener throws or returns a rejected promise: only the Error's message is sent, for example "test reject", instead of "Uncaught Error: test reject".

What to do: Stop matching on the "Uncaught Error: " prefix; compare error.message values or send structured error objects.

New API

runtime.onEnabled fires when your extension is re-enabled

  • Chrome 155 · Oct 6, 2026

Chrome 155 adds chrome.runtime.onEnabled, fired when the extension goes from disabled to enabled while the browser runs. Until now install/startup events did not cover a user turning the extension back on.

What to do: Re-initialize native host handshakes, license checks or sync in an onEnabled listener, guarded with a feature check for Chrome < 155.

New API

tabs.createSplit, tabs.unsplit and tabs.create({splitWithTabId}) manage split view

  • Chrome 155 · Oct 6, 2026

Chrome 155 documents chrome.tabs.createSplit(tabIds) and chrome.tabs.unsplit(splitViewId), plus a splitWithTabId option for tabs.create to open a tab in a split view with an existing one. The feature flag kApiTabsSplitView is on by default at tag 155. Earlier versions only exposed splitViewId on Tab.

What to do: Feature-detect the new methods before using them; tab managers can now arrange split views.

September 2026

Breaks

alarms.clearAll() no longer resolves to a boolean

  • Chrome 157 · scheduled for Nov 3, 2026
  • Firefox 157 · Sep 29, 2026

alarms.clearAll() now resolves to undefined instead of a boolean. Chrome used to resolve true every time, Firefox only when something was cleared. Any branch on the result is dead code on Firefox 157 already and on Chrome from Chrome 157 (stable November 3, 2026). WebKit merged the same change, so Safari returns undefined too.

What to do: Stop branching on the value clearAll() returns. If you need to know whether alarms existed, call alarms.getAll() before clearing. Check alarms.clear() call sites while you are there: it was not part of this change, but code that branches on one often branches on the other.

Behavior change

Themes render differently and some theme keys have no effect

  • Firefox 157 · Sep 29, 2026

Firefox 157 ships the Nova interface. The toolbar and sidebar sit flush with the window, the sidebar shares the theme background, the selected-tab shadow is gone and the accent color follows the OS. ntp_text no longer styles the new tab search box, popup_text no longer styles the address bar dropdown, sidebar_highlight and sidebar_highlight_text have no visible effect, and button_background_hover/active also apply to tab strip buttons.

What to do: Check light/dark, vertical tabs and sidebar states. Set tab_selected, tab_line, sidebar and sidebar_text, and use semi-transparent button hover colors. Image themes should set backgrounds_area (Firefox 156) and a toolbar color.

Behavior change

Error pages can no longer carry the failed URL's extension permissions

  • Chrome 154 · Sep 22, 2026

Chrome 154 ignores error documents (chrome-error://chromewebdata/) in extension permission checks. They cannot claim host permissions, receive activeTab grants, connect to externally_connectable extensions or spoof declarativeContent identities for the failed URL. Chrome 151 already blocked externally_connectable messaging from error pages.

What to do: Do not rely on extension features inside error pages; handle failed connections from web pages that failed to load.

Breaks

Manifest CSP validation rejects control characters

  • Chrome 154 · Sep 22, 2026

Chrome 154 makes the manifest CSP check reject content_security_policy strings (extension_pages and sandbox) that contain non-printable characters such as DEL or vertical tab, so the extension fails to load with an invalid manifest key error. Earlier validation accepted them while the runtime parser dropped directives, which could be used to bypass sandbox directives.

What to do: Make sure content_security_policy strings contain only printable ASCII and normal whitespace.

New API

Own-page tab metadata is readable without the "tabs" permission

  • Chrome 154 · Sep 22, 2026

Chrome 154 stops scrubbing url, title and favIconUrl of tabs that show the extension's own pages, so tabs.query can find the extension's own tab without the tabs permission. The tabs permission triggers a "Read your browsing history" warning.

What to do: If tabs is requested only to find or dedupe your own options/popup pages, remove it and bump minimum_chrome_version to 154; keep it for other browsers that still scrub.

New API

Theme backgrounds_area chooses where backgrounds are drawn

  • Firefox 156 · Sep 15, 2026

The theme manifest key gains backgrounds_area: 'window' draws images and gradients across the whole window, 'top_toolbars' restricts them to the top toolbars, and 'auto' (default) decides from additional_backgrounds_alignment. It is the main fix for image themes that look wrong next to the Nova sidebar.

What to do: For image themes made for a wide short toolbar set 'top_toolbars' together with a frame color.

Behavior change

Content scripts, webRequest and DNR no longer see privileged Chrome content

  • Chrome 153 · Sep 8, 2026

Chrome 153 stops delivering extension content scripts to privileged renderer processes (privileged WebContents), and hides their network requests from webRequest and declarativeNetRequest, including their own top-level navigation. Extensions can no longer observe, block or redirect that content.

What to do: Do not rely on injecting into or intercepting Chrome-owned privileged pages; no manifest change needed.

Breaks

chrome.cookies now respects user-blocked sites and withheld host permissions

  • Chrome 153 · Sep 8, 2026

Chrome 153 checks full page access, not just manifest host permissions, in the cookies API. When a user turns off extensions for a site or withholds host permissions, cookies.get, set and remove no longer work for that domain, which they did before.

What to do: Handle cookie API failures for restricted sites; request host access at runtime (permissions API) and show a message when the user has blocked the site.

Behavior change

Match patterns handle non-ASCII (UTF-8) paths correctly

  • Chrome 153 · Sep 8, 2026

Chrome 153 fixes URLPattern path matching for UTF-8 characters, including "/*" variants, invalid UTF-8 and case-insensitive matching. The commit notes it also fixes UTF-8 paths for web_accessible_resources. Patterns with non-ASCII paths may start matching, or stop matching, URLs that matched wrongly before.

What to do: Re-test content_scripts, host_permissions and web_accessible_resources entries that contain non-ASCII characters.

New API

MV3 alarms can fire as often as every second

  • Chrome 153 · Sep 8, 2026

Chrome 153 lowers the minimum alarm granularity of packed MV3 extensions from 30 seconds to 1 second, reasoning that the service worker stays alive 30 seconds anyway and alarms are cheaper than offscreen-document timer workarounds. The alarms API page still says 30 seconds.

What to do: If you run an offscreen document only to drive a short setInterval, evaluate alarms with short periods; keep a fallback for Chrome < 153.

Behavior change

Protocol handlers registered by extensions are removed with the extension

  • Chrome 153 · Sep 8, 2026

Chrome 153 ties protocol handlers registered from extension frames to the registering extension, so disabling or uninstalling the extension removes them. Before, a handler registered with elevated extension privileges could outlive its owner.

What to do: Re-register handlers if the extension is re-enabled and do not depend on handlers surviving uninstall.

Behavior change

webRequest extensions no longer start their service worker eagerly at launch

  • Chrome 153 · Sep 8, 2026

Chrome 153 removes the startup spin-up of service-worker extensions that hold the webRequest permission. Because filtered listeners are persisted, those workers stay dormant after a browser restart and wake up on demand when a matching request occurs.

What to do: Do not assume the service worker is running at browser start; initialize state lazily and register all listeners at the top level of the worker.

Behavior change

alert() in background pages no longer logs to the console

  • Firefox 155 · Sep 1, 2026

Firefox 155 removes the override that turned alert() in extension background pages into a console log (which also opened the Browser Console). MDN already documents that background pages do not support alert(), confirm() or prompt(). Code using alert() as a debug print loses that output.

What to do: Use console.log, or show UI through notifications or an extension page.

August 2026

Deprecated

Chrome Web Store removes all remaining Manifest V2 extensions (31 Aug 2026)

  • Chrome · Aug 31, 2026 — Chrome Web Store removal; MV2 stopped running in Chrome 138/139

The official MV2 timeline says all remaining MV2 extensions were removed from the Chrome Web Store on 2026-08-31. Existing installs can no longer receive updates or be reinstalled. MV2 extensions already stopped running in Chrome 138/139 (July/August 2025); later Chromium commits that delete MV2 flags and policies state there is no production behaviour change.

What to do: Ship an MV3 version; MV2 listings and updates are gone from the store.

Breaks

action.setBadgeText rejects text longer than 100 bytes

  • Chrome 152 · Aug 25, 2026

Chrome 152 limits the text passed to action.setBadgeText() (and browserAction.setBadgeText) to 100 bytes. Longer strings return the error "Badge text size is N bytes which exceeds the limit of 100 bytes." instead of being set. The switch was enabled by default and later removed.

What to do: Keep badge text to a few characters and truncate before calling; catch the rejection.

Behavior change

Corrupted storage.local databases are auto-reset on all channels

  • Firefox 154 · Aug 18, 2026

Bug 1992973 enables, on all channels, an automatic reset of the IndexedDB behind storage.local when Firefox detects known corruption cases that left extensions stuck with 'An unexpected error occurred'. Extensions that were permanently broken for some profiles start working again, with the stored data lost.

What to do: Treat empty storage.local after errors as possible; keep recoverable state in storage.sync or on a server, and re-initialise defaults.

New API

Manifest 'sandbox' key lets extension pages run eval in an opaque origin

  • Firefox 154 · Aug 18, 2026

Firefox supports the sandbox manifest key: listed pages load with an opaque origin and without direct access to extension APIs, and may use eval() and similar constructs that the extension CSP blocks. Chrome already supports the same key, so cross-browser extensions can share one setup.

What to do: Move eval or new Function work (templating, wasm, user code) into a sandbox page and talk to it with postMessage; delete Firefox-only workarounds when your minimum is 154.

July 2026

New API

devtools.inspectedWindow.eval and devtools.network.getHAR return promises

  • Chrome 151 · Jul 28, 2026

Starting in Chrome 151, chrome.devtools.inspectedWindow.eval(), inspectedWindow.getResources() and chrome.devtools.network.getHAR() (plus Resource/Request.getContent()) return promises. Callbacks keep working.

What to do: Use async/await in DevTools pages and drop callback wrappers when the minimum Chrome version allows.

Behavior change

DNR session rules stop eating the global static rule quota

  • Chrome 151 · Jul 28, 2026

Chrome 151 excludes session rulesets when computing rule counts, so session rules no longer reduce getAvailableStaticRuleCount() or the shared global static rule budget. The number returned grows for extensions that keep many session rules.

What to do: Do not hard-code assumptions about available static rules based on session rule usage; re-read getAvailableStaticRuleCount() when enabling rulesets.

New API

chrome.mimeHandler lets an extension handle PDFs and other MIME streams natively

  • Chrome 151 · Jul 28, 2026

Chrome 151 adds the chrome.mimeHandler API and the "mime_types_handler" manifest key. An extension (for example a PDF viewer) can register for a MIME type and keeps the original URL in the address bar, reuses the response Chrome already fetched, works for POST responses and embedded documents, and handles file: URLs without the file-access toggle. As of Chrome 151 only application/pdf is available to public handlers.

What to do: Document viewers that redirect navigations with webRequest or declarativeNetRequest should evaluate mime_types_handler. Feature-detect the API, since earlier versions ignore the key.

Behavior change

userScripts.execute() rejects invalid file sources immediately instead of hanging

  • Chrome 151 · Jul 28, 2026

Starting in Chrome 151, chrome.userScripts.execute() returns an error right away when its file sources fail validation (duplicate files, empty paths, files with an invalid MIME type). Before, the failed check was ignored and the returned promise never settled. The vendor "What's new" note claiming synchronous syntax validation with diagnostics in Chrome 149 is not reflected in the API reference or in any Chromium change found.

What to do: Handle rejections from userScripts.execute(), add timeouts only for real injection waits, and validate file lists before calling.

New API

chrome.publicSuffix API: query the browser Public Suffix List

  • Chrome 153 · Sep 8, 2026
  • Firefox 153 · Jul 21, 2026

Chrome 153 adds chrome.publicSuffix (permission "publicSuffix") with getDomain(), getKnownSuffix() and isKnownSuffix() to look up the registrable domain or public suffix of a hostname from the browser Public Suffix List. Extensions that bundle their own copy of the list can drop it. Chrome 157 makes hostnames with a leading dot invalid (separate item).

What to do: Add the publicSuffix permission, replace bundled PSL code with the API, validate inputs first, and keep a fallback for Chrome < 153.

Behavior change

Container colors renamed; use getSupportedColors() and getSupportedIcons()

  • Firefox 153 · Jul 21, 2026

contextualIdentities gets getSupportedColors() and getSupportedIcons(). The color names changed: 'turquoise' became 'cyan', 'toolbar' became 'gray', and 'violet' was added. The old names are still accepted as input but are normalised: create/update with 'turquoise' stores and returns 'cyan', and existing containers are migrated, so query()/get()/onUpdated now report 'cyan' and 'gray'. Code that compares color === 'turquoise' or 'toolbar', or maps colors from a hard-coded table, stops matching.

What to do: Compare against both old and new names (or normalise turquoise->cyan, toolbar->gray), and fill color/icon pickers from the two new methods instead of hard-coded arrays.

New API

documentId gives a stable document identifier across extension APIs

  • Firefox 153 · Jul 21, 2026

Firefox 153 adds documentId, including runtime.getDocumentId(), webNavigation events and methods, webRequest events, scripting injection targets and messaging APIs. If the frame's document changes, an operation that uses the ID fails instead of silently targeting the new document.

What to do: Prefer documentId over tabId plus frameId for long-lived references and injections. See MDN 'Work with documentId'.

Breaks

file:// access becomes a separate opt-in permission, off by default

  • Firefox 153 · Jul 21, 2026

Extensions can no longer read file:// URLs by default, including already installed ones. Users must enable the new 'Access local files on your computer' permission (desktop). extension.isAllowedFileSchemeAccess() now returns true when the user granted it (it always returned false before), and devtools.inspectedWindow.eval() on file:// URLs needs it too. Existing users lose local-file features after the upgrade until they flip the switch.

What to do: Detect with extension.isAllowedFileSchemeAccess(), show instructions (about:addons, Permissions and data), and degrade gracefully. Do not assume <all_urls> covers file://.

Behavior change

Local Network Access prompts are on for all users and reach content scripts

  • Firefox 153 · Jul 21, 2026

Firefox asks before a public website connects to localhost or the LAN: Strict ETP users in 147, a rollout to all users from 151, default for everyone in 153. Mozilla engineers say content-script requests behave like the page's; extension pages with host permission for localhost are exempt. Content scripts that call a local helper app from public pages can get a permission prompt or fail.

What to do: Make the localhost call from the background script or an extension page with host permission for it, and have content scripts message it. Handle fetch failures with a clear user message.

Breaks

tabs.captureTab/captureVisibleTab return blurred images of internal pages

  • Firefox 153 · Jul 21, 2026

The capture is now blurred (downscaled to at most 300 px with JPEG artifacts) when the tab shows a system-principal page such as an about: page or another add-on's page; with a rect option it returns a 1px placeholder. Mozilla called it a breaking change to a public API and did not uplift to ESR 140. Tab-preview and screenshot extensions will show blur for those tabs.

What to do: Treat blurred output as expected for internal pages; do not rely on pixel-accurate captures of about: pages. Skip those URLs in thumbnail logic.

New API

Theme additional_backgrounds accept CSS gradients

  • Firefox 153 · Jul 21, 2026

The theme manifest key's images.additional_backgrounds now accepts CSS gradients as well as image URLs, and a new properties.additional_backgrounds_size sets the size of each item. Older Firefox versions ignore it.

What to do: Use gradients instead of generated gradient images, and set strict_min_version only if the theme breaks without them.

Behavior change

webNavigation.onCommitted fires once for cross-origin iframe loads

  • Firefox 153 · Jul 21, 2026

Bug 1750196 (fixed 153) moves the webNavigation.onCommitted trigger for subframes from OnStateChange to OnLocationChange. As a result: the duplicate onCommitted for cross-origin iframe loads under Fission is gone; subframe onCommitted now fires when the document actually commits (later than before); and with redirects it reports the post-redirect URL instead of the pre-redirect one. Code that counted events, deduplicated by hand, or matched the pre-redirect URL in frames will see different events.

What to do: Remove duplicate-event workarounds once your minimum is 153, keep handlers idempotent, and match frame URLs from onCommitted as the final (post-redirect) URL.

June 2026

New API

alarms.create gets persistAcrossSessions to control alarm persistence

  • Chrome 150 · Jun 30, 2026

Chrome 150 adds the persistAcrossSessions flag to alarms. true (the default) persists the alarm until the extension updates; false clears it when the extension reloads or the browser restarts. Earlier versions behaved unpredictably, and other browsers do not support the flag yet.

What to do: Set persistAcrossSessions explicitly where behaviour matters, and keep re-creating important alarms at service worker startup for older Chrome and other browsers.

New API

contextMenus supports the "tab" context for the tab strip menu

  • Chrome 150 · Jun 30, 2026

Starting in Chrome 150, chrome.contextMenus accepts the "tab" context, adding items to the tab strip right-click menu. Items receive all selected tabs when several are selected. The "all" context does not include "tab".

What to do: Add contexts: ["tab"] where you want tab-strip actions, and check info and tab arguments for multi-selection. Feature-detect for older Chrome.

Behavior change

pageCapture.saveAsMHTML respects "block extensions on this site"

  • Chrome 150 · Jun 30, 2026

Chrome 150 fixes pageCapture so it checks whether the user blocked extension access to the page. Extensions that captured MHTML of such pages, with or without host permissions, now get no capture. Chrome 156 also validates subframes of the capture.

What to do: Handle a failed saveAsMHTML on restricted sites and tell users when a page is blocked.

Behavior change

Automatic dark-theme CSS filter on pageAction SVG icons is gone

  • Firefox 152 · Jun 16, 2026

Firefox no longer applies a greyscale/brightness CSS filter to SVG pageAction (address bar) icons on dark themes. Icons that relied on the filter now render in their own colors. Mozilla suggests @media (prefers-color-scheme: dark) inside the SVG, or theme_icons for the MV3 action.

What to do: Add a prefers-color-scheme: dark rule to the SVG, or provide theme_icons. Test with the about:config boolean extensions.webextensions.pageActionIconDarkModeFilter.enabled set to false before 152.

Breaks

executeScript/insertCSS into moz-extension pages is removed

  • Firefox 152 · Jun 16, 2026

Firefox 152 removes the ability to run code in an extension's own moz-extension: documents with tabs.executeScript/insertCSS/removeCSS and scripting.executeScript/insertCSS/removeCSS. Calls aimed at your popup, options page or other extension tabs now stop working.

What to do: Import scripts directly in the extension page's HTML, or register a runtime.onMessage listener in the document and send a message to trigger the code.

Breaks

SharedArrayBuffer is hidden from content scripts by default

  • Firefox 152 · Jun 16, 2026

Bug 2033743 (fixed 152) hides the SharedArrayBuffer global from the sandbox content scripts run in, unless the page itself is allowed to use it (cross-origin isolated). Before this it was exposed unintentionally on every page. A content script that names the global directly (new SharedArrayBuffer, instanceof SharedArrayBuffer) now throws ReferenceError. Shared WebAssembly.Memory still works and its buffer is still a SharedArrayBuffer (bug comment 8).

What to do: Feature-detect with typeof SharedArrayBuffer !== 'undefined', or reach the constructor through new WebAssembly.Memory({initial:1, maximum:1, shared:true}).buffer.constructor. Moving the code to an extension page does not help: unprivileged extension pages do not get SharedArrayBuffer either (bug 1673477 open).

Breaks

Web pages and content scripts can no longer start extension worker scripts

  • Firefox 152 · Jun 16, 2026

A web page or content script could create a Worker or SharedWorker from a web-accessible moz-extension: URL. From Firefox 152 such worker loads are treated as cross-origin unless the loader is the extension's own origin. Mozilla's tests assert that creating an extension worker from a content script is disallowed, and from an extension subframe it is allowed. The emergency rollback pref extensions.web_accessible_workers.deprecated_behavior (default false) is removed in Firefox 160 (bug 2074192).

What to do: Create the worker in an extension page or in a moz-extension: iframe that the content script inserts, and talk to it with messaging. Alternatively build the worker from a Blob/data URL in the page context.

Breaks

DNR redirect rules need file access for file: URLs

  • Chrome 149 · Jun 2, 2026

From Chrome 149 declarativeNetRequest rules (redirect) only act on requests from file: URLs, or redirect a request to a file: URL, when the extension has been granted access to file URLs by the user. Without it the rules are skipped.

What to do: Do not depend on DNR redirects involving file: URLs unless you tell users to enable "Allow access to file URLs" for the extension.

New API

Extensions can set the forbidden Origin header in fetch() (feature enabled)

  • Chrome 149 · Jun 2, 2026

Chrome 149 enables BypassRequestForbiddenHeadersCheck in the network and Blink layers. Extensions (and CDP) that are registered in the origin access list may set the Origin request header from fetch() if they choose; other fetch behaviour is unchanged. This can replace header-rewriting rules that existed only to spoof Origin.

What to do: If you use declarativeNetRequest modifyHeaders or webRequest only to set Origin on your own requests, test setting it directly in fetch(), with host permissions for the target.

May 2026

Behavior change

webRequest.onErrorOccurred error strings changed

  • Firefox 151 · May 19, 2026

The error property of webRequest.onErrorOccurred now falls back to NS_ERROR_NET_ON_RECEIVING_FROM instead of a string that starts with NS_ERROR_NET_ON_ or NS_ERROR_NET_UNKNOWN_. Mozilla describes it as part of performance and reliability work. Code that matches the old strings stops matching.

What to do: Do not branch on exact Firefox error strings; treat unknown values as a generic network failure and log them.

Behavior change

browser.* namespace is defined in Chrome 148; webextension-polyfill becomes a no-op

  • Chrome 148 · May 5, 2026

From Chrome 148 all extension APIs are also available as browser.*, pointing at the same objects as chrome.*, in content scripts, service workers and offscreen documents. webextension-polyfill skips wrapping when browser already exists, so it does nothing on 148+. Code that treats a defined browser global as proof of Firefox will take its Firefox branch on Chrome. Extensions that declare a devtools_page only get browser from Chrome 152.

What to do: Replace browser-sniffing of the form typeof browser !== "undefined" with feature detection, test polyfill-dependent code on 148+, and drop the polyfill once your users are on 148 (or set minimum_chrome_version to 148).

New API

Opt in to structured-clone serialization for extension messaging

  • Chrome 148 · May 5, 2026

Chrome 148 supports "message_serialization": "structured_clone" in manifest.json. Messages then use the structured clone algorithm instead of JSON, so Map, Set, BigInt, Date, Error, Blob and similar types survive runtime.sendMessage and ports. It is opt-in and extensions with different formats cannot message each other.

What to do: Add the manifest key if you can require Chrome 148+, remove manual serialization workarounds, and mind the limits: SharedArrayBuffer is not supported, transferables are copied, native messaging stays JSON.

New API

runtime.onMessage listeners can return a Promise

  • Chrome 148 · May 5, 2026

Chrome 148 lets runtime.onMessage listeners answer asynchronously by returning a Promise (or being async), with chrome.* and browser.* alike; return true plus sendResponse still works. It shipped together with the browser namespace because the polyfill used to provide this behaviour. It is not available in extensions that declare a devtools_page.

What to do: Simplify async handlers or remove the polyfill, and review existing async onMessage listeners: their resolved value is now what the sender receives. Keep return true where you must support Chrome 147 and older.

April 2026

New API

Extension pages can use WebAuthn with a host-permitted RP ID

  • Firefox 150 · Apr 21, 2026

Extension documents can call navigator.credentials.create()/get() with an RP ID for any domain covered by their host_permissions. Before this Firefox rejected a custom RP ID with a SecurityError. Servers validating the credential will see a stable moz-extension://hash origin in clientDataJSON, which differs from Chrome's chrome-extension://id.

What to do: If your extension offers passkey login for your own web service, add the RP ID host to host_permissions and teach the server to accept the Firefox origin format.

Breaks

tabs.update now throws when it would unhighlight every tab in a window

  • Chrome 147 · Apr 7, 2026

Chrome 147 changes tabs.update({highlighted: false}) so that trying to unselect the only highlighted tab (at least one tab per window must be highlighted) throws an error. Earlier versions silently ignored the request.

What to do: Do not unhighlight the last highlighted tab; wrap the call in try/catch or check tab.highlighted first.

March 2026

New API

action.openPopup() no longer needs a user gesture on desktop

  • Firefox 149 · Mar 24, 2026

action.openPopup() and browserAction.openPopup() open the popup without a user action on Firefox desktop, as in Chrome and Safari. You can open it from an alarm, native messaging event or background condition. Android is not covered.

What to do: Remove click-handler workarounds that only existed to get a gesture. Keep a fallback (notification or badge) for Firefox below 149 and for failed calls.

Deprecated

Injecting scripts or CSS into moz-extension pages is deprecated

  • Firefox 149 · Mar 24, 2026

Firefox 149 deprecates running code in the extension's own moz-extension: documents with tabs.executeScript/insertCSS/removeCSS and scripting.executeScript/insertCSS/removeCSS. Nightly stops it, beta and release log a console warning. The restriction applies to every channel from Firefox 152.

What to do: Load scripts from the page's HTML, or register a runtime.onMessage listener in the document and send a message to trigger the code.

Behavior change

openPopup() rejects when the target window is not focused or other UI is open

  • Firefox 149 · Mar 24, 2026

If windowId is passed to openPopup(), the window must be focused or the call rejects; call windows.update(windowId, {focused: true}) first. Bug 2022281 (fixed 149/150) also makes openPopup() reject while another panel or context menu is open. Combined with the removal of the gesture requirement, calls that used to be silently blocked now reject with an error you must handle.

What to do: Catch the rejection, focus the window first, and do not retry in a tight loop. Bug 2062229 (fixed 157) stops tab hover previews from counting as a blocking panel.

Behavior change

Split view adds tabs.Tab.splitViewId and changes tabs.move/group behavior

  • Firefox 149 · Mar 24, 2026

Firefox 149 exposes splitViewId in tabs.query, tabs.onUpdated and tabs.Tab. In 150 tabs.move can swap tabs inside a split view and unsplits a view when its tabs are listed non-adjacently; in 151 tabs.group, tabs.ungroup and tabs.move keep split views together instead of failing or separating them. Extensions that reorder or group tabs may now see split views disappear or move as a unit.

What to do: Handle tabs.SPLIT_VIEW_ID_NONE and the new property in tab managers, and re-test reorder, group and stash flows with a split view open.

Behavior change

window.structuredClone() in content scripts clones into the page realm

  • Firefox 149 · Mar 24, 2026

structuredClone() now instantiates objects in the realm it is called on, not the caller's. For compatibility the content-script global got its own structuredClone that shadows window.structuredClone. So a bare structuredClone(v) behaves as before (value lives in the content script), while an explicit window.structuredClone(v) now creates a value in the page scope.

What to do: Use window.structuredClone(v) when you want the page to read the value; use cloneInto(v, window) if you must support Firefox 148 and older. Re-test code that shares objects with page scripts through wrappedJSObject.

February 2026

Behavior change

Firefox 148 ships Trusted Types; content scripts are exempt, main-world code is not

  • Firefox 148 · Feb 24, 2026

Firefox 148 supports the Trusted Types API for web pages. Isolated-world content scripts are exempt from page Trusted Types policies (bug 1947732 fixed 140, bug 1986632 fixed 145 for document.write, eval, script enforcement and importScripts); worker eval/new Function cases are still open (bug 1990732). Mozilla states that MAIN-world content scripts cannot be exempted because they behave like page scripts, so on a page that sends require-trusted-types-for 'script', their innerHTML, eval and similar sink calls throw.

What to do: Test injection on pages that send Content-Security-Policy: require-trusted-types-for 'script'. Prefer isolated-world content scripts and avoid innerHTML or script-element injection into the page world.

New API

declarativeNetRequest can match on the top-level site (topDomains, excludedTopDomains)

  • Chrome 145 · Feb 10, 2026

Chrome 145 adds the topDomains and excludedTopDomains rule conditions, so a DNR rule can target requests by the domain of the top-level frame. It also adds the RuleConditionKeys enum for feature-detecting condition keys, and a topUrl field in TestMatchRequestDetails for testMatchOutcome(). Before, conditions could only look at the request initiator or the request URL.

What to do: Replace per-site rule toggling or broad initiatorDomains approximations with topDomains / excludedTopDomains where you need page-scoped rules. Guard with minimum_chrome_version 145.

Behavior change

sidePanel.close({tabId}) no longer falls back to the global panel

  • Chrome 145 · Feb 10, 2026

In Chrome 145 sidePanel.close() with a tabId closes only a tab-specific panel for that tab. If just the global panel is open the returned promise rejects with an error, where earlier versions closed the global panel.

What to do: Pass windowId to close a global panel, or catch the rejection when closing by tabId and retry with windowId.

January 2026

Behavior change

browsingData password removal became a silent no-op

  • Chrome 144 · Jan 13, 2026

Since Chrome 144, browsingData.removePasswords() and the passwords data type of browsingData.remove() do nothing: the call succeeds, the passwords are not deleted, and a console warning is logged. Extensions that promise to clear saved passwords no longer do so.

What to do: Remove the passwords option from clear-data features and stop telling users that passwords are cleared; direct them to chrome://settings/passwords.

Behavior change

MV3 webRequest listeners persist across restarts and wake the service worker

  • Chrome 144 · Jan 13, 2026

From Chrome 144 the WebRequestPersistFilteredEvents behaviour is on by default: filtered webRequest listeners of service-worker extensions are persisted and restored at browser startup. A matching network request can then start the worker instead of being missed while it was not running. Later releases (see the Chrome 153 item on startup spin-up) rely on this.

What to do: Register webRequest listeners synchronously at the top level of the service worker, keep the filter stable, and test cold-start request handling after a browser restart.

Behavior change

Clipboard read without clipboardRead shows a paste button

  • Firefox 147 · Jan 13, 2026

Without the clipboardRead permission, navigator.clipboard.readText() and read() in an extension now show a paste button to get user confirmation. With clipboardRead the call still reads silently. Extensions that read the clipboard without the permission, expecting a plain failure or a different prompt, now show a button the user has to click.

What to do: Request clipboardRead if silent reads are part of the product, or design the flow around the paste confirmation. See MDN 'Interact with the clipboard'.

Breaks

KeyboardEvent.initKeyEvent() stops working in extensions

  • Firefox 147 · Jan 13, 2026

initKeyEvent() was deprecated in Firefox 93 but kept working for add-on scripts through the preference dom.keyboardevent.init_key_event.enabled_in_addons. In 147 release builds that preference became false by mistake (bug 1848384), which broke old password-manager autofill. Mozilla then decided to keep it false everywhere (bug 2015079, target 149), as the first step to removing the method.

What to do: Replace initKeyEvent() with new KeyboardEvent(type, init). No workaround is promised for users.

December 2025

Behavior change

browsingData localStorage removal also clears sessionStorage

  • Firefox 146 · Dec 9, 2025

browsingData.removeLocalStorage() and browsingData.remove() with localStorage set now also delete sessionStorage objects (parity with the 'Clear cookies and site data' UI). Cleaner or privacy extensions that expected sessions to survive a localStorage clear will lose them.

What to do: If your extension must keep session data, do not call the localStorage clear for origins where that data matters, or restore what you need afterwards.

Breaks

Extensions can no longer open DevTools windows or tabs through extension APIs

  • Chrome 143 · Dec 2, 2025

Chrome 143 removes a carve-out that let extensions holding the devtools or debugger permission create a DevTools window or tab through the extension APIs that open tabs and windows. Chrome says opening DevTools should be left to the user, so those navigations are now blocked.

What to do: Stop opening devtools:// pages programmatically. Ask the user to open DevTools, or build the feature as a devtools_page panel.

November 2025

Breaks

cookies.set() now rejects invalid cookies

  • Firefox 145 · Nov 11, 2025

From Firefox 145 cookies created with cookies.set() are validated and invalid ones are rejected (the validator ran in Nightly only since 142). Extensions that built sloppy cookie values and relied on Firefox to accept them now see failed calls. MDN does not list which values count as invalid.

What to do: Wrap cookies.set() in try/catch, log the rejection and test with edge cases: name or value with illegal characters, mismatched prefix (__Secure-/__Host-) and attribute combinations, and bad domain/path.

Behavior change

New Firefox extensions must declare data collection in manifest.json

  • Firefox · Nov 3, 2025

From 2025-11-03 AMO requires every NEW Firefox extension to declare browser_specific_settings.gecko.data_collection_permissions (required: ["none"] when nothing is collected). Firefox shows the declared data types in the install prompt, on AMO and in about:addons. It does not change how already-published extensions run; Mozilla says all extensions will have to adopt it later, but I found no date.

What to do: Before submitting a brand-new Firefox extension add the key and declare the data your code or third-party SDKs really send out. For existing add-ons add it with your next release so you are ready when it becomes mandatory.

October 2025

New API

chrome.sidePanel.onClosed event reports when the side panel closes

  • Chrome 142 · Oct 28, 2025

Chrome 142 adds sidePanel.onClosed, fired whenever the extension side panel is closed or replaced. It reports windowId and path, plus tabId for tab-specific panels. Side panel extensions no longer have to infer closing indirectly.

What to do: Use sidePanel.onClosed to release per-panel state or sync UI. Feature-detect chrome.sidePanel.onClosed so the extension still loads on Chrome 141 and older.

New API

Declarative CSS injection can choose the user or author origin

  • Firefox 144 · Oct 14, 2025

Firefox 144 adds a css_origin key to manifest content_scripts entries and a cssOrigin property to scripting.registerContentScripts() (scripting.RegisteredContentScript) and contentScripts.register(), with values 'user' or 'author' (default 'author'). The origin value of scripting.insertCSS() also became case-insensitive. Extensions that inject user-origin stylesheets only through insertCSS/tabs.insertCSS calls can now declare that origin statically.

What to do: If you inject user-origin CSS imperatively (insertCSS with origin/cssOrigin 'user') only to get it declared at document load, move it to content_scripts with "css_origin": "user" or to a registered script with cssOrigin: 'user'. Remember user-origin rules only beat page rules when they are !important. Set strict_min_version 144 or keep the imperative path for older Firefox.

New API

storage.local and storage.managed get getBytesInUse()

  • Firefox 144 · Oct 14, 2025

Firefox 144 implements getBytesInUse() on storage.local and storage.managed, matching Chrome. Before this the call was not supported in those areas in Firefox, so cross-browser code had to guard it or estimate sizes by hand.

What to do: Remove Firefox-specific fallbacks that estimate storage size (for example JSON.stringify of storage.local.get(null)) when your minimum Firefox version is 144 or higher.

Date not pinned

Behavior change

Extensions on Googlebook OS: getPlatformInfo says android, nativeMessaging fails

  • Chrome

Chrome extensions run on Googlebook OS (Android stack with ChromeOS foundations) but runtime.getPlatformInfo().os returns "android" on a laptop, native messaging connections fail, commands read the linux key bindings, and ChromeOS-only APIs are undefined. Platform checks and native host flows can misfire.

What to do: Use feature detection; combine getPlatformInfo with navigator.userAgentData.mobile when you must tell Googlebook from a phone; handle nativeMessaging failures with a web fallback.

Deprecated

Edge starts turning off Manifest V2 extensions for consumers (2026-2027)

  • Edge — gradual: notices from August 2026, then off by default over the following months

Microsoft announced on 2026-08-07 that Edge begins the consumer transition away from MV2 in August 2026: notices first, then MV2 extensions turned off by default over the following months in Canary, Dev and Beta before Stable, aiming to finish by the end of 2026. Enterprise deprecation follows in early 2027. Edge docs also list deprecation warnings for some users from mid-September 2026.

What to do: Submit an MV3 build through Partner Center now; MV2 extensions will be disabled by default for consumers before the end of 2026.