Firefox 148: AI Controls, WebGPU in Service Workers, and a Safer DOM
Firefox 148 (February 24, 2026) adds centralized AI feature management, WebGPU support in Service Workers, Trusted Types and the Sanitizer API for XSS defense, Iterator.zip(), and key CSS additions. Here's what actually matters for developers.
Firefox 148 shipped on February 24, 2026, with a mix of user-facing AI controls and developer-relevant platform additions. Here's a focused breakdown of what changed.1
AI Controls: A Central Panel for AI Features
The headline user feature is a new AI Controls section in Firefox Settings. Mozilla is adding AI-enhanced features to the browser — things like text summarization, translation assistance, and context-based suggestions — and wants a single place where users can see and manage all of them.
What this means in practice: AI features in Firefox are opt-in by default. Users who don't want any of them can turn them all off from one place rather than hunting through individual settings. The framing is explicitly privacy-first: this isn't a kill switch for something that was already running, it's a visibility layer for features that haven't yet been turned on.
Separately, Firefox 148 decouples "Remote Improvements" (background browser tweaks rolled out by Mozilla) from telemetry requirements. Previously, opting out of telemetry also blocked you from receiving these updates. That coupling is now broken — you can receive remote improvements while still keeping telemetry off.
WebGPU in Service Workers
Service workers can now use the WebGPU API. This completes WebGPU's availability across all worker contexts (dedicated workers already had it).
The practical payoff: GPU compute can now run in the background, outside of any visible tab. An extension can perform image processing, ML inference, or heavy computation in a service worker that persists across tab switches, without blocking the main thread of any page.
For multi-tab extensions and progressive web apps that share state across contexts, this is meaningfully useful. The canonical example from Mozilla: extensions that need to analyze or transform content from multiple tabs can do GPU-accelerated processing in a single service worker instead of spinning up per-tab compute.
Trusted Types API: Enforcing XSS Discipline at the API Level
Firefox 148 ships the Trusted Types API. It's a mechanism for sites to enforce that certain dangerous DOM sinks — innerHTML, eval(), setTimeout() with string arguments, src attributes — only accept values that have been processed by a registered transformation function (a "policy").
The key property: Trusted Types doesn't mandate how you sanitize — it just requires that you go through a named policy. This makes dangerous DOM operations auditable. You can trace every injection point to a specific policy function, which makes security audits tractable in large codebases.
// Define a policy
const policy = trustedTypes.createPolicy('my-policy', {
createHTML: (input) => DOMPurify.sanitize(input),
});
// Now this works — goes through the policy
element.innerHTML = policy.createHTML(userInput);
// And this throws if Trusted Types is enforced
element.innerHTML = userInput; // TypeError
Enforcement is configured via a Content-Security-Policy: require-trusted-types-for 'script' header. Without enforcement, Trusted Types is available but not required — useful for auditing.
HTML Sanitizer API: Safe DOM Insertion
Alongside Trusted Types, Firefox 148 also ships the HTML Sanitizer API, which provides the missing native primitive for safe HTML injection:
element.setHTML(string)— likeinnerHTML = string, but safe. Strips script elements, event handler attributes, and other injection vectors before inserting.document.parseHTML(string)— parses HTML safely into aDocumentobject without executing scripts.
// Old way — dangerous
element.innerHTML = untrustedHTML;
// New way — safe
element.setHTML(untrustedHTML);
// Parse without side effects
const doc = document.parseHTML(untrustedHTML);
The sanitizer is configurable via a Sanitizer object — you can specify which elements and attributes to allow or deny. The defaults are conservative: script elements, event handlers, and navigation-risky attributes are blocked.
JavaScript: Iterator.zip() and Iterator.zipKeyed()
From the TC39 joint iteration proposal: Iterator.zip() and Iterator.zipKeyed() are now available.
Iterator.zip() takes multiple iterators and returns a new iterator that yields arrays of same-index elements — like Python's zip():
const nums = [1, 2, 3].values();
const letters = ['a', 'b', 'c'].values();
for (const [num, letter] of Iterator.zip(nums, letters)) {
console.log(num, letter); // 1 'a', 2 'b', 3 'c'
}
Iterator.zipKeyed() works the same way but takes an object instead of individual iterators, yielding objects with the same keys:
const result = Iterator.zipKeyed({ x: [1,2,3].values(), y: [4,5,6].values() });
// yields { x: 1, y: 4 }, { x: 2, y: 5 }, { x: 3, y: 6 }
Useful for positionally-aligned datasets — pairing X and Y coordinates, matching request-response pairs, combining parallel data streams.
CSS: shape(), position-try-order, and overflow on Images
shape() function: CSS now has a proper shape definition function for clip-path and offset-path that uses standard CSS syntax. The existing path() function uses SVG path syntax — strings like "M 10,10 L 100,10" — which can't use CSS units or calc(). The new shape() uses CSS syntax throughout:
.clipped {
clip-path: shape(
from 10px 10px,
line to 100% 10px,
arc to 100% 100% of 50px,
line to 10px 100%,
close
);
}
Because it uses CSS units, the shape can be responsive — percentages, viewport units, and math functions all work.
position-try-order: Part of CSS Anchor Positioning, this property controls which fallback position is tried first when an anchored element doesn't fit. Previously @position-try fallbacks were tried in declaration order; now you can prioritize by available space:
.tooltip {
position: absolute;
position-anchor: --button;
position-try-order: most-height; /* try the option with the most vertical room first */
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
}
overflow on replaced elements: Images, videos, and other replaced elements can now have overflow: visible — previously they were always clipped to their bounding box. This makes it possible for content (like box shadows or outlines) to render outside an image's natural bounds without being cut off.
Other Additions
location.ancestorOrigins: A read-only array listing the origins of all ancestor frames that embed the current document. Useful for detecting iframe embedding and verifying the embedding context.NavigationPrecommitController.addHandler(): Part of the Navigation API. Lets you register a post-commit navigation handler from within a pre-commit handler — for multi-step navigations where the committed handler depends on data fetched during the pre-commit phase.about:blanksynchronous load: A long-standing web compatibility fix. The initialabout:blankdocument now completes synchronously rather than being replaced by a second asynchronously-loaded document, matching how other browsers have handled this.- 51 security fixes in this release.
Worth Watching: Document Picture-in-Picture (Nightly)
Still experimental (disabled by default, enabled via dom.documentpip.enabled in about:config): the Document Picture-in-Picture API. Unlike video PiP, this allows any HTML content to float in an always-on-top window — a full webpage with custom controls, a video conference grid, a dashboard widget. Set the flag in Nightly if you want to experiment with it early.
Footnotes
- Firefox 148.0 release notes: firefox.com/en-US/firefox/148.0/releasenotes · MDN developer notes: developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/148 ↩