WebExtensions API Changes (Firefox 149-152)

Intro

Hey everyone, we’ve been working on some exciting changes, and want to share them with you.

But first, let me introduce myself. I am Christos, the new Sr. Developer Relations engineer in Add-ons, and I’m excited to write my first post on the Add-ons engineering blog.

Deprecations and changes

To start, I’m looking at a couple of features that are going away: avoiding content script execution in extension contexts, decoupling file access from host permissions, and improving the display of pageAction SVG icon.

executeScript / registerContentScript in moz-extension documents

Deprecated: Firefox 149  Removed: Firefox 152

Starting in Firefox Nightly 149 and scheduled for Firefox 152, the scripting and tabs injection APIs no longer inject into moz-extension://documents. This change brings the API in line with broader efforts to discourage string-based code execution in extension contexts, alongside the default CSP that restricts script-src to extension URLs and the removal of remote source allowlisting in MV3 (bug 1581608).

Firefox emits a warning when this restriction is met, so you are aware of and can address any use of this process in your extensions. This is an example of the warning message:

Content Script execution in moz-extension document has been deprecated and it has been blocked

To work around this change,  you can:

  • Import scripts directly in the extension page’s HTML.
  • Use module imports or standard <script> tags in extension documents.
  • Restructure code to avoid dynamic code execution patterns. An extension can run code in its documents dynamically by registering a runtime.onMessage listener in the document’s script, then sending a message to trigger execution of the required code.

File access becomes opt-in

Target: Firefox 152

Extensions requesting file://*/ or <all_urls> currently trigger the “Access your data for all websites” permission message, and when granted, can run content scripts in file:-URLs. From Firefox 152, file access in extensions requires an opt-in for all extensions, including those already installed (bug 2034168).

pageAction SVG icon CSS filter (automatic color scheme)

Removed: Firefox 152

Firefox has been automatically applying a greyscale and brightness CSS filter to pageAction (address bar button) SVG icons when a dark theme is active. This was intended to improve contrast, but it actually reduced contrast for multi-color icons and caused poor visibility for some extensions, such as Firefox Multi-Account Containers.

For icons that adapt to light and dark color schemes, you can now use @media (prefers-color-scheme: dark) in the SVG icon, or the MV3 action manifest key, and specify theme_icons.

Here is an example of how to use a `prefers-color-scheme` media query in a pageAction SVG icon to control how the icon adapts to dark mode:

manifest.json

"page_action": {
  "default_icon": "icons/icon.svg"
}

icons/icon.svg

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
  <style>
    :root { color: black; }
    @media (prefers-color-scheme: dark) { :root { color: white; } }
  </style>
  <path fill="currentColor" d="M2 2h12v12H2z"/>
</svg>

Use of prefers-color-scheme media queries is also allowed in MV2 browserAction and MV3 action SVG icons as an alternative to the theme_icons manifest properties.

There are additional examples at the Mozilla Developer Network on how to test your extension pageAction icon with and without the implicit CSS filter.


New APIs & Capabilities

Now to the new stuff. Here, you get the ability to use popups without user activation, initial support for the new tab split view feature, and WebAuthn RP ID assertion.

openPopup without user activation (Firefox Desktop)

Available: Firefox 149 Desktop

action.openPopup() and browserAction.openPopup() no longer require a user gesture on Firefox Desktop. You can open your extension’s popup programmatically, e.g., in response to a native-messaging event, an alarm, or a background-script condition.

This change is part of the ongoing cross-browser alignment work in the WebExtensions Community Group to harmonize popup behavior across engines.

Example

Before (Firefox < 149): must hang off a user gesture, e.g., a context menu click:

browser.menus.create({
  id: "nudge",
  title: "Open popup",
  contexts: ["all"],
});

browser.menus.onClicked.addListener((info) => {
  if (info.menuItemId === "nudge") {
    browser.action.openPopup(); // user clicked the menu → allowed
  }
});

 

