November 2026
- Chrome 158 · scheduled for Nov 17, 2026
Scheduled for Chrome 158 (code merged 2026-10-14, before the 2026-10-26 branch point): action popup URLs must use the chrome-extension scheme. blob: and filesystem: URLs are rejected both in default_popup and in action.setPopup.
What to do: Use a normal extension page for the popup instead of a blob URL.
- 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.
- 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.
- 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.
- 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.
- 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).
- 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.
- 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
- Chrome 156 · Oct 20, 2026
Chrome 156 adds checks so chrome.tabCapture refuses tabs whose URL is blocked by enterprise policy or the user's site restrictions (and file: URLs without access) and disconnects an active capture when navigation reaches such a site.
What to do: Handle tabCapture failures and mid-capture disconnects for restricted 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.
- 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.
- 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.
- 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.
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.
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.
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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
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.
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.
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.
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.
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.
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.
- 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
- 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.
- 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.
- Chrome 152 · Aug 25, 2026
Chrome 152 adds a name property to AlarmCreateInfo, so alarms.create can take the alarm name in its options object, as agreed in the WebExtensions Community Group. The older name-as-first-argument form still exists.
What to do: Use {name} in the info object for code shared with other browsers, and keep the old form for Chrome < 152.
- Chrome 152 · Aug 25, 2026
Chrome 152 marks browsingData.removeAppcache(), removeWebSQL() and the appcache and webSQL data types as deprecated because the underlying web platform features are gone. The commit states there is no behaviour change.
What to do: Remove these data types from clear-data options.
- 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.
- 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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- Firefox 153 · Jul 21, 2026
Content scripts can now access document.adoptedStyleSheets and ShadowRoot.adoptedStyleSheets without .wrappedJSObject, which exposed the extension to interference from the page.
What to do: Remove wrappedJSObject-based access to adoptedStyleSheets when your minimum is Firefox 153.
- 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'.
- 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://.
- 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.
- 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.
- 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.
- Firefox 153 · Jul 21, 2026
userScripts.execute() injects one or more user script sources into a tab or frame on demand, in a defined order, as opposed to the persistent URL-pattern userScripts.register(). Contributed by a community member.
What to do: Replace register()/unregister() workarounds used for one-time injection with execute().
- 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
- 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.
- Chrome 150 · Jun 30, 2026
Starting in Chrome 150, chrome.alarms.create() enforces a 1024-byte limit on alarm names; a longer name throws a TypeError. Chrome 148 only logged a warning. A Finch kill-switch exists but should not be relied on.
What to do: Keep alarm names short (use IDs or hashes) and store extra data elsewhere.
- Chrome 150 · Jun 30, 2026
Chrome 150 documents chrome.offscreen.hasDocument(), which resolves to a boolean telling whether the extension has an active offscreen document. It avoids ad hoc checks before calling createDocument().
What to do: Call hasDocument() before createDocument() instead of try/catch or context lookups, and guard for Chrome < 150.
- 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.
- 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.
- 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.
- 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).
- 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.
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.
Chrome 149 documents PlatformNaclArch and the nacl_arch field of runtime.getPlatformInfo() as deprecated because Native Client was removed in Chrome 139. The field is still returned; the WECG is separately proposing to retire it.
What to do: Stop reading nacl_arch and use arch instead.
May 2026
- 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.
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).
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.
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.
March 2026
- Firefox 149 · Mar 24, 2026
action.isEnabled and browserAction.isEnabled now take tabId as a top-level parameter, like Chrome, and calling them with no argument returns the global state instead of throwing 'Incorrect argument types'.
What to do: Drop the {tabId} wrapper object and try/catch workarounds on Firefox 149+.
- 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.
- 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.
- 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
- 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.
- 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.
- 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
- 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.
- 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.
- 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'.
- 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.
November 2025
- 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.
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
- 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.
- 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.
- 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
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.
- 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.