After (Firefox ≥ 149) — same intent, no user gesture needed, fires from a timer:

browser.alarms.create("nudge", { delayInMinutes: 1 });

browser.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === "nudge") {
    browser.action.openPopup(); // works without a click
  }
});

It’s the same call with the same result, but only the trigger changes from a user-action handler to any background event.

It’s the same call with the same result, but only the trigger changes from a user-action handler to any background event.

splitViewId in the tabs API

Available: Firefox 149

Firefox 149 introduces a new read-only splitViewId property on the tabs.Tab object to expose Firefox’s new split view feature (where two tabs are displayed side-by-side in one window). Split views are treated as one unit, and Web Extensions treat them the same way.

In Firefox 150, extensions can swap tabs within a split view. This update also resolves a confusing issue where using the user interface to reverse tab order incorrectly reports the tabs.onMoved event with inaccurate values. Additionally, Firefox introduces unsplitting behavior for web extensions: when tabs.move() is called with split-view tabs positioned separately (non-adjacently) in the array. Now, after the call, Firefox removes the split view rather than keeping the tabs locked together.

Here is an example of using the new splitViewId property.

// Log whenever a tab joins or leaves a split view.
browser.tabs.onUpdated.addListener((tabId, changeInfo) => {
  if (!("splitViewId" in changeInfo)) return;

  if (changeInfo.splitViewId === browser.tabs.SPLIT_VIEW_ID_NONE) {
    console.log(`Tab ${tabId} left its split view`);
  } else {
    console.log(`Tab ${tabId} joined split view ${changeInfo.splitViewId}`);
  }
});
// Firefox desktop also supports a filter to limite onUpdated events:
// }, { properties: ["splitViewId"] });

 

Firefox 151 enables extensions to move split views in tab groups. More improvements are coming, such as the ability to create split views from extensions (bug 2016928).

 

WebAuthn RP ID assertion

Available: Firefox 150

Previously, web extensions couldn’t use WebAuthn credentials registered on their company’s website or mobile apps. When extensions tried to set a custom Relying Party ID (RP ID) in navigator.credentials.create() or navigator.credentials.get(), Firefox rejected it with “SecurityError: The operation is insecure.”

With Firefox 150, Extensions can now assert a WebAuthn RP ID for any domain they have host permissions for

when calling navigator.credentials.create() or navigator.credentials.get(). This applies to both the publicKey.rp.id field during credential creation and the publicKey.rpId field during authentication.

A critical detail for server-side validation: When relying party servers validate credentials created by extensions, they must account for different origin formats across browsers. In Chrome, the origin follows the pattern chrome-extension://extensionid, which matches the extension’s location.origin. Firefox 150 introduces a new stable origin format: moz-extension://hash, where the hash is a 64-character SHA-256 representation of the extension ID (using characters a-p to represent hex values). Importantly, this hash-based origin is the same all users, unlike Firefox’s existing UUID-based moz-extension:// URLs used for extension documents.

To extract the origin from a credential for validation:

let clientData = JSON.parse(new TextDecoder().decode(
  publicKeyCredential.response.clientDataJSON
));
console.log(clientData.origin);

For more details, see Use Web Authn API in web extensions on MDN.

Summary

Change Type Firefox Version
executeScript / registerContentScript in moz-extension documents Deprecation → Removal Deprecated 149, removed 152
File access opt-in Change 152
pageAction SVG CSS filter Removal 152
openPopup() without user activation New capability 149 (Desktop only)
splitViewId on tabs.Tab New API 149
WebAuthn RP ID assertion New capability 150

Need more?

You can always find detailed information about WebExtensions API and Add-ons updates in the MDN release notes, e.g., for Firefox 149 and Firefox 150.

For any help or questions navigating any changes, don’t hesitate to post your topic on the Add-ons Discourse.

 

No comments yet

Post a comment

Leave a Reply

Your email address will not be published. Required fields are marked *