June 10, 2026

Introducing the Field Guide to Grid Lanes

Surfin’ Safari

This week, we launched the Field Guide to Grid Lanes at gridlanes.webkit.org.

Field Guide to Grid Lanes website header

If you ever bookmarked the CSS Tricks Complete Guide to Flexbox, HTML5 Rocks, or CSS Zen Garden, a guide like this might feel familiar. It’s designed to be an easy introduction, a reference guide — and just plain fun.

The interactive playground

At the top is a live, editable Grid Lanes layout. Switch between Waterfall and Brick. Try preset layouts. Drag the slider labeled “Flow tolerance” and click “Play tab order” to understand the impact of flow-tolerance.

Interactive drawing of website with items laid out with Grid Lanes. Multiple buttons, sliders, and a code editor make it possible to try out many combinations quickly & see the results.

Resize the demo browser window to test responsive behavior without resizing your whole window. Edit the CSS directly. Copy the code you create.

The cheat sheet

Next is the Field Guide itself — a single-page reference for every property, value, and option.

Screenshot of first part of the reference guide, showing lots of definitions, little diagrams, and tiny code snippets

It has four sections:

  • Grid Lanes Basicsdisplay: grid-lanes, plus the difference between waterfall and brick layouts
  • Options for Lane Definitions — Grid track-sizing with fr units, fixed lengths, percentages, auto, min-content & max-content, fit-content(), minmax(), repeat(), and auto-fill vs auto-fit
  • Options for Placement & Spacingflow-tolerance, gap, spanning tracks, and explicit placement
  • Good to Know — info about source order, progressive enhancement, and switching layouts at different breakpoints

The demos

To showcase the possibilities of Grid Lanes, we created six demos, each available in several variations:

The six demos listed on the homepage — with a screenshot of each.
  • Photos — just images, in a variety of aspect ratios
  • Recipes — components containing both flexible images and varying lengths of text
  • Newspaper — longer passages of text, with a few images (and a lot of CSS puns)
  • Mega Menu — lists of very short text
  • Timeline — text in brick layout
  • Pinboard — mixed media

Each demo opens with a floating control panel.

Screenshot of one of the demos, happens to be of photo layout. The controls are showing, as described in the article.

“Layout” offers a dropdown of variations — showing off what Grid Lanes can do, and comparing it to Flexbox, Multicolumn, and Grid. “Numbers” shows item order. “Flow tolerance” lets you experiment with its effects. The code panel displays the key layout CSS.

“Hide controls” puts the focus on the demo itself. To get the controls back, click the gear that appears in the lower-right corner.

Working with Safari’s developer tools

Web Inspector knows about Grid Lanes, too. Toggle “Order Numbers” to reveal overlays marking the DOM order of items. These numbers are extremely useful when experimenting to find the best flow-tolerance value for your content.

Screenshot of photo demo, with Web Inspector open. The Grid Inspector is showing, with Item numbers are turned on. This creates lines all over the webpage, revealing the Grid Lanes layout, and marking each Item with a number.

Learn more by reading New Safari developer tools provide insight into CSS Grid Lanes.

What about other browsers?

Grid Lanes works today in Safari 26.4+. For the latest information about other browsers, check Can I Use. For progressive enhancement guidance, read When will CSS Grid Lanes arrive? How long until we can use it?

Made by the people who shipped it

The Field Guide was built by the same team behind Grid Lanes. We hope this is a fun experience that makes Grid Lanes easy to learn. Bookmark it, share it with colleagues, and let us know what you make.

Visit the Field Guide →

Feedback

We’d love to hear from you. Find us online: Jen Simmons on Bluesky / Mastodon, Saron Yitbarek on Bluesky / Mastodon, and Jon Davis on Bluesky / Mastodon. You can follow WebKit on LinkedIn.

June 10, 2026 07:30 AM

June 09, 2026

Discover MapKit JS 6: Rebuilt for Today’s Web Developer

Surfin’ Safari

MapKit JS allows you to bring the power and simplicity of Apple Maps to your website or web app. Whether you’re building a store locator, a travel planner, or a companion web experience for a native app, MapKit JS offers you a robust, privacy-first framework, providing the mapping data, services, and design quality behind Apple Maps, directly to your JavaScript code.

If you haven’t tried MapKit JS lately, now is a great time to take another look. With our latest version 6 release, we’ve made it easier than ever for you to integrate MapKit JS into your apps and websites by modernizing around today’s web development patterns.

See It in Action

Getting a MapKit JS map onto a page takes a few lines of code. In this post, we’ll guide you, step-by -step, through how to build a sample app — Yosemite Explorer — featuring points of interest across Yosemite National Park in California. Explore the demo.

image, showing the full app: POIs marked on the map with one selected, showing place detail popover and driving directions

Try it yourself

Apple Developer Account

To display a map, provide a developer token to MapKit JS. You generate that token through an Apple Developer account — and that same account unlocks a lot more than just maps. With an Apple Developer account, you get access to Apple’s full suite of developer services: publish Safari Web Extensions, and use powerful web frameworks like MusicKit JS for Apple Music integration, CloudKit JS for iCloud-backed data storage, and more. Sign up for an Apple Developer account.

MapKit JS 6 makes token setup significantly simpler. You can now use a static token bound to your website’s domain, generated directly from the Apple Developer website — no private key management or self-signing required. For details, see Creating a Maps token.

Load the Framework

New in v6, the MapKit JS loader ships as an NPM package, so the framework integrates directly into modern build pipelines. If you are looking to build a quick prototype or load MapKit JS directly in HTML, you can also skip to [Loading the MapKit JS script in your browser] below.

Load with MapKit JS Loader

To get our Yosemite map running, the next step is to install the package:

npm install @apple/mapkit-loader

Now you can load the framework with the token you generated in the previous step:

import { load } from "@apple/mapkit-loader";

const mapkit = await load({
    libraries: ["map", "services", "annotations"],
    token: "your-token-here"
});
console.log("MapKit loads", mapkit.loadedLibraries);

Let’s reload the page. The console.log() output should appear in Web Inspector:

screenshot of Web Inspector showing MapKit JS loads

MapKit JS partitions its features into libraries, so you load only what you need. Set the libraries array to the minimal set for the best performance. When you load MapKit JS through MapKit JS Loader, it automatically sources full TypeScript support through DefinitelyTyped into your project. Find a list of available libraries in developer documentation for MapKit JS.

Alternative: Load the MapKit JS script in HTML

For quick prototyping or web apps without a build pipeline, you can load and initialize MapKit JS with a <script> tag:

<script src="https://cdn.apple-mapkit.com/mk/6/mapkit.core.js"
     crossorigin async
     data-callback="initMapKit"
     data-libraries="map,services,annotations"
     data-token="your-token-here"></script>

When the script loads, it invokes window.initMapKit — a callback you define — after which the mapkit namespace is available under window.

Create a Map

You create a map by passing the ID of a container element and a region. For our example, we center the map view on Yosemite Valley and set the camera high enough to cover the whole valley.

First, in HTML, define a sized container for the map view:

<div id="map-container" style="width: 500px; height: 500px;"></div>

In JavaScript, frame the map viewport by setting a center coordinate (latitude and longitude) and a camera distance (in meters):

const map = new mapkit.Map("map-container", {
    center: new mapkit.Coordinate(37.7456, -119.5936),
    cameraDistance: 28000
});

With that, the framework renders an interactive map in your container, centered on Yosemite Valley:

image showing map loads inside the container

Alternative: Create a Map with frameworks

When using a UI framework, pass in the DOM element directly. For example, in React:

const mapRef = useRef(null);

useEffect(() => {
        new mapkit.Map(mapRef.current, {
            region: new mapkit.CoordinateRegion(
                new mapkit.Coordinate(37.3349, -122.0090),
            new mapkit.CoordinateSpan(0.01, 0.01)
        )
    });
}, []);

return <div ref={mapRef} style={{ width: "500px", height: "500px" }} />;

Add Place Annotations

You can add custom annotations or overlays to the interactive map view. In this example, we’ll showcase places in Yosemite National Park with PlaceAnnotation — an annotation that automatically picks up the place’s title, coordinate, and iconography from Apple’s data.

Each place in Apple Maps is referenced by a Place ID, an opaque string that represents a point of interest rather than a specific coordinate or address. You can find Place IDs with the Place ID Lookup tool or the Search service. New in v6, PlaceLookup returns a promise, so you can pass a Place ID, await the result, and create the annotation in a single flow:

const placeLookup = new mapkit.PlaceLookup();

const id = "I7408F9590EC1AB75";
const place = await placeLookup.getPlace(id);
const annotation = new mapkit.PlaceAnnotation(place, {
    selectionAccessory: new mapkit.PlaceSelectionAccessory()
});

addToList(annotation);
map.addAnnotation(annotation);

The code snippet above adds a PlaceAnnotation to the map and populates the same place on a list. The list draws the place name using the annotation.title property:

image showing the map with a PlaceAnnotation

The snippet also sets a selectionAccessory option to a PlaceSelectionAccessory instance. When the user selects a marker, selection accessory displays detailed information of that place, like contact information or operating hours.

image showing the marker selected with selection accessory shown

Repeat the place lookup to populate the app with all places.

Respond to Interactions

We want to present the user interface in a consistent state. New in v6, MapKit JS uses the standard browser EventTarget model, so handling map events works like handling any other DOM event. When the user selects an annotation on the map, you can update the corresponding list item:

map.addEventListener("select", (event) => {
    const selected = event.annotation;
    setListSelected(selected.id);
});

Likewise, when the user selects a list item, select the corresponding annotation on the map. Since each list item holds a reference to the annotation instance, set its selected property to true:

element.addEventListener("click", (event) => {
    annotation.selected = true;
});

This creates a two-way binding so the list and the map feel connected when either is interacted with. With that, we completed our app:

The same addEventListener pattern applies to annotation selections, map region changes, and every other MapKit JS interaction. See Handling map events to learn about all available event types.

Apple Maps, Built for the Web

In walking through the Yosemite Explorer example, you’ve seen several v6 changes working together. Installing MapKit JS as an npm package makes getting started more straightforward. Handling annotation selection with addEventListener puts the standard browser EventTarget model to work — one that v6 adopts consistently across the API. Using await for place lookups uses native promises throughout. And the authentication token you configured at the start is scoped to specific capabilities, giving you fine-grained control over access. Together, these changes make bringing the power of Apple Maps to your website a natural part of your development process.

Next Steps

Explore the MapKit JS documentation to dive deeper into the API. If you are using an earlier version of MapKit JS, check out the migration guide. Try the code samples in your own project, and share feedback through Feedback Assistant.

June 09, 2026 05:00 PM

June 08, 2026

Release Notes for Safari Technology Preview 245

Surfin’ Safari

Safari Technology Preview Release 245 is now available for download for macOS Tahoe and macOS Sequoia. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 312965@main…313358@main.

Accessibility

Resolved Issues

  • Fixed VoiceOver’s “Skip redundant labels” setting not being respected on certain web pages. (312967@main) (176297111)

CSS

New Features

  • Added support for the case-sensitive modifier s in CSS attribute selectors. (313234@main) (126331481)
  • Added support for the :host:has() compound selector in CSS. (313350@main) (139799278)

Resolved Issues

  • Fixed aspect-ratio not being respected on flex children when the flex container has position: absolute. (313213@main) (117807518)
  • Fixed aspect-ratio not working correctly on flex children that also have overflow set. (313170@main) (118926827)
  • Fixed image aspect-ratio not being preserved when width: 100% and height: 100% are set but no ancestor has a defined width. (313003@main) (162373271)
  • Fixed transferred min/max block-size constraints not being applied for intrinsic keyword widths on replaced elements. (313091@main) (173128588)
  • Fixed serialization of multi-word font family names that were always incorrectly quoted due to treating the full string as a single identifier. (313271@main) (175522811)
  • Fixed CSSStyleDeclaration.setProperty() failing to apply !important priority to an existing inline style property when the value was an integer of 255 or lower. (313159@main) (176099619)
  • Fixed an issue where elements using stretch sizing inside anonymous block wrappers resolved to their intrinsic size instead of stretching to fill the available space. (313359@main) (176398251)
  • Fixed :has() style invalidation failing in complex nested cases involving :is(). (312966@main) (176719780)
  • Fixed -webkit-perspective not establishing a containing block for fixed-positioned descendants. (313020@main) (176729670)
  • Fixed nested multi-column layouts with three or more levels failing to paginate content across pages. (312973@main) (176741498)
  • Fixed :has() selector performance by using scope selectors to limit style invalidation traversal for class, attribute, and pseudo-class changes. (313009@main) (176771971)
  • Fixed non-replaced blocks with aspect-ratio and a percentage max-width collapsing to zero width during intrinsic sizing. (313074@main) (176873776)
  • Fixed percentage max-width on elements with aspect-ratio resolving against the wrong axis in perpendicular writing modes. (313078@main) (176879597)
  • Fixed z-index not applying to statically-positioned display: -webkit-box items to align with Firefox and Chrome behavior. (313081@main) (176886461)
  • Fixed flex containers using box-sizing: border-box providing the wrong cross size to stretched flex items. (313175@main) (176989934)
  • Fixed flex containers with aspect-ratio-derived height not providing a definite cross size to their flex items. (313256@main) (177085129)
  • Fixed SVG images with no intrinsic dimensions collapsing to zero height inside column flex containers. (313257@main) (177086497)

Editing

Resolved Issues

  • Fixed a regression where Vietnamese and Korean keyboard input methods incorrectly exited modeless composition mode, requiring a double spacebar press to complete each word. (313286@main) (176847897)
  • Fixed a recent regression that “Zhuyin – Traditional” input method stalling for multiple seconds when composing Chinese text. (313336@main) (177042301)

HTML

Resolved Issues

  • Fixed a severe performance regression causing dynamic insertion of <img> elements with a src attribute to be dramatically slower than other browsers. (313268@main) (166201075)
  • Fixed nested calls to requestClose() incorrectly firing multiple cancel events and causing a stack overflow. (313239@main) (174850509)
  • Fixed requestClose() incorrectly removing the open attribute when called on a disconnected dialog element. (313251@main) (174855725)
  • Fixed <a rel="ar"> elements wrapping <model> elements to correctly enter ARQL without extra steps and to display the AR badge. (313047@main) (176410897)
  • Fixed the HTML preload scanner not preloading resources referenced by legacy <image> tags. (312984@main) (176712749)

Images

Resolved Issues

  • Fixed rendering performance of HDR images that have gain-maps by using GPU-backed surfaces. (313339@main) (176605566)

JavaScript

New Features

  • Added support for static import defer semantics. (313139@main) (176568369)

Resolved Issues

  • Fixed the Array ToPrimitive fast path incorrectly ignoring overrides of Object.prototype.valueOf. (313028@main) (175122250)
  • Fixed input position corruption in regular expression backward matching when rewinding over a surrogate pair. (313026@main) (175122467)

MathML

New Features

  • Added support for operator dictionary entries for multi-character operators to align with the MathML Core specification. (313083@main) (176543727)

Resolved Issues

  • Fixed the MathML operator dictionary to correct the stretchy property for several operators, resolving Web Platform Test failures. (312993@main) (170901728)
  • Fixed spacing values for prefix operators +, , ±, , , and infix operator in the MathML Core operator dictionary. (312999@main) (176652211)
  • Fixed the operator dictionary entry for the prefix operator to use the correct spacing values (3, 0) instead of (2, 1). (312997@main) (176693587)
  • Fixed nonce-hiding support for MathML elements to align with the HTML specification. (313075@main) (176875058)

Media

Resolved Issues

  • Fixed timeupdate events being fired during seeking before the seek operation completes. (313165@main) (176861767)
  • Fixed the ended event not always firing when the MediaSource duration is changed to match the current playback position. (313141@main) (176863546)
  • Fixed a MediaSource issue where the decode-key cleanup in coded frame processing was incorrectly removing non-orphaned samples. (313296@main) (176971800)
  • Fixed currentTime() returning a stale value after the playback rate was changed from zero to a non-zero value. (313249@main) (177046564)

Rendering

Resolved Issues

  • Fixed an issue where a child element with filter: blur() ignored border-radius overflow clipping from its parent. (312531@main) (175519148)
  • Fixed drop-shadow filters and transform: translate() incorrectly clipping nested elements after a regression. (313316@main) (175905543)
  • Fixed a repaint issue where table rows did not repaint their previous position after a preceding row changed size, causing content to appear at both the old and new locations. (313168@main) (176172404)

SVG

Resolved Issues

  • Fixed negative stroke-dashoffset values rendering with incorrect offsets when stroke-dasharray has an odd number of values. (313353@main) (103596361)
  • Fixed an issue where @prefers-color-scheme in an SVG image will sometimes not follow the system color appearance. (313021@main) (176413340)
  • Fixed getScreenCTM() returning an incorrect matrix when the document is scrolled under a CSS-transformed ancestor. (313111@main) (176814876)

Web API

Resolved Issues

  • Fixed missing custom element callbacks for the role attribute. (312976@main) (176713992)
  • Fixed incorrect URL parser invocation on the Notification object. (312988@main) (176762955)
  • Fixed requestAnimationFrame() not providing sub-millisecond timestamp precision in cross-origin isolated contexts. (313153@main) (176967366)

Web Extensions

Resolved Issues

  • Fixed cross-origin XMLHttpRequest incorrectly triggering additional Web Extension permission requests. (313506@main) (154866064)
  • Fixed loading Web Extensions breaking Cloudflare bot challenge pages. (313007@main) (176618014)

Web Inspector

New Features

  • Added unique colors for style events such as “Style Invalidated” and “Style Recalculated” in the Timeline view to distinguish them from layout events. (312995@main) (176770197)

WebGPU

Resolved Issues

  • Fixed a WGSL shader validation failure in binary arithmetic expressions. (313135@main) (176473479)

WebRTC

Resolved Issues

  • Fixed outgoing video feeds freezing when the Safari window is obscured by another window while a virtual background is active. (312990@main) (170720729)
  • Fixed screen sharing via getDisplayMedia() starting at extremely low quality and taking up to 30 seconds to become legible for remote participants. (313072@main) (175425085)

June 08, 2026 10:19 PM

Igalia WebKit Team: WebKit Igalia Periodical #67

Igalia WebKit

Update on what happened in WebKit in the week from June 1 to June 8.

Another great week, this time we have a performance improvement implemented in the Skia-based compositor, an excellent writeup about how to investigate and isolate memory leaks in WPE WebKit, a couple of multimedia fixes, and a variety of improvements and fixes across WebKit ports.

Cross-Port 🐱

Implement dialog integration with close watcher.

Implement node iterator and live range pre-remove steps for in-progress moveBefore() implementation.

Fix an early return in CloseWatcher close to align with the spec.

The Web Inspector now shows DOM nodes associated with layout and rendering events in a separate column of layout timeline next to initiator, sizing, and timing information. Hovering over rows in the details table highlights the associated node, and clicking it reveals the node in the "Elements" tab. This makes it easier to match events with specific nodes and helps debugging changes to a web page.

Fix popover light dismiss to account for disabled command buttons.

Multimedia 🎥

GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.

Fix mediaTime provided with requestVideoFrameCallback in case of captureCanvas as source.

Graphics 🖼️

Batched painting support was implemented in the Skia-based compositor, improving the performance in several cases.

Community & Events 🤝

Pawel Lampe published a blog post where he's presenting and discussing a guide on structured approach to narrowing down and debugging memory leaks within WPE WebKit.

That’s all for this week!

By Igalia WebKit Team at June 08, 2026 08:57 PM

Web Technology Sessions at WWDC26

Surfin’ Safari

Welcome to WWDC26. This year, the WebKit team is here with six sessions covering new CSS layouts, customizable form controls, 3D models, immersive spatial experiences, and browser extensions. Regardless of what you’re building for the web, we hope there’s something in here that might make doing your work a little easier, and maybe a little more exciting.

Watch them all on the Apple Developer site, or read on to find the sessions you’re interested in.

What’s New in Safari 27

Image of the presenter, Jen Simmons

For an overview of what we’ve been working to bring you this past year, start here. We cover the full range of what’s coming to Safari 27 with a particular focus on quality. We’ve shipped over 1000 fixes and improvements across the board, and we hope that work solves some of your problems and makes it easier for you to build for the web. Quality leads the agenda, but we still made room for some great features as well. This session gives a sneak peek at the five features brought to you in Safari 27.

Grid Lanes

A website using grid lanes layout with Web Inspector open with graphical overlays of tracks and item placement numbering projected over the content

Grid lanes finally brings the dream of a CSS-only masonry layout to the web, allowing you to create the famous Pinterest-style layout with no additional JavaScript required. It works in both the vertical “waterfall” direction and the horizontal “brick” direction, and it brings the full power of CSS Grid track sizing with it.

Brandon shows you the few lines of code it takes to implement this layout and also teaches you about some of its helpful features, like flow-tolerance, which gives you control over how far items can drift from source order to fill in the layout — an important consideration for accessibility.

To play with Grid Lanes yourself, check out our interactive demo on our Field Guide at gridlanes.webkit.org and experiment with the layout options. When you’re ready, you can copy the code you write and bring it into your own projects.

Customizable Select

Image of the presenter Tim Nguyen in a dark blue shirt in a room with a plant on the right and a green wall behind him

If you’ve built forms for the web, you’ve probably had your fair share of wrestling form controls. Customizable select is here to give you a better alternative, using the power of just HTML and CSS.

Add appearance: base-select to any <select> element and it immediately starts inheriting your design system — fonts, colors, and more. From there, you have full CSS control over every part of the element: the picker popup via ::picker(select), the disclosure icon via ::picker-icon, the selected option’s checkmark via ::checkmark. You can even add rich HTML content inside your <option> elements — descriptions, images, anything you want — while keeping all the accessibility and robustness of a native form control.

This session is Tim’s guide to what Customizable Select now makes possible: a form control that’s truly in your control. If you’ve ever built a custom dropdown from scratch to get the styling you needed, this session is for you.

model element

An iPhone with a website for recreational equipment showing a 3D water bottle with an augmented reality QuickLook button

The <model> element comes to iOS, iPadOS, and macOS in Safari 27. Embedding interactive 3D models in a webpage now works like embedding any other media: use a <source> element to provide files in multiple formats, set environmentmap for custom lighting, use stagemode="orbit" to let users rotate the model, and reach for the JavaScript API for more programmatic control.

If you’re new to the world of 3D models, Aleksei guides you through the process, starting from the first question: where do you even get 3D models? Then he moves into how to optimize them for the web, how to write the markup, and how to use the JavaScript API for animation playback, programmatic rotation, and more.

Whether you’re building a product preview for your online store, an educational tool, or just something eye catching to grab your users attention, this session gives you a practical on-ramp.

Immersive website environments

An illustration of a person wearing an Apple Vision Pro in front of a floating window surrounded by a 3D environment with rocks, grass, and a water well with buckets on and around it

In visionOS 27, <model> goes further still with immersive website environments. A user can tap to leave the browser window behind and be surrounded by the model you provide — a full spatial experience launched directly from a webpage.

The Immersive API — which is very similar to the Fullscreen API — gives you control over how the environment appears and how users interact with it. Jean built an interactive theater-seating experience to show how this works in practice: a user browses seats on a webpage, then steps into the theater itself to see the view from their seat before they buy.

This session covers what’s possible, how the API works, and the decisions that go into designing an experience that feels right in visionOS.

Web Extensions

An image of the presenter, Kiara Rose wearing a red top in a room with a wood panel wall and a small side table with a modern-style lamp

And for Web Extensions, the Safari web extension packager makes it easier than ever to distribute your extension. Publishing a Safari extension no longer requires Xcode, or even a Mac. Now, you can package and submit your extension using App Store Connect from any web browser, on any operating system. One codebase, every browser.

But this session is about more than just packaging — Kiara breaks down how to build a web extension step by step, building a web extension from the ground up. If you’ve been holding off on publishing your extension for Safari, or if you’re building your first extension and want to support all browsers from the start, this is the session to watch.

We cover a wide range of web topics in this year’s session, including layouts, 3D objects, and web extensions. Whether it’s a feature or a fix, we hope you’ll find something here that’s applicable and helpful to the work that you do. Enjoy WWDC26!

Feedback

We love hearing from you. To share your thoughts, find our web evangelists online: Jen Simmons on Bluesky / Mastodon, Saron Yitbarek on BlueSky, and Jon Davis on Bluesky / Mastodon. You can follow WebKit on LinkedIn. If you run into any issues, we welcome your feedback on Safari UI (learn more about filing Feedback), or your WebKit bug report about web technologies or Web Inspector. If you run into a website that isn’t working as expected, please file a report at webcompat.com. Filing issues really does make a difference.

June 08, 2026 07:38 PM

News from WWDC26: WebKit in Safari 27 beta

Surfin’ Safari

Safari 27 beta is here. Don’t miss our WWDC26 sessions on web technology, including What’s new in WebKit for Safari 27, to go deeper on our work in this release. Now, let’s dig into this beta, packed with 58 new features, 525 fixes and 4 deprecations that will hopefully make your work as a web developer a little easier.

Here’s a sneak peek of the highlights:

  • After years of anticipation, you can now use customizable <select> to style your form elements to match the rest of your site or app without rebuilding it in JavaScript or sacrificing accessibility.
  • Scroll anchoring prevents those visual jumps when content loads above the viewport.
  • WebAssembly JavaScript Promise Integration (JSPI) lets Wasm code participate in the async world of JavaScript.
  • Transform-aware anchor positioning closes out a major gap in the anchor positioning story.
  • The :heading pseudo-class, the revert-rule keyword, and the stretch keyword for box sizing all land in CSS.
  • Subpixel inline layout makes text rendering more precise.

And that’s just the start.

If you look through the lists of features and fixes in Safari 27, you’ll notice that, although there are 58 brand-new features and 525 fixes — the largest pile of fixes in any Safari release in recent memory — most of what is released is not about new things.

Most of this work has been about existing features behaving more correctly, handling more edge cases, and fitting together with other features the way you’d expect. We committed our time to increasing quality — that’s the story of this release and the year that led to it.

A lot of this work was also about aligning to web standards. When we found a spec that was unclear or incomplete, we helped update it, and then updated WebKit to match.

For example, Safari 27 contains 30 SVG fixes, including updates based on recent decisions in SVG 2 where we revived the Working Group. SVG is used on 67% of webpages, making this work very impactful. Anchor positioning continues to get refined as the CSS specification settles. And in more subtle places throughout the release — URL parsing details, event listener options, timezone identifier handling, innerText edge cases — features that look unchanged on the surface now behave the same way in Safari as they do in Chrome, Firefox, and Edge.

We also spent time making sure features still work across different contexts. A :has() selector invalidating properly when siblings change. An aspect-ratio resolving correctly against a percentage height. box-shadow rendering correctly on table-row elements. background-clip: text working on table header elements. Bugs that appear when combining features are among the hardest to find and the most frustrating, but we’ve made significant progress in hunting them down.

If something has been bothering you, test it in Safari 27 beta. You might be pleasantly surprised. And if it hasn’t been fixed yet, file a bug report, or add a comment to an existing issue with a concrete scenario, a link to a real site, or a reduced test case. The more concrete the problem, the more helpful it is.

This work goes beyond the Safari browser. When your customers open their news app, their banking app, their shopping app, there’s a good chance the interface they interact with is powered by the HTML, CSS, and JavaScript that’s rendered by WebKit and JavaScriptCore — the same engines inside Safari. Every fix in this release isn’t just for the browser — it benefits everything the web platform touches.

Let’s dive in.

Customizable Select

Safari 27 beta adds support for customizable <select> , which transforms the <select> element. You can now build a fully custom form element that matches the look and feel of your website or web app, without reaching for a JavaScript library or sacrificing the accessibility, reliability, and native platform integration of a real HTML form control.

Use the new appearance: base-select to clear the native styling and start with a clean palette. Then insert any additional CSS you want to create your custom design. Customizable <select> comes with new pseudo-elements for more granular control, like ::picker-icon to target the disclosure indicator and ::checkmark to target the checkmark that appears next to the selected <option>. Both can be fully customized.

Use the new <selectedcontent> element inside the button to display the currently selected option’s content and style <selectedcontent> directly to get it to look exactly how you want in the closed state.

And because you’re still working with a real <select>, everything that comes with a native form control still works: keyboard navigation, screen reader support, form submission, validation, change events. You get the styling freedom of a custom widget with the power of a native HTML element.

Customizable <select> is a multi-vendor effort. The same syntax is coming to other browsers. Use progressive enhancement and style your <select> with appearance: base-select for a great experience in browsers that support it, and let browsers that don’t fall back to their built-in rendering.

Learn more in the WWDC26 session Rediscover the HTML Select Element, where Tim walks through the full API and how to build layouts inside options with Grid and Flexbox.

Additional bug fixes:

  • Fixed an issue where <select multiple> did not always fire onchange when the mouse button was released far outside the element. (173882861)
  • Fixed an issue where <select> control rendering was broken in vertical writing mode. (174068353)
  • Fixed a performance issue where parsing <select> elements with thousands of <option> children via innerHTML caused O(n²) overhead due to repeated list recalculation. (174244946)
  • Fixed <option> elements to correctly implement the HTML specification’s dirtiness concept for tracking user-modified selected state. (175306111)
  • Fixed the select picker appearing at an incorrect position when the <select> element is anchor positioned. (175454476)
  • Fixed the default display value for <optgroup> and <option> elements to block, matching the behavior of other browsers. (175473184)
  • Fixed <option> and <optgroup> elements to match the :disabled pseudo-class when inside a disabled <select>. (176559708)

Animations

Safari 27 beta adds the animation property to the AnimationEvent and TransitionEvent interfaces, letting event handlers directly access the Animation object associated with the event.

Additional bug fixes:

  • Fixed an issue where animation-fill-mode did not correctly apply viewport-based units after the viewport was resized. (80075191)
  • Fixed an issue where !important declarations did not override CSS animation values when CSS transitions were also running on the same property. (174367827)
  • Fixed an issue where identity matrix decomposition generated invalid quaternions, resulting in incorrect transform animations. (174813328)

CSS

Transform-aware anchor positioning

Safari 27 beta adds support for transform-aware anchor positioning. Now, when an anchor element has a CSS transform applied — scale, rotate, translate, or any combination — elements positioned relative to that anchor follow its transformed position instead of its pre-transform layout position. This works for transforms applied via the transform property as well as through the individual translate, rotate, and scale properties.

This closes a long-standing gap in the anchor positioning story. If you use anchor positioning to attach a tooltip, popover, or annotation to a transformed element, it now tracks correctly, even with animated transforms.

:heading pseudo-class

Safari 27 beta adds support for the :heading pseudo-class, which matches any heading element — <h1> through <h6>.

Instead of writing h1, h2, h3, h4, h5, h6 in your selector list, you can just write :heading. And :heading can also be combined with functional selectors to target headings at specific levels:

revert-rule keyword

The revert-rule keyword is now supported in Safari 27 beta. Like revert and revert-layer, revert-rule rolls back the cascade — but specifically to the state as if the current style rule had not been present.

It gives you a more precise tool for working with overrides, especially in component libraries and design systems where you want to selectively undo declarations within a rule without losing the rest.

stretch keyword for box sizing

Safari 27 beta adds support for the stretch keyword in box sizing properties like width, height, min-width, and so on. stretch tells an element to fill the available space in the relevant axis, accounting for margins.

.card {
  width: stretch;
}

Previously, the common way to achieve this was width: 100% — which doesn’t account for margins and can cause overflow when margins are applied. The stretch keyword does the right thing. If you’ve been using -webkit-fill-available as a workaround for the same behavior, now is a good time to switch.

Dutch IJ digraph in text-transform: capitalize and ::first-letter

The Dutch IJ digraph is now available in Safari 27 beta. When the content language is Dutch (lang="nl"), text-transform: capitalize and ::first-letter now correctly titlecase “ij” to “IJ” at the start of words.

A small but genuine improvement for anyone writing Dutch text on the web.

position-anchor: normal and none

Safari 27 beta adds support for position-anchor: normal and position-anchor: none. Before, position-anchor defaults to auto, which makes every element use the implicit anchor element as its default anchor, if one exists. This has a side effect of changing an element’s positioning behavior, whether it uses anchor positioning or not. This may result in compatibility problems.

To fix this, the CSS Working Group has added two values to position-anchor to allow opting out of the new behavior:

  • none: an element does not have a default anchor, and its positioning behavior is unchanged.
  • normal: auto if an element uses position-area, otherwise none.

position-anchor: normal is the new default value, replacing auto. This means elements will only have a default anchor and get anchor positioning behavior if it uses position-area, otherwise its behavior remains unchanged.

anchor-valid and anchor-visible

Safari 27 beta adds support for anchor-valid and anchor-visible . Originally, position-visibility: anchors-valid hides an element if any of its required anchor references can’t be resolved. However, it was not clear what constitutes “required anchor references”. The CSS Working Group has since then changed its behavior to only look at the default anchor box. To match the new behavior, some keywords also got renamed to drop the plurality:

  • anchors-valid is renamed to anchor-valid
  • anchors-visible is renamed to anchor-visible

Safari 27 beta aligns with the new behavior but also still supports the old keywords for compatibility.

Style containment for quotes

Safari 27 beta adds support for contain: style applying to CSS quotes. This allows you to scope effects of quotes to a certain subtree.

insert keyword for text-autospace

Safari 18.4 added support for text-autospace to control spacing between Chinese/Japanese/Korean (CJK) and non-CJK characters. Safari 27 beta now adds the insert keyword, making the following syntaxes equivalent:

  • text-autospace: ideograph-alpha ideograph-numeric insert
  • text-autospace: ideograph-alpha ideograph-numeric

Additional Bug Fixes:

  • Fixed an issue where -webkit-text-fill-color incorrectly overrode text-decoration-color. (47010945)
  • Fixed shape-outside computing incorrect text wrapping in RTL writing modes. (56890238)
  • Fixed flex layout to use the used flex-basis instead of the specified value for definiteness evaluation. (85707621)
  • Fixed an issue where the outline offset was too large for outline: auto on macOS. (94116168)
  • Fixed an issue where element positioning was incorrect when the containing block was an anonymous block. (96548847)
  • Fixed an issue where box-shadow did not work on display: table-row elements. (96914376)
  • Fixed text-indent with calc() containing percentages to correctly treat percentage components as zero for intrinsic size contributions. (97025949)
  • Fixed an issue where out-of-flow content had an incorrect height when set to fit-content. (97492632)
  • Fixed an issue with percentage size resolution in flex items in quirks mode. (100183902)
  • Fixed an issue where clip-path: inset() border-radius values did not render correctly at certain element and clip-path sizes. (110847266)
  • Fixed -webkit-box flexbox emulation not sizing children correctly inside <fieldset> elements. (114094538)
  • Fixed: Improved performance on pages using :where and :is selectors. (114904007)
  • Fixed an issue where elements with display: table could have incorrect layout when borders were present. (116110440)
  • Fixed font-family serialization to preserve quotes around family names that match CSS-wide keywords or generic families. (125334960)
  • Fixed an issue where elements with border, position: absolute, and aspect-ratio: 1 were not rendered as squares. (126292577)
  • Fixed an issue where perspective-origin failed to resolve var() references when used as the second value, preventing animations from being applied. (131288246)
  • Fixed :focus-visible incorrectly matching after a programmatic focus() call triggered by clicking a button with child elements. (134337357)
  • Fixed an issue where the bottom margin of a last child element collapsed out of a parent with min-height. (134356544)
  • Fixed a performance issue where pages with many DOM manipulations and complex :has() selectors could freeze. (138431700)
  • Fixed an issue where a font was downloaded despite no characters in the document falling within its unicode-range. (140674753)
  • Fixed an issue where @media (prefers-color-scheme: dark) inside an iframe did not match when the iframe’s color-scheme was set to dark. (142072593)
  • Fixed an issue where background-clip: text did not work on table header elements. (142812484)
  • Fixed an issue where width: 0 did not collapse a table cell to its minimum size. (142814603)
  • Fixed an issue where :has(:empty) continued to match after the targeted element’s content was dynamically changed to no longer be empty. (143864358)
  • Fixed an issue where floats and out-of-flow objects could be incorrectly adjacent to anonymous blocks. (144481961)
  • Fixed an issue where text gradually disappeared when toggling text-transform on elements with ::first-letter styling. (145550507)
  • Fixed an issue where height: max-content resolved to zero on absolutely positioned elements when a child had max-height: 100%. (147333178)
  • Fixed an issue where tables with collapsed borders incorrectly calculated the first row width, causing excess border width to spill into the table’s margin area. (149675907)
  • Fixed an issue where an inline-flex container with flex-direction: column did not update its width to match the intrinsic size of a child image when the image was not cached. (150260401)
  • Fixed CSS zoom interacting incorrectly with font-weight, font-style, and font-variant on iPad. (152173269)
  • Fixed an issue where non-replaced elements with aspect-ratio enforced the automatic minimum size even when min-width was explicitly set to 0. (156837730)
  • Fixed an issue where an element can’t anchor to its previous sibling. (162903640)
  • Fixed :has() style invalidation performance for selectors where :has() is in non-subject position. (163512170)
  • Fixed an issue where RTL grid scrollable areas did not correctly account for grid layout and scrollbars. (167792896)
  • Fixed pixel snapping to be applied consistently for all border-width value types. (168240347)
  • Fixed rendering of linear gradients when all color stops are at the same position. (169063497)
  • Fixed an issue where inset box-shadow was incorrectly positioned on table cells with collapsed borders. (169254286)
  • Fixed position-try-order to interpret logical axis values using the containing block’s writing mode instead of the element’s own writing mode. (169501069)
  • Fixed an issue where children with percentage heights inside absolutely positioned elements using intrinsic height values (fit-content, min-content, max-content) incorrectly resolved against the containing block’s height instead of being treated as auto. (171179193)
  • Fixed an issue where percent-height replaced elements computed stale preferred widths in shrink-to-fit containers. (171184282)
  • Fixed a regression where @scope styles did not apply to slotted elements in web components. (171383788)
  • Fixed an issue where the table cell nowrap minimum width calculation quirk was applied outside of quirks mode. (171410252)
  • Fixed a performance issue where contain: layout caused significantly slower forced layouts when all siblings created their own formatting context. (171545381)
  • Fixed an issue where dynamically inserting text before existing content did not update ::first-letter styling. (171649994)
  • Fixed an issue where underlines were split when a ruby base was expanded due to long ruby text. (171653095)
  • Fixed an issue where changing color-scheme did not repaint iframe background. (171658244)
  • Fixed an issue where nested children of a popover element failed to render when using position: absolute. (171735933)
  • Fixed an issue where color: initial resolved to the wrong color when the system is in dark mode. (172320282)
  • Fixed an issue where an element with display: contents did not establish an anchor scope when using anchor-scope. (172355302)
  • Fixed an issue where ordered list numbers with large starting values were clipped off-screen. (172515216)
  • Fixed <general-enclosed> in media queries to reject content with unmatched close brackets per the <any-value> grammar. (172575115)
  • Fixed an issue where the rlh unit was double-zoomed with evaluation-time CSS zoom. (172798163)
  • Fixed an issue where anchor-positioned elements anchored to children of sticky-positioned boxes did not stick correctly. (172884148)
  • Fixed an issue where pseudo-elements were not sorted correctly when sorting anchor elements by tree order. (173032203)
  • Fixed outline: auto to correctly respect zoom. (173068660)
  • Fixed outline-offset to work correctly with outline: auto on iOS. (173130230)
  • Fixed :active, :focus-within, and :hover pseudo-classes to correctly account for elements in the top layer. (173145294)
  • Fixed a regression where the ic length unit was incorrectly affected by page scaling. (173198587)
  • Fixed the shape() function to omit default control point anchors in computed value serialization per the CSS Shapes specification. (173233716)
  • Fixed: Updated SVG and MathML user agent style sheets to use :focus-visible instead of :focus. (173321368)
  • Fixed an issue where lh and rlh units resolved with double-zoom when line-height was a number value. (173448638)
  • Fixed outline-width to be ignored when outline-style is auto, matching the specification. (173567890)
  • Fixed :in-range and :out-of-range pseudo-classes for time inputs with reversed ranges. (173589851)
  • Fixed :placeholder-shown to correctly match input elements that have an empty placeholder attribute. (173604635)
  • Fixed an issue where ligatures caused a non-zero layout width for text with font-size: 0. (173840866)
  • Fixed computed value of auto insets or margins as returned by getComputedStyle() to be zero, if the element uses position-area or anchor-center. (173885561)
  • Fixed position-area not being able to anchor to an element positioned using anchor functions. (173964030)
  • Fixed :in-range and :out-of-range pseudo-classes to correctly update when the readonly attribute changes. (173978657)
  • Fixed an issue where view-timeline-inset serialization failed to coalesce identical values. (174096313)
  • Fixed CSS variable cycle detection to match the CSS Values Level 5 specification. (174105259)
  • Fixed url() token serialization in CSS custom properties. (174144616)
  • Fixed text-autospace to correctly handle supplementary Unicode characters. (174148315)
  • Fixed an issue where flex items with different order values caused incorrect baseline alignment. (174241817)
  • Fixed an issue where hovering over ::first-letter text showed a pointer cursor instead of the expected I-beam cursor. (174258447)
  • Fixed an issue where display: grid on a <fieldset> element added extra unnecessary space below its content. (174301311)
  • Fixed outline radii rendering for elements with a non-auto outline-style. (174328839)
  • Fixed an issue where aspect-ratio was not honored when the page was zoomed in. (174361289)
  • Fixed replaced elements to use the transferred size through intrinsic aspect ratio for min-content and max-content sizing. (174386310)
  • Fixed an issue where height: 100% on a child element altered the layout when the parent’s height was defined via aspect-ratio. (174448267)
  • Fixed margin collapse to be allowed when the preferred block size behaves as auto, per the CSS Sizing specification. (174547610)
  • Fixed an issue where document.styleSheets and shadowRoot.styleSheets incorrectly included adopted style sheets, which per the CSSOM specification should only appear in the final CSS style sheets list used for style resolution. (174583340)
  • Fixed the CSSOM preferred style sheet set name to be established at sheet creation time based on insertion order rather than tree order. (174586058)
  • Fixed highlight pseudo-elements such as ::selection and ::highlight to disallow vendor-prefixed properties, aligning with the CSS Pseudo-Elements specification. (174590593)
  • Fixed cycle detection and nested function call handling in CSS custom functions. (174609179)
  • Fixed FontFace.loaded to reject when a local() font source fails to load. (174631384)
  • Fixed an issue where word-break: break-all incorrectly allowed CJK close punctuation to appear at the start of a line. (174656971)
  • Fixed an issue where word-break: keep-all incorrectly suppressed line break opportunities at CJK punctuation characters. (174658701)
  • Fixed the FontFace constructor to reject with a SyntaxError instead of a NetworkError when a BufferSource fails to parse, per the CSS Font Loading specification. (174669738)
  • Fixed the FontFace family attribute to return the serialization of the parsed value. (174698351)
  • Fixed grid layout to correctly handle percentage and calc() values for the specified size suggestion. (174863227)
  • Fixed :has() sibling invalidation issues related to relation forwarding. (175006235)
  • Fixed an issue where min-width: auto was not correctly computed for flex items. (175157619)
  • Fixed an issue where percentage heights inside flex items did not resolve correctly in quirks mode. (175158571)
  • Fixed an issue where margin-trim: block-start did not apply to blocks nested inside inline boxes. (175162899)
  • Fixed an issue where dynamically changing display: contents on a <fieldset> legend caused incorrect rendering. (175163337)
  • Fixed: Improved :has() invalidation performance by including the full selector context in invalidation selectors. (175177078)
  • Fixed the CSS preload scanner to resolve relative @import URLs against the <base> element URL. (175305190)
  • Fixed -webkit-box flex distribution for children with orthogonal writing modes. (175323734)
  • Fixed calc(infinity) as a flex-grow factor not stretching a flex item to 100% width. (175431146)
  • Fixed :has() sibling invalidation failing due to an internal bitfield overflow, causing stale styles when siblings are added or removed. (175433733)
  • Fixed :has() invalidation for sibling combinators when elements are inserted or removed from the DOM. (175441568)
  • Fixed transition-property not preserving the specified case of <custom-ident> values during serialization. (175467206)
  • Fixed the will-change property not serializing correctly when used with non-property identifiers or identifiers in a non-standard case. (175482352)
  • Fixed percentage top and bottom values on relatively positioned elements not resolving when the containing block has aspect-ratio. (175502356)
  • Fixed: Updated the enhanced <select> element to use self- keywords for anchor positioning. (175505107) Fixed text-indent computation when tab stop positions are involved. (175529961)
  • Fixed calc() margin computations in flex layout. (175532405)
  • Fixed calc() margin computations for block, fieldset, and table caption layouts. (175548980)
  • Fixed handling of <li> value attributes in reversed ordered lists. (175558324)
  • Fixed CSS trigonometric functions to correctly convert degrees to radians. (175575617)
  • Fixed sibling-index() and sibling-count() inside calc() functions to be correctly simplified. (175590806)
  • Fixed sibling-index() and sibling-count()to correctly return 0 when used in cross-tree ::part() styling. (175592607)
  • Fixed the CSS resize handle not working on an element when the handle overlaps a child iframe. (175621855)
  • Fixed flex container baseline alignment being incorrectly computed for scroll containers by clamping to the border edge. (175631095)
  • Fixed an issue where inline-level boxes with calc() margins or padding lost the fixed component during intrinsic width computation. (175669222)
  • Fixed floats with margin-start incorrectly overlapping adjacent floats. (175669464)
  • Fixed aspect-ratio calculations for block-level elements with size constraints. (175669713)
  • Fixed aspect-ratio calculations for flex items with percentage cross-size constraints. (175669774)
  • Fixed aspect-ratio calculations for flex items with definite cross-size values. (175690028)
  • Fixed revert-layer computing incorrectly when there is a leading empty or space substitution value. (175729680)
  • Fixed :has() invalidation incorrectly resetting sibling relation bits, causing style invalidation failures for first-in-sibling-chain elements. (175738008)
  • Fixed an issue where flex items with explicit min-height: min-content were incorrectly treated as scrollable, zeroing out their minimum size. (176173688)
  • Fixed :has() invalidation performance when used inside nested :is() selectors. (176354723)
  • Fixed sibling-count() & sibling-index() used in @keyframes to re-resolve when siblings change. (176531901)
  • Fixed an issue with a collapsed border color mismatch when the table cell has a different writing-mode. (173655092)

Scroll Anchoring

Scroll anchoring is now supported in Safari 27 beta. When content is inserted or removed above the current viewport position — an image loads, an ad injects, a comment appears — the browser automatically adjusts the scroll position so the content you’re reading stays put instead of jumping.

This is an improvement you’ll feel on many sites, especially ones with lazy-loaded images, infinite-scroll feeds, or dynamically injected content. Most sites get this for free — no opt-in required.

Scroll anchoring is controlled by the overflow-anchor CSS property, which defaults to auto. If you have a specific element where you need to opt out of scroll anchoring, set overflow-anchor: none.

Additional bug fixes:

  • Fixed an issue on iOS where calling scrollTo during a momentum scroll incorrectly interrupted the scroll, ensuring that momentum scrolling continues as expected and smooth scrolling behaves properly. (41949531)
  • Fixed an issue on iOS where programmatic smooth scrolling with scroll-snap-type: mandatory failed after the browser chrome was hidden. (100727098)
  • Fixed an issue where interrupting scroll momentum caused the scrolling container to stop rendering and hit-testing to be misplaced. (116205365)
  • Fixed an issue on iOS where restored scroll position was incorrect after relaunching Safari. (127308062)
  • Fixed an issue where tabbing in a scroll container with scroll-padding did not scroll the focused element into view. (147513379)
  • Fixed an issue on macOS where custom CSS scrollbars could be cut off and the scrollbar corner rect was sized incorrectly. (168566468)
  • Fixed rubberbanding behaving incorrectly when a site triggers a smooth scroll to the top during a rubberband. (170705188)
  • Fixed an issue where pages could become blank and jump to the top after dynamically loading new content when scroll anchoring was enabled. (170889205)
  • Fixed an issue where scroll anchoring could cause pages to scroll to negative offsets. (171221075)
  • Fixed an issue where pages using the Navigation API could have offset hit test locations, making elements unclickable. (171752650)
  • Fixed an issue on iOS where composited layers would briefly flash blank when window.scrollTo() was called synchronously with a DOM layout change. (173197381)
  • Fixed an issue where sticky-positioned elements could flicker rapidly after scrolling. (173680821)
  • Fixed an issue where scroll anchoring could cause a page to scroll to the top or bottom automatically. (173885027)
  • Fixed an issue where calling scrollIntoView() on a scrollable element incorrectly scrolled the element’s own contents. (174173683)
  • Fixed scroll anchoring interfering with rubberbanding on some websites. (175195943)
  • Fixed an issue on iOS where scroll position was not preserved correctly when rotating the device on right-to-left pages. (175910769)

HTML

sizes=”auto” on img

The auto keyword in the sizes attribute on <img> elements is now supported in Safari 27 beta.

<img src="photo.jpg"
     srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
     sizes="auto"
     loading="lazy"
     alt="A mountain landscape">

When an image uses loading="lazy" and you don’t know its rendered layout width ahead of time, sizes="auto" tells the browser to calculate the size automatically based on the actual layout width once it’s known. This makes responsive images work correctly for images inside layout containers with dynamic widths.

shadowrootslotassignment attribute

Safari 27 beta adds support for the shadowrootslotassignment attribute on declarative shadow roots. This lets you configure the slot assignment mode (named or manual) directly in HTML when defining a shadow root declaratively, matching the JavaScript attachShadow({ slotAssignment: "manual" }) option.

Additional bug fixes:

  • Fixed an issue where an HTML map element without a name attribute did not match its associated image using the id attribute. (12359382)
  • Fixed sequential focus navigation to skip elements that do not meet the specification’s focusability requirements. (103370883)
  • Fixed viewport <meta> parsing to correctly treat form feed as ASCII whitespace per the HTML specification. (108440799)
  • Fixed parsing of javascript: URLs to align with the specification. (147612682)
  • Fixed an issue where a third nested <iframe> using the srcdoc attribute did not render. (167917471)
  • Fixed incorrect parsing of pixel-length margin attributes on <body>, <iframe>, and <frame> elements. (171240848)
  • Fixed an issue where replaceWith() stopped processing remaining nodes if a script in the replacement removed a sibling. (172753019)
  • Fixed an issue where HEIC images were incorrectly converted to JPEG when uploaded via drag-and-drop or file input. (173206598)
  • Fixed the HTML preload scanner to skip preloading stylesheets that have the disabled attribute. (173378582)
  • Fixed an issue where setting the rel attribute on an <a> element multiple times did not clear prior link relations. (173567839)
  • Fixed the HTML parser fast path to correctly process escaped attribute values longer than one character. (173673581)
  • Fixed the HTML parser fast path to correctly detect nested <li> elements. (173983892)
  • Fixed the HTML parser fast path to use the adjusted current node for MathML and SVG integration point checks. (174096305)
  • Fixed document named item collection to include all <object> elements, aligning with other browser engines. (174537345)
  • Fixed window.open() to correctly consume user activation when creating a new browsing context, aligning with the HTML specification. (174587258)
  • Fixed remaining issues with <img sizes="auto"> to fully align with the specification. (174684058)
  • Fixed an issue where dir=auto on <slot> elements did not update when slotted content changed. (174871706)
  • Fixed an issue where <option> elements rendered incorrectly when the label attribute was empty. (174979446)
  • Fixed an issue where the preload scanner incorrectly skipped <source> elements with an empty type attribute inside <picture>. (175094037)
  • Fixed innerText to emit a newline for empty <option> or <optgroup> inside <select>. (175245381)
  • Fixed HTML floating-point number parsing to correctly handle values with a leading + sign. (175300431)
  • Fixed innerText to no longer emit newlines for visibility: hidden block elements. (175569426)
  • Fixed innerText to correctly emit blank lines around <p> elements regardless of their CSS display value. (175729427)
  • Fixed the speculative preload scanner to no longer incorrectly preload scripts inside SVG elements. (175800116)
  • Fixed innerText on tables to no longer emit spurious trailing newlines and to preserve row-exit newlines after empty rows. (176635985)
  • Fixed an issue where inserting an image with a srcset attribute into a dynamically created iframe resulted in an invisible image. (66849050)
  • Fixed naturalWidth and naturalHeight returning incorrect values for SVG images without intrinsic dimensions. (141196049)
  • Fixed an issue where HDR images would flicker and lose their HDR appearance when overlapping layers animate. (163382580)
  • Fixed an issue where adopting a standalone img element did not update its image data. (172856773)

JavaScript

Top-Level Await

Safari 27 beta includes a complete standards-compliant rewrite of the ECMAScript module (ESM) loader. The new loader is implemented in native C++ and conforms directly to the ECMAScript specification’s module loading algorithms, replacing an earlier implementation based on an abandoned 2016 WHATWG Loader proposal that predated top-level await entirely.

The rewrite fixes module execution ordering and initialization issues that could cause imports to access exports before they were fully evaluated. It was validated against test262, the Web Platform Tests, and additional test cases.

Top-level await is a foundational feature of modern JavaScript module authoring, and it’s been a real pain point in Safari for a while — a known source of cross-browser bugs that developers building module-based apps had to work around. This fix closes that gap.

Additional bug fixes:

  • Fixed multiple top-level await correctness bugs with a rewrite of the ES module loader for standards compliance. (97370038)
  • Fixed regular expressions in Unicode mode to not count non-capturing groups and modifiers toward the number of available backreferences. (167746769)
  • Fixed %TypedArray%.prototype.subarray to calculate beginByteOffset correctly to align with ECMA-262. (168143600)
  • Fixed the trace behavior of RegExp.prototype[Symbol.split] to align with ECMA-262. (168288878)
  • Fixed Array.prototype.concat to correctly handle arrays with indexed accessors, preventing getter reentry from bypassing Symbol.isConcatSpreadable checks. (172237596)
  • Fixed an issue where a greedy or non-greedy non-BMP character class in a regular expression could advance the index past the end of input. (172978772)
  • Fixed an issue where class instance field initializers did not have the correct evaluation context when used inside arrow functions and nested scopes. (173296563)
  • Fixed TypedArray [[Set]] to check the receiver before writing to the typed array. (173386404)
  • Fixed %ArrayIteratorPrototype%.next() to return { done: true } instead of throwing a TypeError when the source TypedArray is detached and the iterator has already completed. (173759106)
  • Fixed an issue where a fixed-count mixed-width character class in a regular expression did not correctly restore the index on backtrack. (173972458)
  • Fixed an issue where regular expressions with non-BMP characters could skip valid match positions when alternating between patterns. (174200307)
  • Fixed an issue where regular expression captures were not properly cleared when backtracking out of fixed-count parenthesized groups and negative lookaheads. (174201284)
  • Fixed an issue where import { "*" as x } was incorrectly treated as a namespace import instead of a named import using the string “" as a ModuleExportName. (174314099)
  • Fixed an issue where RegExp.prototype.exec and RegExp.prototype.test could match against a stale pattern if lastIndex has a valueOf that calls RegExp.prototype.compile. (174461752)
  • Fixed an issue where async functions using module-scoped variables could fail when the DFG JIT optimized scope resolution. (174626957)
  • Fixed an issue where Intl.Segmenter with granularity: "word" incorrectly reported isWordLike: false for numeric segments. (175057894)
  • Fixed Object.defineProperties to call Proxy traps in the correct order. (175068687)
  • Fixed an issue where Intl.Locale did not canonicalize before overriding the language. (175092327)
  • Fixed time zone identifiers to return primary IANA time zone IDs instead of legacy ICU identifiers. (175098682)
  • Fixed Intl.DateTimeFormat to preserve the original legacy timezone identifier instead of replacing it with the primary IANA ID. (175206605)
  • Fixed Promise.prototype.finally to throw a TypeError when @@species is not a constructor, matching the behavior of other browsers. (175290627)
  • Fixed the regular expression engine to reject dangling hyphens in character class syntax when using the /v flag. (175559808)
  • Fixed a performance issue with module resolution by limiting cache population to star-resolution and indirect-resolution cases. (175826413)
  • Fixed a performance issue with TypedArray.prototype.lastIndexOf by adding SIMD-accelerated reverse search for numeric types. (175904377)
  • Fixed a performance issue where building a module namespace with many exportstatements was significantly slower than necessary. (175949532) Fixed DataView constructor to match specification-defined argument validation order and error throwing behavior. (176110210)
  • Fixed an issue where Array.prototype.concat could produce incorrect results when combining arrays with incompatible indexing types. (176219964)

WebAssembly

JavaScript Promise Integration (JSPI)

Safari 27 beta adds support for WebAssembly JavaScript Promise Integration (JSPI). JSPI lets synchronous-looking WebAssembly code suspend and wait for JavaScript Promises, making it much easier to port existing C, C++, Rust, and other language code to the web where that code expects synchronous I/O.

Before JSPI, porting code that called synchronous APIs to Wasm required rewriting everything on top of a callback or async state machine. With JSPI, the Wasm module can suspend at a call site and resume when the Promise resolves — the rest of the module sees straight-line synchronous code. This is a significant capability for the Wasm ecosystem.

Additional bug fixes:

  • Fixed WebAssembly.Suspending and WebAssembly.SuspendError to be data properties instead of getter functions, aligning with other WebAssembly attributes like WebAssembly.Module. (170155726)
  • Fixed incorrect IntegerOverflow exceptions thrown by i32.rem_s, i64.rem_s, i32.div_u, i64.div_u, i32.rem_u, and i64.rem_u when both operands are constants. (175122462)
  • Fixed a regression where RegisterSet::normalizeWidths() lost vector-width information, causing v128 argument corruption in WebAssembly SIMD thunks. (176035764)

MathML

Multiple-character operators in MathML

Safari 27 beta now supports multiple-character operators in MathML, improving the rendering of complex mathematical notation that uses operators like ++, :=, /=, and similar. We also updated operator dictionary to MathML Core, so that we also support multi-character, combining character, and updated operator dictionary.

tabindex, focus(), blur(), and autofocus on MathML

Safari 27 beta now supports tabindex, focus(), blur(), and autofocus on MathML elements, improving MathML feature parity’s with HTML. This makes math content fully participate in keyboard navigation and focus management, which supports interactive educational content and accessibility.

Additional bug fixes:

  • Fixed an issue where symmetric non-stretchy large operators were not centered around the math axis. (170905663)
  • Fixed an issue where dynamic changes to <mo> element attributes did not trigger a relayout. (170907029)
  • Fixed an issue where minsize and maxsize defaults and percentages did not use the unstretched size as specified. (170908253)
  • Fixed positioning of the <mprescripts> element within <mmultiscripts> layout. (170909975)
  • Fixed an issue where the MathML fraction bar was not painted when its thickness was equal to its width. (170934351)
  • Fixed an issue where <none> and <mprescripts> elements were not laid out as <mrow> elements in MathML. (170940035)
  • Fixed an issue where MathML token elements ignored -webkit-text-fill-color when painting math variant glyphs. (172020318)
  • Fixed padding and border rendering on <msqrt> and <mroot> elements and corrected token sizing for mathvariant. (173081436)
  • Fixed absolute positioning of elements inside MathML by ensuring logical height is updated. (173088146)
  • Fixed tabIndex values not being set correctly for MathML elements. (174734133)

Spatial Web

Immersive website environments in visionOS

Illustration of woman wearing VisionPro standing in field facing screen.

Safari 27 beta in visionOS 27 adds support for immersive website environments. Developers can now provide incredible immersive experiences with a simple <model> element and one JavaScript API call. The Immersive API on the model element works similarly to how the Fullscreen API does on video elements. Learn more in the WWDC26 session Explore immersive website environments in visionOS.

Consider what this makes possible in a browser: a ticketing site where you can see the view from your seat before you buy, or a hotel that lets you walk the room, all built with standard web technology, no app required.

First person view of sitting in a virtual theater seat looking at a browser on the right and the stage to the left.

img controls on spatial and panorama photos

Safari 27 beta in visionOS 27 adds support for the controls attribute on <img> elements displaying spatial and panorama photos. When applied, the image gets native interactive controls appropriate for the content type, allowing the image to be viewed spatially or immersively wrapped around the user.

Photo of valley surrounded by mountains, an open cloudy sky, and a lake towards the right center.

model on iOS, iPadOS, and macOS

The <model> HTML element is now available in Safari on iOS, iPadOS, and macOS, joining its existing availability in visionOS. Web developers can now embed interactive 3D content using standard HTML across Apple platforms.

<model src="teapot.usdz"></model>

Learn more in the WWDC26 session Get started with the HTML Model Element.

An iPad, desktop, and an iPhone each displaying a browser showing an image of a 3D hiking boot model.

dynamic-range-limit on model

Safari 27 beta adds support for dynamic-range-limit on the <model> element, giving you control over the HDR rendering range for 3D content in iOS and macOS.

Additional bug fixes:

  • Fixed spatial and panoramic image controls to support RTL language layout and localization of type labels. (161690817)
  • Fixed <model> elements displaying at 100x the expected size for assets authored in tools that use centimeter units. (167805672)
  • Fixed an issue where WebXR viewports did not get an initial value until getViewport() was called. (168125694)
  • Fixed an issue where the <model> element stagemode orbit physics behaved differently between iOS and visionOS. (172189776)
  • Fixed an issue on visionOS where fullscreen video would sometimes jump when exiting fullscreen if the browser window was narrower than the video. (174454557)

WebGPU

Safari 27 beta now supports the clip_distances built-in value in WGSL shaders. Clip distances are a WebGPU feature that allows vertex shaders to define custom clipping planes, enabling you to discard geometry on one side of an arbitrary plane before rasterization occurs.

Additional bug fixes:

  • Fixed compressedTexImage not validating whether the compressed texture format extension has been enabled. (175652171)
  • Fixed some texImage functions reporting errors with incorrect function names. (175652807)
  • Fixed some WebGL context state properties not being correctly reset on context loss. (176190808)
  • Fixed GPUDevice.onuncapturederror event handler attribute not working. (149577124)
  • Fixed: Restored maxStorageBuffersInFragmentStage and related WebGPU limits. (160800947)
  • Fixed rendering failing when using direct GPUTexture objects instead of GPUTextureView with multisampled resolve targets in render passes. (175452924)

Media

TextTrackCue.endTime = Infinity

Safari 27 beta supports setting TextTrackCue.endTime to Infinity to represent an unbounded cue duration. It’s useful for captions or data cues of live streams.

Additional bug fixes:

  • Fixed an issue where decoding WebM audio files with more than two channels would fail. (82160691)
  • Fixed an issue where preservesPitch and playbackRate were not correctly handled on an HTMLMediaElement connected to an AudioContext via createMediaElementSource. (93275149)
  • Fixed MediaCapabilities.decodingInfo() incorrectly reporting VP8 in WebM as not supported. (127339546)
  • Fixed an issue on iPad where exiting fullscreen on a media document incorrectly navigated back to the previous page instead of returning to the inline view. (137220651)
  • Fixed an issue where the WebCodecs VideoDecoder API output frames in an incorrect order for videos containing B-frames. (145093697)
  • Fixed an issue where the darkening overlay on inline video controls made accurate scrubbing difficult and displayed video content incorrectly on macOS. (161271114)
  • Fixed an issue where WebM with VP9/Vorbis fallback would not play. (164053503)
  • Fixed video playback failing when the declared MIME type in a <source> element does not match the actual content type served by the server. (166181001)
  • Fixed an issue where text selection was broken after pausing a video when the media player ran in the content process. (167727538)
  • Fixed HTMLMediaElement.currentTime to report smoothly progressing values instead of updating only at fixed intervals. (170115677)
  • Fixed an issue where MP4 files containing Opus audio tracks could not be decoded with decodeAudioData. (170196423)
  • Fixed an issue where the VideoFrame constructor did not handle the video color range correctly for NV12 (I420 BT601) video frames. (170299037)
  • Fixed an issue where Live Text selection was unavailable on paused fullscreen videos. (170817667)
  • Fixed an issue where FairPlay-protected VP9 content failed to play via MediaSource. (171210968)
  • Fixed an issue where autoplay would proceed before default text tracks finished loading. (171699293)
  • Fixed the currentTime getter to return defaultPlaybackStartPosition when no media player exists. (171722368)
  • Fixed HTMLMediaElement to fire a timeupdate event when resetting the playback position during media load as required by the specification. (171785463)
  • Fixed an issue where the media player preload attribute was not properly updated when the autoplay attribute was set. (171883159)
  • Fixed an issue where seeking in a WebM video did not work correctly while content was still loading. (172473039)
  • Fixed an issue where media playback could not move to the next item in a playlist when the tab was in the background. (172676372)
  • Fixed an issue where scrubbing a video in full-screen mode could cause it to exit full-screen. (172682230)
  • Fixed an issue where HDR video content appeared washed out due to colorspace information being lost during processing. (172721079)
  • Fixed Encrypted Media Extensions to check support for the full content type including codecs, rather than only the MIME type. (173852931)
  • Fixed an issue where setting HTMLMediaElement.volume had no effect when the element was connected to an AudioContext. (174278899)
  • Fixed ImageCapture to correctly queue takePhoto() and applyConstraints() requests to avoid concurrent capture session reconfiguration. (174950018)
  • Fixed a regression where videos would stop playing and lose audio after a few seconds on some websites. (174966899)
  • Fixed an issue where U+0000 (NULL) characters were not allowed in VTTCue text content. (175084171)
  • Fixed video content disappearing after switching to another tab and back. (175257980)
  • Fixed WebVTT cue settings line parsing failures. (175296476)
  • Fixed ::cue() selectors to correctly match the WebVTT root object in addition to child nodes. (175550173)
  • Fixed currentTime on iOS to update more frequently during media playback. (175774587)
  • Fixed Media Source Extensions readyState not being updated immediately when playback stalls due to a gap in buffered data. (176330683)

Web API

Service Worker static routing API

Safari 27 beta adds support for the Service Worker static routing API. This lets a service worker declare routing rules that the browser can use to bypass the service worker entirely for certain requests, reducing overhead for high-performance PWAs.

Dedicated workers inside shared workers

Safari 27 beta adds support for creating dedicated workers inside shared workers, per the HTML Standard.

ReadableStream improvements

Safari 27 beta adds three improvements to ReadableStream:

  • async iteration with for await...of
const response = await fetch("/data");
for await (const chunk of response.body) {
  process(chunk);
}
  • the ReadableStream.from() static method for creating a stream from any async iterable or iterable
const vegetables = ["Carrot", "Broccoli", "Tomato", "Spinach"];
const asyncIterator = (async function* () {
  yield 1;
  yield 2;
  yield 3;
})();

// Create ReadableStream from the array
const myReadableStream = ReadableStream.from(vegetables);

// Create ReadableStream from async iterator
const asyncReadableStream = ReadableStream.from(asyncIterator);

  • the ability to transfer a ReadableStream , WritableStream and TransformStream across contexts via postMessage()

Additional bug fixes:

  • Fixed the parent window’s history.state being set to null when history.pushState is called from a child iframe. (50019069)
  • Fixed clicking on a scrollbar of an overflow container blurring the current activeElement. (92367314)
  • Fixed an issue where the change event was not fired on <input> and <textarea> elements when they lost focus while another application was in the foreground. (98526540)
  • Fixed Web IDL bindings to correctly reject SharedArrayBuffer where [AllowShared] is not specified. (107786134)
  • Fixed Content Security Policy to only recognize ASCII whitespace excluding vertical tabs to align with the specification. (108559413)
  • Fixed emoji input on Google Docs and similar web applications by supressing keypress events for supplementary characters. (122678873)
  • Fixed an issue where MouseEvent.offsetX and MouseEvent.offsetY were not relative to the padding edge as specified. (125763807)
  • Fixed an issue on visionOS where the gamepadconnected event did not fire unless gamepad permission had already been granted. (141623162)
  • Fixed an issue where CSPViolationReportBody did not include the source line number in Content Security Policy violation reports. (152607402)
  • Fixed an issue where selecting credentials in the Digital Credentials API sometimes required a second click to trigger verification. (163295172)
  • Fixed window bar visibility properties (toolbar.visible, statusbar.visible, menubar.visible) to return static values per the HTML specification for privacy and interoperability. (166554327)
  • Fixed handling of unknown DigitalCredential protocols by gracefully filtering them out and showing a console warning instead of throwing an error. (166673454)
  • Fixed: Updated the Digital Credentials API to rename DigitalCredentialRequest to DigitalCredentialGetRequest per the latest specification. (167115220)
  • Fixed an issue where Service Worker routes were not matched when no fetch event handler was set. (167753466)
  • Fixed spec conformance issues in the Streams API piping and abort behavior. (167841090)
  • Fixed Service Worker static routing rules to enforce limitation checks as required by the specification. (167977145)
  • Fixed layerX and layerY to return correct values with CSS transforms. (168968832)
  • Fixed location.ancestorOrigins returning stale origins after an iframe is removed from the document. (169097730)
  • Fixed NavigateEvent.canIntercept to correctly return false when navigating to a URL with a different port, aligning with the Navigation API specification. (169845691)
  • Fixed NavigateEvent.navigationType to return "replace" when navigating to a URL that matches the active document’s URL. (169999046)
  • Fixed an issue where the dragend event had incorrect coordinates when dragging within a nested <iframe>. (170750013)
  • Fixed an issue where navigation.currentEntry.key did not change in private browsing windows after calling history.pushState(). (171147417)
  • Fixed an issue where touch event properties values were sometimes swapped with neighboring values. (171567543)
  • Fixed a performance issue where ResizeObserver callbacks became increasingly sluggish over time. (172718139)
  • Fixed a performance issue where IntersectionObserver became sluggish over time when observing many elements due to O(n²) iteration. (172727210)
  • Fixed an issue where navigation.currentEntry.id did not change in private browsing windows after calling history.replaceState(). (172897962)
  • Fixed an issue where document.open() incorrectly aliased the caller’s security origin. (173369038)
  • Fixed an issue where history.replaceState() on a traversed history entry incorrectly changed navigation.currentEntry.key to a new UUID instead of preserving the original key. (173388766)
  • Fixed an issue where Object.prototype could not be serialized by structuredClone(). (173728983)
  • Fixed an issue where backslashes were not handled correctly in non-special URLs. (173757759)
  • Fixed a URL parsing bug in the special relative or authority state. (173772241)
  • Fixed an issue where event listener once and passive flags were not preserved when copying listeners between elements. (173834642)
  • Fixed: Preserved existing listener options (such as passive defaulting) when overwriting event handler attributes. (173842822)
  • Fixed the Credential Management API to properly define which credential types are allowed in the same get() request. (173918198)
  • Fixed an issue where event.target was not set after dispatching an event in a shadow tree with no listeners. (174136382)
  • Fixed an issue where navigator.credentials.create() and navigator.credentials.get() discarded the AbortSignal reason and always rejected with a generic AbortError. (174220589)
  • Fixed Range.extractContents() to not extract out-of-bounds nodes when the end container is removed during extraction. (174307275)
  • Fixed document.createEvent() to throw an exception for "MutationEvents", "MutationEvent", "PopStateEvent", and "WheelEvent", aligning with other browser engines. (174339775)
  • Fixed ParentNode.append() to correctly de-duplicate nodes when the same node is passed multiple times. (174365465)
  • Fixed an issue where MutationObserver delivered childList records in the wrong order when script ran during node insertion. (174368989)
  • Fixed an issue where setting a URL object’s port property to whitespace behaved incorrectly. (174484035)
  • Fixed a missing return in the Navigation API’s performTraversal that caused incorrect behavior when traversing to an unknown key. (174513305)
  • Fixed Blob.slice() to correctly clamp fractional start and end parameters using round-half-to-even rounding per the File API specification, which may change how edge-case fractional values like 0.5 are rounded. (174555334)
  • Fixed postMessage() to validate transferable object states after serialization, aligning with the HTML specification. (174558047)
  • Fixed structuredClone() and window.postMessage() to correctly throw a DataCloneError when serializing a SharedArrayBuffer outside of cross-origin isolated contexts. (174562553)
  • Fixed an issue where calling Element.blur() on an <iframe> did not reset document.activeElement to <body>. (174591529)
  • Fixed document.styleSheets to be accessible on documents created by DOMParser. (174625774)
  • Fixed innerText getter to correctly handle trailing newlines and blank lines for <p> elements and headings. (174642704)
  • Fixed innerText whitespace handling at inline-block boundaries. (174713114)
  • Fixed XMLSerializer namespace handling to correctly serialize elements with namespace prefixes. (174726401)
  • Fixed the innerText getter to preserve newlines for elements with white-space: pre-line. (174727341)
  • Fixed service worker registrations to be unregistered when the main script is missing. (174755909)
  • Fixed innerText handling of replaced elements at block boundaries. (174816319)
  • Fixed EventSource to be closed when window.stop() is called. (174830925)
  • Fixed service worker registrations to be unregistered when failing to retrieve stored imported scripts. (174833692)
  • Fixed calling preventDefault() during a pointerdown event to correctly suppress mousedown and mouseup events on iOS. (174864309)
  • Fixed innerTextto not fall back totextContentfor elements withdisplay: contents`. (174883499)
  • Fixed innerText to preserve the contents of <option> elements inside <select>. (175006854)
  • Fixed Element.innerText to collect option text when called directly on a <select> element. (175156630)
  • Fixed worker scripts to always be decoded as UTF-8, as per the specification. (175327455)
  • Fixed an issue where an Event object’s target property could lose its JavaScript wrapper due to premature garbage collection. (175439759)
  • Fixed an issue where ancestors of TreeWalker.currentNode could be prematurely garbage collected. (175442228)
  • Fixed FileSystemDirectoryHandle.resolve() to return the correct path array for child entries. (175645387)
  • Fixed PerformanceNavigationTiming.domInteractive and domContentLoadedEventEnd incorrectly returning 0 instead of the correct timestamps. (175739835)
  • Fixed FileSystemDirectoryHandle.removeEntry() to correctly remove entries. (175745157)
  • Fixed CryptoKey to correctly remain associated with its secure context. (176157712)
  • Fixed: Improved cross-origin isolation enforcement for workers. (176175488)
  • Fixed SharedArrayBuffer cloning and agent cluster ID assignment. (176465817)

Rendering

srgb-linear and display-p3-linear

Safari 27 beta adds srgb-linear and display-p3-linear to predefined color spaces, making these linear-light color spaces available in Canvas, WebGL, and other APIs.

Subpixel inline layout

Subpixel inline layout is now available in Safari 27 beta. Text and inline elements can now be positioned with device-pixel precision, improving layout accuracy in block direction.

You don’t need to do anything to benefit. Layout engines have been moving in this direction for years; Safari 27 brings inline layout up to that bar.

Additional bug fixes:

  • Fixed an issue where a 2D canvas element unnecessarily forced a compositing layer. (172864747)
  • Fixed an issue where table cells with rowspan values exceeding the actual number of rows were incorrectly computing heights. (3209126)
  • Fixed an issue where ::first-letter styles caused Range.getClientRects() and Range.getBoundingClientRect() to return incorrect dimensions. (71546397)
  • Fixed incorrect distributed height in table rows when a <td> element has an explicit height set. (78549188)
  • Fixed an issue where U+2028 LINE SEPARATOR was not rendered as a forced line break. (88470339)
  • Fixed an issue where a block formatting context with margin-start could overlap an adjacent float. (93187697)
  • Fixed position: relative on table rows (<tr>) to correctly establish a containing block for absolutely positioned descendants. (94294819)
  • Fixed <marquee> elements causing incorrect table width calculations. (99826593)
  • Fixed boxes in the top layer to use the initial containing block as their static-position rectangle. (155495104)
  • Fixed an issue where a flex item containing a percentage-height image did not shrink correctly around the image. (156902823)
  • Fixed an issue where form controls with height: 100% in auto-height containers incorrectly resolved to zero height. (161699543)
  • Fixed incorrect box sizing on inline elements when they have no siblings and their padding-left plus margin-left equals zero. (162376969)
  • Fixed a regression where an element inside an iframe gaining its own compositing layer could cause an iframe’s semi-transparent background to appear darker.(163509267)
  • Fixed an issue where space-taking scrollbars did not trigger a proper re-layout when the box size depends on content size. (166836126)
  • Fixed an issue where View Transition snapshots were incorrectly stored in sRGB, causing rendering issues with non-sRGB colors. (167634138)
  • Fixed an issue where font subpixel quantization was unnecessarily disabled in some cases, improving text rendering quality. (168088611)
  • Fixed table layout to properly handle visibility: collapse on columns. (168556786)
  • Fixed intrinsic sizing for absolutely positioned replaced elements. (168815514)
  • Fixed text being incorrectly truncated in RTL containers when combined with text-overflow: ellipsis and an inline-block pseudo-element. (168875614)
  • Fixed percentage padding in table cells to resolve against column widths. (168940907)
  • Fixed a regression where hovering over elements could leave repaint artifacts on the page. (169112402)
  • Fixed table height distribution to apply to tbody sections instead of only the first section. (169154677)
  • Fixed an issue where table sections with explicit heights did not properly constrain and distribute space among contained rows. (169235210)
  • Fixed an issue where images with min-width: fit-content rendered at an incorrect width. (169359566)
  • Fixed an issue where height: 100% was incorrectly calculated for replaced elements like images serving as grid items nested inside a flexbox. (169431440)
  • Fixed an issue where images were incorrectly stretched in certain page layouts. (170270187)
  • Fixed an issue where Find in Page scrolled to the wrong location when matching text inside elements with user-select: none. (170477571)
  • Fixed the baseline calculation for inline-block elements so that when overflow is not visible, the baseline is correctly set to the bottom margin edge. (170575015)
  • Fixed an issue where replaced elements did not correctly apply min-height and min-width constraints in certain configurations. (170765025)
  • Fixed an issue where overlay backgrounds would briefly dim incorrectly when de-compositing in a scrollable container. (171024685)
  • Fixed a regression where sticky-positioned elements inside overflow containers could appear in front of content that should overlap them. (171179878)
  • Fixed an issue where auto table layout did not honor max-width on table cells when distributing width between them. (171459245)
  • Fixed an issue where border-spacing incorrectly included collapsed columns in auto table layout calculations. (171468102)
  • Fixed an issue where percentage-height children of table cells with unresolvable percentage heights were not sized intrinsically. (171469500)
  • Fixed an issue where cell backgrounds in collapsed-border tables extended into adjacent cells’ border space at table edges. (172068907)
  • Fixed an issue where if a document in an iframe uses @prefers-color-scheme, it does not follow the color-scheme set by grandparents of the iframe. (172229372)
  • Fixed an issue where list item margins were computed incorrectly when the page was zoomed in or out. (172312498)
  • Fixed an issue where about:blank iframes did not always have a transparent background. (172400258)
  • Fixed an issue where a right-floated table could overlap another table. (172655655)
  • Fixed an issue where grid containers failed to avoid float boxes. (172655720)
  • Fixed an issue where an anonymous block created for list markers was not properly collapsed when block content prevented line-box parenting. (172686060)
  • Fixed an issue where checkboxes could overlap with adjacent text. (172741572)
  • Fixed an issue where checkbox outlines appeared misaligned. (172742551)
  • Fixed an issue where list markers rendered on the wrong line when list items started with empty inline elements. (172762578)
  • Fixed an issue where U+2029 PARAGRAPH SEPARATOR was not treated as a forced line break. (173106856)
  • Fixed an issue where tiles were missing after navigating back in history. (173288233)
  • Fixed an issue where view transition snapshots could capture stale transform values for accelerated CSS transform animations. (173323193)
  • Fixed an issue with outside list markers when blockification prevents line-box parenting. (173417560)
  • Fixed a regression where nested empty inline boxes accumulated an incorrect vertical offset, causing inline elements to stack as block-level elements. (173723162)
  • Fixed an issue where pseudo-elements were incorrectly included in outline rect collection. (174033087)
  • Fixed an inverted Y-axis comparison that could cause incorrect caret positioning. (174144220)
  • Fixed an issue where <br> elements with line-height: 0 still created extra vertical space, failing to respect the declared line height. (174400946)
  • Fixed auto outlines to more closely follow the border radii of elements. (174466854)
  • Fixed how gradients are renderer to improve performance. (174880197)
  • Fixed image-orientation being ignored for background-image, border-image, and list-style-image. (174894122)
  • Fixed a white-space: pre-wrap layout issue with justified text. (174937310)
  • Fixed an issue where minimum height was not correctly computed for flex items. (174999995)
  • Fixed an issue with flex-wrap and flex factor computation for wrapping flex items. (175012395)
  • Fixed vertical writing-mode content incorrectly wrapping when the parent has auto height. (175123356)
  • Fixed minimum height calculation for flex items. (175195518)
  • Fixed incorrect bounding box position for newline characters. (175243361)
  • Fixed an issue where a child element with filter: blur() ignored border-radius overflow clipping from its parent. (175519148)
  • Fixed an issue where absolutely positioned tables with content exceeding their declared width were incorrectly positioned. (175755871)
  • Fixed height calculations for absolutely positioned tables with percentage-sized children. (175762381)
  • Fixed an issue where containers with block-in-inline content did not expand when max-height was removed. (175799547)
  • Fixed an issue where absolutely positioned tables with explicit percentage or fixed heights did not resolve correctly against their containing block. (175852400)
  • Fixed always-on scrollbar thumbs not rendering on the root element of nested documents with display: flex. (175866046)
  • Fixed absolutely positioned tables ignoring min-height and shrinking below their content height. (175883577)
  • Fixed absolutely positioned tables ignoring max-height constraints. (175932457)
  • Fixed scrollbar-gutter placement on the root element in RTL layouts. (175939512)
  • Fixed an issue where a flex item with aspect-ratio and content-box padding computed the wrong height in a column flex container. (176033726)
  • Fixed an issue where block-level boxes nested within inline elements were not properly aligned when using align-content: center. (176173122)
  • Fixed intrinsic sizing for non-replaced elements with percentage dimensions. (176493856)
  • Fixed an issue where panning a zoomed-in PDF on iOS would frequently rubber band back to the starting position. (156854435)

WebRTC

Safari 27 beta adds support for the targetLatency attribute in WebRTC, for specifying a target latency on a receiver. It adds support for the RTCRtpCodec dictionary and related constructs, improving the ability to inspect and configure codecs. It adds support for RTCRtpReceiver.jitterBufferTarget, for tuning the jitter buffer. And it adds video source width and height to RTC stats.

Additional bug fixes:

  • Fixed an issue where I420 BT709 VideoFrame was encoded in an incorrect color space when encoding to VP9. (169425608)
  • Fixed an issue where RTCPeerConnection.addIceCandidate() did not reject when the connection was already closed. (170470988)
  • Fixed an issue where RTCDataChannel did not check the SCTP buffered amount synchronously. (172386678)
  • Fixed an issue where MediaStreamTrack could have incorrect settings if the source settings changed while the track was being transferred. (172657570)
  • Fixed an issue where a remote WebRTC track was not unmuted when the first packet was received. (172904930)
  • Fixed validation of RTC send encodings to better align with the specification. (172997814)
  • Fixed an issue where RTCRtpSender.setParameters did not clear parameters that were unset by the web application. (173678165)
  • Fixed WebRTC VP9 encoders to correctly propagate frame colorspace information. (174008548)
  • Fixed a regression where RTCPeerConnection with iceTransportPolicy: "relay" failed to gather ICE candidates. (174794660)
  • Fixed RTCInboundRtpStreamStats.trackIdentifier to match MediaStreamTrack.id. (174938984)

Web Extensions

The runtime.getDocumentId() Web Extension API now has support in Safari 27 beta. It adds reporting of uncaught JavaScript exceptions and unhandled promise rejections in Web Extension scripts, making extensions easier to debug. And it adds support for propagating user gestures through sendMessage(), connect(), postMessage(), and executeScript() — so extensions can reliably perform actions like media playback that require user activation.

Additional bug fixes:

  • Fixed browser.i18n.getMessage() to correctly substitute named placeholders when they appear adjacent to non-space characters. (169146196)
  • Fixed browser.i18n.getMessage() to correctly substitute two adjacent named placeholders. (175315700)
  • Fixed an issue where web extension service worker registration database files accumulated on each Safari launch, causing performance degradation. (175484888)

Networking

Secure cookies on loopback

Safari 27 beta adds support for Secure cookies on loopback hosts. For loopback hosts using plaintext HTTP, cookies marked Secure can now be set via JavaScript and Set-Cookie headers, matching the behavior of other browsers. This simplifies local development and testing with Secure cookies.

Additional bug fixes:

  • Fixed redirects to data: URLs to be blocked for subresources such as images and scripts, aligning with the Fetch specification. (74165956)
  • Fixed XMLHttpRequest incorrectly dropping the request body during redirects. (98459882)
  • Fixed X-Frame-Options to only strip tab or space characters, not vertical tabs. (126915315)
  • Fixed an issue where Safari’s address bar could display an internationalized domain name (IDN) homograph as a visually identical legitimate Latin domain, enabling potential phishing attacks. (166796168)
  • Fixed an issue where the preload scanner did not include integrity metadata in requests, causing incorrect Integrity-Policy violation reports. (168280745)
  • Fixed range request validation to properly handle HTTP 416 (Requested Range Not Satisfiable) responses. (168487440)
  • Fixed a regression where the referrer could be missing after a process-swap navigation. (169006635)
  • Fixed incorrect URL query percent-encoding when using non-UTF-8 character encodings such as iso-8859-2, windows-1250, and gbk. (169566553)
  • Fixed an issue where the multipart form data parser incorrectly required CRLF after the closing delimiter, causing some web applications to fail to render correctly. (174348783)
  • Fixed an issue where partitioned cookies could not be deleted via WKHTTPCookieStore. (174557252)

Storage

maxAge in the Cookie Store API

Safari 27 beta now supports specifying maxAge when setting a cookie via the Cookie Store API.

await cookieStore.set({
  name: "session",
  value: "abc123",
  maxAge: 60 * 60 * 24 * 7, // one week, in seconds
});

Additional bug fixes:

  • Fixed an issue where IndexedDB could incorrectly return a version 0 database after an abort during the initial onupgradeneeded event. (176195526)

Editing

Safari 27 beta now supports menu items that convert editable text between Simplified and Traditional Chinese characters, now available in the “Transformations” submenu of the context menu for relevant text selections.

Additional bug fixes:

  • Fixed an issue where characters styled with ::first-letter were not selectable. (5688237)
  • Fixed font size 13pt being incorrectly mapped to <font size="2"> (10pt) when using rich text editing. (15292320)
  • Fixed an issue where the Font Picker style selection became unusable after changing fonts when editing multiple lines of text. (110651645)
  • Fixed an issue where adjusting text selection with touch handles was prevented by JavaScript touch event handling on some websites. (151851274)
  • Fixed an issue where execCommand('FormatBlock') did not preserve inline styles of replaced block elements, causing text formatting to be lost when pasting content. (157657531)
  • Fixed an issue where text-indent flickered or was ignored on contenteditable elements while typing. (170280101)
  • Fixed an issue where text selection would jump unexpectedly when selecting absolutely-positioned content inside an element with user-select: none. (170475401)
  • Fixed an issue where text selection was lost when focus transitioned from a contentEditable element to a non-editable target. (171221909)
  • Fixed an issue where CJK encoding state was not reset appropriately during text decoding. (174649963)

SVG

Safari 27 beta adds support for the lang and xml:lang attribute in SVG. You can now specify the language of text content in SVG, enabling correct language-aware text rendering and accessibility announcements.

Additional bug fixes:

  • Fixed offsetX and offsetY for SVG elements to use the outermost SVG as the base for coordinate calculation. (168548585)
  • Fixed an issue where backslash-escaped dot characters in SVG animation timing attribute ID references were not parsed correctly. (94260935)
  • Fixed an issue where SMIL animations of href or xlink:href on SVG <image> elements had no visual effect. (96316808)
  • Fixed an issue where SVG animation did not clear the animated CSS property when attributeName was dynamically changed. (97097883)
  • Fixed :visited link color to properly propagate to SVG through currentColor. (98776770)
  • Fixed an issue where a CSS filter referencing an SVG filter via url(#id) was not invalidated when the filter content changed. (101870430)
  • Fixed SVG2 systemLanguage attribute to improve parsing and compliance with the specification. (116427520)
  • Fixed removing an item from SVGTransformList to properly allow attribute removal. (117840533)
  • Fixed an issue where the XML document parser did not defer inline script execution until pending stylesheets had loaded. (122574381)
  • Fixed an issue where animated SVG images referenced via an <img> tag did not animate correctly due to repaint artifacts with object-fit. (141815698)
  • Fixed an SVG <tspan> positioning bug with xml:space="preserve" that caused multi-line text to render incorrectly. (143722975)
  • Fixed an issue where SVG elements referencing non-existent filter IDs were not rendered. (164046592)
  • Fixed an issue where URL fragments were not percent-decoded before being used for SVG references. (169582378)
  • Fixed: Updated the default values of fx and fy attributes on SVGRadialGradientElement to 50% to align with the SVG2 specification. (169645572)
  • Fixed SVGAnimatedRect.baseVal to ignore invalid values set on the viewBox attribute, such as negative width or height, aligning with Firefox and Chrome. (170214971)
  • Fixed SVG length attributes to reset to their default values when removed, rather than retaining previously set values. (170360351)
  • Fixed an issue where getScreenCTM() did not include CSS transforms and zoom contributions in the legacy SVG rendering path. (171525696)
  • Fixed an issue where an invalid attribute type in one SVG animation group prevented all subsequent animation groups from running. (172593109)
  • Fixed a regression where wheel events were not dispatched to an empty <svg> root element. (172909441)
  • Fixed an issue where SMIL parseClockValue did not reject out-of-range minutes and seconds values per the SMIL timing specification. (173577212)
  • Fixed the SMIL values attribute to preserve empty values and handle trailing semicolons. (173594455)
  • Fixed SMIL repeat(n) event conditions not triggering animations. (173599629)
  • Fixed SVG2 getStartPositionOfChar and getEndPositionOfChar to be more compliant with the specification. (174145885)
  • Fixed glyph-orientation-vertical: auto to use UTR#50 Vertical Orientation properties for correct character orientation in vertical text. (175064567)
  • Fixed SVG intrinsic aspect ratio being incorrectly suppressed when preserveAspectRatio is set to none. (175173375)
  • Fixed SVG images without complete intrinsic dimensions incorrectly using ratio-based scaling for background-size. (175345107)
  • Fixed the SMIL clock value parser to accept hours greater than 99 and reject malformed seconds values. (175593583)
  • Fixed stroke-dasharray interpolation to use least common multiple for list length matching and corrected composition behavior. (175598175)
  • Fixed an issue where @prefers-color-scheme in an SVG image will sometimes not follow the system color preference. (176413340)

WKWebView

Safari 27 brings a significant expansion of WKWebView public API for native app developers. The additions make it easier to build advanced browser and web-hosting experiences on top of WebKit:

  • WKSerializedNode — clone DOM nodes, including shadow roots, between different WKWebView instances.
  • WKJSHandle — use JavaScript object references from native code.
  • WKContentWorldConfiguration — configure content world properties such as autofill scripting, shadow root access, and inspectability when creating a WKContentWorld.
  • alternateRequest and overrideReferrerForAllRequests on WKWebpagePreferences — modify the main resource request during navigation and apply custom referrer headers across all resource loads.
  • willSubmitForm callback on WKNavigationDelegate — receive notification of HTML form submissions via a new WKFormInfo object.
  • mainFrameNavigation on WKNavigationAction and mainFrameNavigation on WKNavigationResponse — correlate navigation actions and responses with each other and their originating loads.
  • WKWebView.load(url:) — load a URL directly without wrapping it in an NSURLRequest.
  • WKHTTPCookieStore.cookies(for:) — retrieve cookies matching a specific URL without fetching the entire cookie store.

Web Inspector

Several Web Inspector updates in Safari 27 beta make common debugging tasks easier.

The Color Picker now shows color contrast information inline as you edit — no more switching tools mid-decision to check whether a color combination is accessible. This works when you’re editing both foreground and background colors at the same time.

Color picker in Web Inspector showing color contrast information

The Color Picker’s format and gamut controls are also now visible upfront instead of hidden. If you’ve ever gone hunting for those options, this will help.

Color picker in Web Inspector showing dropdown of formats.

In the Network tab, when a resource redirects, you can now see every request in the chain rather than just the final destination. It’s much easier to figure out what’s actually happening.

The network tab in Web Inspector showing three redirects.

The Elements tab adds Subgrid and Grid-Lanes badges that make it easy to identify subgrid and grid-lanes layout contexts as you explore a page.

Shows the subgrid lines in Web Inspector on top of Stripe's Dev page.

The Timeline tab now includes the layout root element in Layout event details, so you can see which element triggered a layout pass.

Additional bug fixes:

  • Fixed an issue where CSS properties added to new rules were not applied and were marked as invalid. (103548968)
  • Fixed an issue in the Network panel where the Request / Response menu did not remember the user’s previously selected value. (108231795)
  • Fixed editing inline style attribute values in the Elements panel resulting in truncated or malformed content. (149523483)
  • Fixed an issue where the input field for recording canvas frames in the Graphics tab was sometimes too small to type in and only allowed typing one character at a time. (157787230)
  • Fixed the Network tab filtering by resource type not working after clearing a filter that had no matches. (161570940)
  • Fixed an issue where CSS rules added via the “Add New Rule” button in the Styles panel were intermittently not applied or would vanish after entering a property. (164971557)
  • Fixed an issue where tree outlines in Web Inspector would intermittently show blank content while scrolling when a filter was active. (169502061)
  • Fixed an issue where an active recording in the Timelines tab would stop when navigating or reloading the current page even when the setting to stop recording once the page loads was turned off. (169732727)
  • Fixed an issue in the Timelines tab where rows containing object previews were sometimes not visible in the heap snapshot data grid. (170164522)
  • Fixed context menu items in the Elements tab to only display relevant options when multiple DOM nodes are selected. (170307979)
  • Fixed an issue where previewing resources in the Network tab displayed an error upon navigating away and Preserve Log was enabled. (171216835)
  • Fixed an issue where selected DOM node keys in a Map in the Scope Chain sidebar had unreadable white text on a light background. (171840122)
  • Fixed an issue where the WebAssembly debugger had no source bytes for modules compiled via WebAssembly.instantiateStreaming, preventing source-level debugging in LLDB. (174362152)
  • Fixed the WebAssembly debugger to generate human-readable module names from the WebAssembly name section and fetch URL, replacing bare address-based fallback names in LLDB’s image list. (174465437)
  • Fixed the Safari Develop menu and WebDriver to launch Device Hub instead of Simulator when available in Xcode. (174276041)
  • Fixed the Layers 3D view to correctly map textures to composited bounds and use proper selection highlighting instead of tinting textures. (174355052)
  • Fixed the Layers 3D view to re-snapshot preserved layers after a repaint instead of displaying stale textures. (174358757)
  • Fixed an issue where all folder tree elements were expanded after filtering for a resource in the Sources panel. (175009135)
  • Fixed an erroneous “There are unread messages that have been filtered” banner appearing in the Console when console.groupCollapsed() is used. (175279759)
  • Fixed an issue in the Storage tab where filtering by storage type did not reveal the popup with options. (175444192)
  • Fixed Timeline recordings showing unrelated events incorrectly nested inside longer events. (176309164)
  • Fixed Web Inspector toolbar buttons rendering at incorrect sizes. (176508343)

Accessibility

  • Fixed an issue where calling speechSynthesis.cancel() removed utterances queued by subsequent speechSynthesis.speak() calls. (46151521)
  • Fixed an issue where SVG <use> elements referencing <symbol> elements inside an <img> were incorrectly included as unnamed images in VoiceOver’s Images rotor. (98999595)
  • Fixed an issue where changing the id attribute of an element targeted by aria-owns did not update the accessibility tree. (107644248)
  • Fixed slot elements referenced by aria-labelledby to correctly use their assigned slotted content for accessible names and ignore hidden slotted nodes. (114500560)
  • Fixed <meter> element to have consistent labels between aria-label and title attributes. (127460695)
  • Fixed elements with display: contents and content in a shadow root to have their content properly read when referenced by aria-labelledby. (129361833)
  • Fixed aria-labelledby to use the checkbox name instead of its value when the checkbox name comes from an associated <label> element. (141564913)
  • Fixed VoiceOver cursor positioning for elements focused via the drawFocusIfNeeded() canvas API. (146323788)
  • Fixed grid elements with child rows in a shadow root to properly work with VoiceOver. (153134654)
  • Fixed an issue where VoiceOver read text within images that have role="presentation". (159304061)
  • Fixed an issue where content within dynamically expanded <details> elements was not exposed in the accessibility tree. (159865815)
  • Fixed an issue where the contextmenu event was not fired for elements inside iframes when triggered by keyboard or assistive technology actions such as VoiceOver’s VO+Shift+M. (164128676)
  • Fixed an issue where changes to <input type="button"> elements inside live regions were not announced by assistive technologies. (168200460)
  • Fixed ::first-letter text not being exposed in the accessibility tree when no other text accompanies it. (168458291)
  • Fixed an issue where VoiceOver was unable to access aria-owned rows and their cells in grids and tables. (168770938)
  • Fixed an issue where VoiceOver could not find focusable splitter elements when navigating to the next or previous form control. (170187464)
  • Fixed an issue where color picker inputs could not be activated using VoiceOver’s press action. (172218114)
  • Fixed an issue where interactive elements containing an <svg> named by a child <title> element did not expose an accessible name. (172559238)
  • Fixed an issue where incorrect bounding boxes were computed for MathML table rows and cells. (172851295)
  • Fixed an issue where comboboxes did not forward focus to their aria-activedescendant, preventing assistive technologies from interacting with list items. (172931277)
  • Fixed an issue where aria-owns was not respected when computing the accessible name from element content. (173249317)
  • Fixed VoiceOver line-by-line reading skipping content in read-only documents. (174349841)
  • Fixed invalidation of aria-hidden=”true” when focus lands inside the aria-hidden subtree. (174449524)

Forms

  • Fixed an issue where keyboard commands such as paste did not work in form fields that restrict input to numbers. (4360235)
  • Fixed an issue where keyboard tabbing position was lost when a focused button became disabled, causing focus to jump to the beginning of the page. (120676409)
  • Fixed an issue where small range input slider thumbs were difficult to interact with on iPadOS and visionOS by expanding their touch hit area. (147428926)
  • Fixed <datalist> suggestions appearing with with white text on a white background in dark mode after typing. (168676757)
  • Fixed an issue on iOS where typing into an <input> element associated with a <datalist> was intercepted by type-to-select behavior. (173346270)
  • Fixed: Made the <input type="checkbox" switch> control behave more like other controls with regards to native appearance CSS properties. (173487610)
  • Fixed an issue where date and time input types without min or max attributes incorrectly matched the :in-range pseudo-class. (174829899)
  • Fixed an issue where cloned <input> and <textarea> elements did not preserve their user-modified state. (174892989)
  • Fixed field-sizing: content clipping the placeholder on number inputs that have no value. (175883299)

Printing

  • Fixed an issue where animations were ignored during print, causing missing content on animated pages. (36901701)
  • Fixed an issue where printing light text on a dark background with backgrounds disabled could result in invisible text. (170070133)
  • Fixed a regression where printing a WebView embedded in an enclosing NSPrintOperation dropped all text. (174756900)

Updating to Safari 27

Safari 27 beta is available on Golden Gate (macOS 27), iOS 27, iPadOS27 and visionOS 27.

To try Safari 27 beta, install the developer beta on your Apple device. You can also install Safari Technology Preview for macOS 27 beta and macOS 26.

Feedback

We love hearing from you. To share your thoughts, find our web evangelists online: Jen Simmons on Bluesky / Mastodon, Saron Yitbarek on BlueSky, and Jon Davis on Bluesky / Mastodon. You can follow WebKit on LinkedIn. If you run into any issues, we welcome your feedback on Safari UI (learn more about filing Feedback), or your WebKit bug report about web technologies or Web Inspector. If you run into a website that isn’t working as expected, please file a report at webcompat.com. Filing issues really does make a difference.

June 08, 2026 07:00 PM

June 02, 2026

Igalia WebKit Team: WebKit Igalia Periodical #66

Igalia WebKit

Update on what happened in WebKit in the week from May 19 to June 1.

The main feature of this week are new releases: stable ones with many security fixes, and development ones with the new Skia-based compositor enabled. Additionally, there was work on Web-facing features, optimizations, spell checking support for the WPE port, and more.

Cross-Port 🐱

WebKit now supports mirroring MathML stretchy operators using the OpenType rtlm feature.

Replaced the CloseWatcherManager's escapeKeyHandler, which will allow other types of close signals to be supported.

Implemented queuing mutation observer records in the work-in-progress moveBefore() implementation.

Implemented popover integration with close watcher.

Fixed popover light dismiss to account for popovertarget on input buttons.

Content filters now create temporary files in the compiled filters directory, which ensures that a file rename can always be used to place them at their final location. This avoids falling back to a regular file copy, which can be slower, when the temporary directory returned by g_get_tmp_dir() (typically /tmp) is in a different volume than the filters' storage path configured for WebKitUserContentFilterStore.

WPE WebKit 📟

Enabled spell checking support in WPE. The existing implementation for the WebKitGTK port, which uses the Enchant library as a backend, was generalized to provide spell checking support in WPE as well. The feature may be toggled at build time using the ENABLE_SPELLCHECK CMake option.

Releases 📦️

WebKitGTK 2.52.4 and WPE WebKit 2.52.4 have been released; they include a number of fixes for security issues, and it is a highly recommended update. The corresponding security advisory, WSA-2026-0003 (GTK, WPE is available as well. The release also includes a number of small improvements and Web compatibility fixes.

Additionally, development releases WebKitGTK 2.53.3 and WPE WebKit 2.53.3 are available since last week. These include a change to use a new Skia-based compositor by default, which is intended to replace TextureMapper once ready. Therefore, bug reports related to website rendering are particularly welcome when using this and subsequent development releases.

Infrastructure 🏗️

The deprecated and un-maintained Flatpak-based SDK was removed. Developers working on the WPE and GTK WebKit ports are encouraged to migrate to the new SDK.

That’s all for this week!

By Igalia WebKit Team at June 02, 2026 12:12 AM

Pawel Lampe: WPE memory leak investigation playbook

Igalia WebKit

Depending on the web application, the WPE WebKit memory usage trend can vary. When simple web applications are being processed, the memory consumption tends to be virtually stable (the same) no matter the period. However, when more complicated web applications are being executed, the memory usage usually grows over time while going back to normal from time to time e.g., when GC / memory pressure mechanism releases all kinds of caches and not-needed memory. Therefore, memory growth itself is not unusual. Nevertheless, as the memory leaks happen in WPE at times, the memory growth is worth investigating — especially if very rapid or unbounded.

This article presents a structured playbook for investigating such a memory growth and memory leaks in WPE. Rather than diving straight into debugging tools, it starts from first principles: confirming the problem is real, choosing the right environment to work in, and narrowing down the leaking area before any heavy tooling is involved. The goal is to reach actual debugging as fast as possible, regardless of whether the environment is an embedded device or a desktop machine, and regardless of how quickly the problem reproduces.

Playbook #

The high-level list of recommended steps to follow is presented below. In a nutshell, the steps 1, 2, and 3 are meant to choose and follow the fastest possible investigation path so that actual debugging of the problem (step 4) can be started as soon as possible.

  1. Confirming the problem
  2. Identifying the best setup for reproducing the problem
  3. Narrowing down
  4. Debugging

1. Confirming the problem #

The ultimate first step when working with alleged memory leak is to check whether the observed memory growth is actually abnormal. In the case of web browsers in general, the memory growth alone may not necessarily mean something is leaking. There may be many regular reasons why the browser’s memory usage is growing, but the usual suspects are:

  • JavaScript-level memory allocations — due to the very nature of JavaScript, the memory it allocates causes the overall web content process memory growth up until the garbage collector (GC) kicks in. Then (from the RSS perspective) some memory is usually freed. However, as it’s not easy to predict when the GC will be invoked (e.g., when the browser processes an application that performs heavy rendering), it’s possible that memory will grow but remain garbage-collectible.
  • JavaScript Just-in-Time (JIT) compilation — when not explicitly disabled or limited, the processing of any web application that has JavaScript code associated with it will cause the browser to continuously compile the JavaScript code in the background so that it executes such code faster in runtime at the expense of memory that is required for storing compiled artifacts.
  • Caches — as the WPE operates, it caches things such as web resources, style resolution artifacts, textures, glyph atlases, layer tiles, display lists, rasterization artifacts, and many others. Naturally, the cache sizes are limited, however, if many caches are growing at the same time, they may create an impression of a leak. The difference in that case is, the caches stop growing at some point.

Due to the above, to confirm the memory growth is abnormal, one should usually try the following first:

  1. Triggering memory pressure to force the browser to trigger GC and evict as many cache entries as possible,
  2. Rerunning the browser with JIT disabled to rule out the JIT-related memory growth — unless the application code is very small.

If the memory growth doesn’t stop with JIT disabled or its level does not go back to normal after triggering memory pressure, the growth can be assumed to be abnormal, and one can proceed to the next step.

2. Identifying the best setup for reproducing the problem #

When the memory growth is atypical, it needs to be narrowed down in a way that the final debugging is possible. For both narrowing down and the debugging, one should aim at the most flexible development environment along with the smallest possible web application that reproduces the problem quickly. What it means in practice is — desktop environment along with small demo web application that reproduces the problem. Whilst it’s not always possible to have such an environment, the 3 general rules are as follows:

  1. Desktop environment is usually better than embedded one in terms of working with memory leaks as it offers minimal overhead (e.g., in terms of compilation times) and huge flexibility in choosing the industry standard tools for profiling/debugging.
  2. Small web application is always better than a big one as long as it still reproduces the same problem in the same amount of time. In such case, a small application minimizes the amount of noise that usually stands in the way of profiling/debugging.
  3. A web application that reproduces the problem quickly is always better than the one that needs much more time for it. The worst thing that can happen in the case of narrowing down memory leaks, is when the memory growth is noticeable or starts after a very long time such as hours/days+.

Given the above, at this point one should go through the below steps:

  1. Check if the setup is trivial enough already — if the web application reproduces the problem quickly in a desktop environment and is simple enough, one should immediately jump to the Debugging section.
  2. Check if the problem can be reproduced on desktop assuming it originally reproduces on embedded.
  3. Check if the problem can be reproduced faster if it’s not reproducing fast enough.
  4. Check if the web application could be simplified.

Once the setup is simplified as much as possible, one should proceed to one of narrowing down sections depending on the setup. Also, if the setup is still not ideal, one should actively seek opportunities for simplifying the setup even during narrowing down as it’s likely that some new information will eventually open new possibilities in terms of simplifying setup.

3. Narrowing down #

When the problem has been confirmed but there are not enough clues to tell exactly which parts leak, the debugging cannot be started right away. In such case, it’s necessary to narrow down the problem to the browser/application area that can be easily debugged.

While in some cases narrowing down is not even necessary, quite often it takes orders of magnitude more time than actual debugging, and hence one should pay special attention to this step.

3a. Narrowing down on embedded when the problem takes a long time to reproduce #

This is the toughest situation one can find themselves in. When a problem takes a long time to reproduce (hours/days+), every iteration/test comes automatically with a significant cost. Moreover, when the environment is an embedded one, rebuilding WPE is usually more time-consuming and the amount of tooling is usually limited — or requires some work to bring it to the image at least.

Due to the above, narrowing down the problem in this setup requires a structured approach with extra care. In such case, the things to check should be approached in steps defined as follows:

  1. Things to check without rerunning the WebKit
    • in case of embedded devices, extra care is needed when attaching a memory profiler. On low-end devices, memory profilers tend to slow down the application hard enough to trigger otherwise non-existent problems.
  2. Things to check without rebuilding the WebKit
    • in case of embedded devices, one should prefer limiting JIT over disabling it as without it, the JS execution may be slow enough to trigger unexpected scenarios.
  3. Things to check if rebuilding WebKit

Ideally, while checking various things along the above steps, one should batch as many checks as possible within individual tests.

3b. Narrowing down on embedded when the problem reproduces quickly #

When the problem reproduces quickly, the limitations of embedded environment are not that relevant. In this scenario, one should prioritize getting debug symbols (RelWithDebInfo build) into the image and utilizing them by running the browser with whatever profilers are available. For the specific things to check, one should seek inspiration in the following groups:

  1. Things to check without rebuilding the WebKit.
  2. Things to check if rebuilding WebKit.

3c. Narrowing down on desktop when the problem takes a long time to reproduce #

This situation is similar to 3a and hence one should follow the things to check from the following groups:

  1. Things to check without rerunning the WebKit.
  2. Things to check without rebuilding the WebKit.
  3. Things to check if rebuilding WebKit.

However, this time, there are some extra opportunities around tooling:

  1. There should be many more tools available already in the system or available to be installed.
  2. Tools such as memory profilers that could slow down the application making it unusable on embedded, may turn out to be working well when the desktop-class processing power is available.

With the above in mind, it’s worth trying all the tools available with priority because if at least one tool works well, one can save hours of narrowing down.

3d. Narrowing down on desktop when the problem reproduces quickly #

This is technically the simplest possible scenario, so basically, all the possibilities are available. The most time-consuming activity in this case is very likely rebuilding WebKit itself — although it should still be relatively fast. In such case, just after a few quick checks with the Web Inspector, it’s recommended to get debug symbols (RelWithDebInfo build) and start with tools such as memory profilers.

Other than the above, one should go through the following groups on things to check:

  1. Things to check without rebuilding the WebKit.
  2. Things to check if rebuilding WebKit.

4. Debugging #

The WPE debugging is twofold and depends on whether the problem is within the engine (usually C/C++ code) or the web application (JavaScript code).

When problem lies in the engine #

Debugging WPE WebKit is the same as debugging any other C/C++ application on Linux (or Mac if the issue is cross-port and one prefers an Apple port to work with), and hence is outside the scope of this article. Some WebKit-specific information can be found in the WebKit Documentation article on building and debugging page and therefore is recommended as a first step.

When problem lies in web application #

When the problem lies in JavaScript code, the situation is usually fairly straightforward. The majority of bugs in this area should be reproducible across various browser engines and hence a full variety of tooling should be available. If the WebKit is preferred or if the problem reproduces only there, the tooling available is still very useful and helps debugging problems quickly. The ultimate tool in such case is the Web Inspector. On official WebKit’s web page there’s entire index of articles on Web Inspector. Among those, the most interesting read is about Timelines Tab where the most useful debugging can be done. Once the features of Timelines Tab are understood, the next important article is the memory debugging guide. It dives into the most important Timelines Tab subsections and showcases the work with heap snapshots which is a key. To supplement it, it’s very important to know the heap snapshot delta feature which is basically about button:

Web Inspector heap delta.

that allows one to inspect the delta-snapshot between 2 snapshots. It’s critical as it answers the question on what JS objects were added between the base snapshot and the later one. If some objects are piling up, it immediately shows which ones.

One important note on snapshots is that in some cases when using Web Inspector is not possible, one can generate the snapshots manually from the web engine’s C++ code by just calling GarbageCollectionController::singleton().dumpHeap(); at some appropriate moment. In this case, the dump will be written to standard output. It can be then turned into a file and imported from any Web Inspector using Import button.

As the Timelines Tab with its subsections should be able to answer on what happens, to understand why it actually happens, the last missing piece is the JS debugger within Web Inspector. It’s not very different to debuggers in other engines, but it’s worth checking a dedicated article on it just to understand the capabilities.

Appendix #

Things to check without rerunning the webkit #

Even if the WPE is running with default settings in release mode, there are plenty of useful things that can be checked while the browser is still running:

  1. Identifying which WebKit process allocates abnormally,
    • there are multiple ways to do this, but usually it’s as easy as using ps utility.
  2. Identifying how fast the process in question allocates the memory,
    • this is useful to know at least for comparison purposes, but it may hint some problems already if the numbers correlate with what web application does.
  3. Checking logs from stdout, stderr, and journal (using journalctl).
  4. Checking detailed process memory statistics.
  5. Triggering and checking the impact of memory pressure on given processes RSS,
    • in short, memory pressure triggers the cleanup of the majority of caches along with GC. Therefore, if this is able to bring memory back to normal level, then the problem is about caches, JS Heap / GC, or fragmentation.
  6. Attaching memory profilers if available,
    • even if the debug symbols are not present, this may be useful to see what data is being captured and how the web application behaves when slowed down by profiler.
  7. Attaching other tools if available,
    • even if the debug symbols are not present, various tools offer different perspectives on what the browser is doing. In some cases, such information may reveal some anomalies that may be related to the main issue.
  8. Cross-checking with other browsers,
    • if other browsers show a similar pattern of memory usage, it’s very likely the problem lies in web application itself. Otherwise, it strongly suggests a bug in the WPE.
  9. Cross-checking with other ports,
    • if any other WebKit port shows a similar pattern of memory usage, it allows one to narrow down the area in the code a bit based on what port it is:
      • if the same behavior is visible in any of Apple ports, the problem is most likely related to cross-platform code,
      • if the same behavior is visible only in GTK port, then the problem is most likely related to GLib-related part, coordinated graphics part, GStreamer-related part, or others that are shared.

Things to check without rebuilding the webkit #

  1. Tweaking and checking the logs from WPE,
    • while generic logs may hint some unusual behavior, more specific ones such as GC logs (JSC_logGC=1) may be used to check how the individual JS heap sizes evolve over time and how GC behaves. If it’s JavaScript leaking the memory, this log will quickly provide the evidence.
  2. Enabling Remote Web Inspector and checking:
    • both breakdown and trend of memory usage in the memory timeline after doing a bit of recording,
    • the effects of takeHeapSnapshot() invoked from JS console:
      • as this function usually triggers GC internally, it may be used to check how much RSS memory is reclaimed by GC in isolation (followed up by scavenger),
      • as this function takes a JS heap snapshot, it then can be used to explore manually if its contents point towards something interesting.
  3. Disabling JIT and checking the memory usage,
    • if the memory usage is stable with JIT disabled, one should proceed to the step below.
  4. Limiting JIT and checking the memory usage,
    • there are at least a few places (levels) where JIT compilation engine allocates memory. If limiting doesn’t resolve the issue completely, it’s likely the engine itself leaks some memory around temporary helper-heaps such as AssemblerData etc.
  5. Experimenting with environment variables and runtime preferences,
    • some environment variables and runtime preferences change the behavior of the web engine significantly. If changing one of them makes the problem go away, it usually helps to narrow down the problematic area quickly.
  6. Running WPE with system malloc (environment variable Malloc=1) and checking the memory usage,
    • when one suspects bmalloc/libpas issues with fragmentation or scavenger, it’s worth running a browser with system malloc to compare the memory evolution over time against the bmalloc/libpas.
  7. Limiting device memory and checking the memory usage,
    • if triggering memory pressure is not possible, an alternative solution is to limit the device memory so that the browser is under constant memory pressure.
  8. Running WPE with sysprof and checking:
    • stack traces — to see what parts of engine are particularly active as it may hint some problematic area,
    • WebKit marks — to see what the engine is doing as well as quantitative data in marks such as EventLoopRun etc. as in those cases the numeric value trends may reveal resource pile up.

Things to check if rebuilding webkit #

  1. Building WPE in release mode with debug symbols and re-trying memory profilers or other tools if the debug symbols were not present before,
    • if some desired tools such as heaptrack, valgrind, perf, or strace were not available before, it’s the right moment to get/build them as well,
    • once the debug symbols are in, one should try:
  2. Building and running with Google perftools,
    • as WPE allows switching to system malloc as an allocator, it’s possible to use custom malloc implementation with instrumentation such as gperftools. For that, the recommended read is this article from fellow Igalian, Pablo Saavedra.
  3. Building and running with sanitizers,
    • if the problem is about low-level leak, address/leak sanitizer should be able to help pointing out the problematic area.
  4. Building and running with memory sampler,
    • the data produced by memory sampler is roughly the same as inspector’s memory timeline, however, it’s much more convenient as it doesn’t need web inspector at all.
  5. Building and running with node statistics,
    • when memory growth seems to be related to DOM mutations, it’s worth enabling and reporting node statistics periodically — in some cases, it may directly suggest what the problem is about.
  6. Building and running with malloc heap breakdown,
    • when all other means fail, a very good last-resort approach for investigating memory usage statistics via a debug-only WebKit feature called Malloc Heap Breakdown. The details can be found in the dedicated article about it.
  7. Building and running with libpas statistics,
    • On very rare occasions such as memory fragmentation or allocation issues, it may be worth checking the libpas (low-level memory allocation and management library) statistics as WPE uses it by default on the vast majority of platforms.

Individual instructions #

Checking detailed process memory statistics #

As WPE WebKit uses multi-process architecture, there are multiple processes that can be checked, although the most interesting one is usually the Web Content Process. Once the PID of the given process is determined (e.g., using ps utility) the usual steps to check detailed memory statistics are:

  • cat /proc/<PID>/status or cat /proc/<PID>/statm for very basic statistics,
  • pmap -X <PID> - for detailed statistics (if available),
  • cat /proc/<PID>/smaps_rollup and cat /proc/<PID>/smaps for detailed statistics (requires CONFIG_PROC_PAGE_MONITOR kernel configuration option).

Triggering memory pressure from OS #

WPE uses a so-called Memory Pressure Monitor to observe the memory usage in the system and to react if there’s not much memory left. The default thresholds are specified in MemoryPressureMonitor.cpp and usually are 90% for non-critical and 95% for critical response. Depending on the response, WPE schedules GC and clears internal caches immediately.

As the above is usually on by default, one can leverage it to trigger GC (along with cache cleanups) by filling up the available memory in the OS to 95+%. There are many ways to allocate memory, yet the simplest is using stress:

  • e.g. stress --vm 1 --vm-bytes 1024M --vm-keep to allocate 1024 MB.

Attaching memory profilers #

When attaching any memory profiler, unless one wants to profile only native allocations (Skia, GStreamer, ICU, etc.), the key is to use Malloc=1 environment variable on WPE startup so that bmalloc uses system malloc instead of libpas. Then the commands are as follows:

  • to attach heaptrack:
    • heaptrack -p <PID> so e.g. heaptrack -p $(pgrep WPEWebProcess) (see this article for details),
  • to run with valgrind’s massif (as attaching to running process is not possible):
    • valgrind --tool=massif --trace-children=yes <WPE-BROWSER-COMMAND> (see this article for details).

Attaching other tools #

If memory profilers are unusable or unavailable, it’s worth checking if other tools are present and experimenting a bit with them if so. In some cases, tools other than memory profilers may give some hints on further investigation or reveal a suspicious pattern within application execution. Some ideas for experiments with various tools are listed below:

  • strace:
    • strace -c -p $(pgrep WPEWebProcess)strace called with -c gives a nice summary of system calls executed by the traced application. It can be useful to check the overall syscall usage pattern to see if there are any anomalies.
    • strace -p $(pgrep WPEWebProcess) -e trace=mmap,munmap,mremap,madvise -ttstrace focused on mmap()-related system calls may be useful to debug libpas.
  • perf:
    • perf record -F 999 -ag -p $(pgrep WPEWebProcess) -- sleep 60 — regular recording with perf can be very useful, especially if symbols are available. With that, one can generate flamegraphs and investigate what’s going on in the browser. While it’s not about profiling memory, it may be helpful to narrow down at least a bit.
    • perf record -F 999 -e syscalls:sys_enter_mmap,syscalls:sys_enter_munmap,syscalls:sys_enter_mremap:sys_enter_madvise -ag -p $(pgrep WPEWebProcess) -- sleep 60perf focused on mmap()-related system calls is much more superior than e.g. strace as it also records stack traces. Therefore, if debug symbols are present, and if the memory growth is very rapid, it’s very likely the libpas mmap() stacktraces will lead to the growth origin statistically.
    • perf trace -e mmap,munmap,mremap,madvise -p $(pgrep WPEWebProcess) — this is very much similar to strace focused on mmap()-related system calls as it shows a live preview of what’s happening.
  • sysprof:
    • sysprof-cli -f — while running system-wide sysprof won’t make WPE push marks into it, the profiling trace may still be useful to some degree, especially if debug symbols are available.

Disabling JIT #

This can be done using an environment variable:

  • JSC_useJIT=false.

Limiting JIT #

Limiting JIT can be achieved via environment variables:

  • JSC_jitMemoryReservationSize=<BYTES> to limit JIT memory usage (the limit is semi-strict as some JIT compilation engine buffers are limited by this value indirectly),
  • JSC_useFTLJIT=false to disable FTL tier,
  • JSC_useDFGJIT=false to disable DFG and FTL tiers,
  • JSC_useBaselineJIT=false to disable Baseline, DFG, and FTL tiers.

Tweaking WPE logs #

WPE is a fairly complex piece of software and hence it offers various logging capabilities related to WebKit itself, as well as to related libraries. The vast majority of logging can be controlled via environment variables:

  • WEBKIT_DEBUG=all to enable all logging channels,
  • WEBKIT_DEBUG=Layout,Media=debug,Events=debug to enable selected logging channels,
  • JSC_logGC=2 to enable JS garbage collector logs,
  • GST_DEBUG=4 to enable gstreamer (multimedia-related) logs (see the documentation),
  • G_MESSAGES_DEBUG=all to enable GLib-level logs.

If MiniBrowser (or similar browser) is used, one can also set a runtime preference to enable JS console.log(...) logging to the standard output:

  • --features=+LogsPageMessagesToSystemConsole.

Enabling remote web inspector #

Enabling WPE’s remote web inspector is a twofold process:

  1. The first step is to run WPE with the proper environment variable so that it starts listening on IP:PORT using tcp socket:
  • WEBKIT_INSPECTOR_SERVER=IP:PORT is the most reasonable option as it uses inspector:// protocol that can be utilized by WebKit-native browsers such as GNOME Web (Epiphany) or Safari,
  • WEBKIT_INSPECTOR_HTTP_SERVER=IP:PORT is a less preferable alternative that uses HTTP protocol and technically works from any browser. However, no seamless integration is guaranteed in this case.
  1. The second step is to connect from a regular web browser to the WPE:
  • using inspector://IP:PORT/ if native inspector server was started,
  • using http://IP:PORT/ if HTTP inspector server was started,
  • forwarding the ports using socat tcp-l:PORT,fork,reuseaddr tcp:IP:PORT if the WPE is running in unreachable network.

Experimenting with environment variables and runtime preferences #

The most outstanding environment variables changing the behavior of WPE are the following:

  • WPE_DISPLAY — assuming the new WPE platform API is used, this environment variable allows one to switch the pre-defined platform implementation thus changing a platform-facing part of graphics pipeline. The valid options are:
    • WPE_DISPLAY=wpe-display-headless — for headless implementation,
    • WPE_DISPLAY=wpe-display-drm — for direct rendering manager integration,
    • WPE_DISPLAY=wpe-display-wayland — for wayland integration,
  • WEBKIT_SKIA_ENABLE_CPU_RENDERING — when set to 1, rendering the DOM contents to the layers is done using Skia CPU backend instead of GPU one.

The most outstanding runtime preferences changing the behavior of WPE are the following:

  • CanvasUsesAcceleratedDrawing — when disabled, 2D canvas will use Skia CPU backend instead of GPU one,
  • LayerBasedSVGEngine — when enabled, WPE uses a different SVG engine internally,
  • AcceleratedCompositing — when disabled, WPE uses experimental, non-composited mode that bypasses all of the compositor work.

Limiting device memory #

On the majority of embedded devices, the device memory can be limited by:

  1. Interrupting the boot sequence (usually holding some key such as z upon booting),
  2. Invoking the command to change the limit and booting, e.g.:
    > global linux.bootargs.console="console=ttymxc0,115200n8 mem=2G"
    > boot
    

Running WPE with sysprof #

Regardless of whether it’s done on desktop (using wkdev-sdk) or on embedded device, the command is always as simple as:

  • sysprof-cli -f -- <WPE-INVOCATION>.

See the documentation entry for more details.

Building WPE in release mode with debug symbols #

On desktop, the simplest way to get release with debug symbols is to utilize CMake’s build type by using -DCMAKE_BUILD_TYPE=RelWithDebInfo within WPE build command, so:

  • ./Tools/Scripts/build-webkit --wpe --release --cmakeargs="-DCMAKE_BUILD_TYPE=RelWithDebInfo".

On embedded, when Yocto is used, one should tweak settings such as:

IMAGE_GEN_DEBUGFS = "1"                                                         
IMAGE_FSTYPES_DEBUGFS = "tar.bz2"
DEBUG_BUILD = "1"
EXTRA_IMAGE_FEATURES_append = " dbg-pkgs"

and potentially INHIBIT_PACKAGE_STRIP to control whether debug symbols should be kept with the binary or not. This may be necessary occasionally as some tools have problems reading .gnu_debuglink and therefore work only with symbols included in the binaries.

Building and running with sanitizers #

WebKit works pretty well with all kinds of sanitizers. To build with any of them a CMake-level helper called ENABLE_SANITIZERS can be used by specifying -DENABLE_SANITIZERS=address, -DENABLE_SANITIZERS=leak etc. With that, the command for building e.g. on desktop could look like:

  • ./Tools/Scripts/build-webkit --wpe --debug --cmakeargs=-DENABLE_SANITIZERS=address.

For more details, one can refer to this article from fellow Igalian, Fujii.

Building and running with memory sampler #

When WPE is built with -DENABLE_MEMORY_SAMPLER=ON, the simple memory sampler can be started along with the browser using environment variable:

  • WEBKIT_SAMPLE_MEMORY=1.

With that, the memory of various WPE processes is sampled every second, and saved to the files under /tmp directory continuously.

Building and running with node statistics #

Node statistics are a debug-only feature that can be enabled by:

  • changing 0 of #define DUMP_NODE_STATISTICS 0 to 1 in Source/WebCore/dom/Element.h,
  • adding dumpStatistics() call, to e.g. Node constructor in Source/WebCore/dom/Node.cpp.

Building and running with libpas statistics #

Libpas statistics are a debug-only feature that can be enabled by changing 0 of #define PAS_ENABLE_STATS 0 to 1 in Source/bmalloc/libpas/src/libpas/pas_config.h and then running WPE with environment variable PAS_STATS_ENABLE=1.

June 02, 2026 12:00 AM

May 21, 2026

Release Notes for Safari Technology Preview 244

Surfin’ Safari

Safari Technology Preview Release 244 is now available for download for macOS Tahoe and macOS Sequoia. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 312008@main…312964@main.

Accessibility

Resolved Issues

  • Fixed VoiceOver cursor positioning for elements focused via the drawFocusIfNeeded() canvas API. (312024@main) (146323788)
  • Fixed an issue where interactive elements containing an <svg> named by a child <title> element did not expose an accessible name. (312953@main) (172559238)
  • Fixed VoiceOver line-by-line reading skipping content in read-only documents. (312263@main) (174349841)

Animations

New Features

  • Added the animation property to AnimationEvent and TransitionEvent interfaces. (312859@main) (176527591)

Resolved Issues

  • Fixed CSS anchor-positioned elements not updating correctly when their anchors have their transform animated. (312752@main) (175896047)

CSS

New Features

  • Added support for the normal and none values on the position-anchor CSS property. (312378@main) (172097721)
  • Added support for transform-aware anchor positioning. (312317@main) (175401339)

Resolved Issues

  • Fixed an issue where -webkit-text-fill-color incorrectly overrode text-decoration-color. (312789@main) (47010945)
  • Fixed shape-outside computing incorrect text wrapping in RTL writing modes. (312516@main) (56890238)
  • Fixed CSS zoom interacting incorrectly with font-weight, font-style, and font-variant on iPad. (312944@main) (152173269)
  • Fixed FontFace.loaded to reject when a local() font source fails to load. (312933@main) (174631384)
  • Fixed CSS trigonometric functions to correctly convert degrees to radians. (312860@main) (175575617)
  • Fixed sibling-index() and sibling-count() inside calc() functions to be correctly simplified. (312026@main) (175590806)
  • Fixed sibling-index() and sibling-count()to correctly return 0 when used in cross-tree ::part() styling. (312544@main) (175592607)
  • Fixed the CSS resize handle not working on an element when the handle overlaps a child iframe. (312099@main) (175621855)
  • Fixed flex container baseline alignment being incorrectly computed for scroll containers by clamping to the border edge. (312206@main) (175631095)
  • Fixed an issue where inline-level boxes with calc() margins or padding lost the fixed component during intrinsic width computation. (312081@main) (175669222)
  • Fixed floats with margin-start incorrectly overlapping adjacent floats. (312083@main) (175669464)
  • Fixed aspect-ratio calculations for block-level elements with size constraints. (312084@main) (175669713)
  • Fixed aspect-ratio calculations for flex items with percentage cross-size constraints. (312085@main) (175669774)
  • Fixed an issue where block-size: stretch resolved incorrectly for absolutely positioned elements with orthogonal writing modes. (312086@main) (175669844)
  • Fixed aspect-ratio calculations for flex items with definite cross-size values. (312108@main) (175690028)
  • Fixed revert-layer computing incorrectly when there is a leading empty or space substitution value. (312721@main) (175729680)
  • Fixed :has() invalidation incorrectly resetting sibling relation bits, causing style invalidation failures for first-in-sibling-chain elements. (312221@main) (175738008)
  • Fixed an issue where absolutely positioned tables with content exceeding their declared CSS width were incorrectly positioned. (312197@main) (175755871)
  • Fixed height calculations for absolutely positioned tables with percentage-sized children. (312204@main) (175762381)
  • Fixed an issue where absolutely positioned tables with explicit percentage or fixed heights did not resolve correctly against their containing block. (312280@main) (175852400)
  • Fixed absolutely positioned tables ignoring min-height and shrinking below their content height. (312313@main) (175883577)
  • Fixed absolutely positioned tables ignoring max-height constraints. (312357@main) (175932457)
  • Fixed an issue where a flex item with aspect-ratio and content-box padding computed the wrong height in a column flex container. (312441@main) (176033726)
  • Fixed an issue where flex items with explicit min-height: min-content were incorrectly treated as scrollable, zeroing out their minimum size. (312517@main) (176173688)
  • Fixed :has() invalidation performance when used inside nested :is() selectors. (312715@main) (176354723)
  • Fixed sibling-count() & sibling-index() used in @keyframes to re-resolve when siblings change. (312895@main) (176531901)

Forms

Resolved Issues

  • Fixed <datalist> suggestions appearing with a white background on white text in dark mode after typing. (312889@main) (168676757)
  • Fixed the select picker appearing at an incorrect position when the <select> element is anchor positioned. (312811@main) (175454476)
  • Fixed field-sizing: content clipping the placeholder on number inputs that have no value. (312936@main) (175883299)
  • Fixed <option> and <optgroup> elements to match the :disabled pseudo-class when inside a disabled <select>. (312890@main) (176559708)

HTML

New Features

  • Added support for tabindex, focus(), blur(), and autofocus on MathML elements per the HTML Standard. (312609@main) (176258900)

Resolved Issues

  • Fixed history.pushState() and history.replaceState() URL rewriting to match the updated specification. (312738@main) (83203469)
  • Fixed sequential focus navigation to skip elements that do not meet the specification’s focusability requirements. (312032@main) (103370883)
  • Fixed innerText to no longer emit newlines for visibility: hidden block elements. (312010@main) (175569426)
  • Fixed innerText to correctly emit blank lines around <p> elements regardless of their CSS display value. (312169@main) (175729427)
  • Fixed the speculative preload scanner to no longer incorrectly preload scripts inside SVG elements. (312327@main) (175800116)
  • Fixed innerText on tables to no longer emit spurious trailing newlines and to preserve row-exit newlines after empty rows. (312941@main) (176635985)

Images

Resolved Issues

  • Fixed naturalWidth and naturalHeight returning incorrect values for SVG images without intrinsic dimensions. (312552@main) (141196049)
  • Fixed an issue where HDR images would flicker and lose their HDR appearance when overlapping layers animate. (312702@main) (163382580)

JavaScript

Resolved Issues

  • Fixed an issue where parse errors in workers were not reported to the parent. (312701@main) (175610725)
  • Fixed a performance issue with module resolution by limiting cache population to star-resolution and indirect-resolution cases. (312416@main) (175826413)
  • Fixed a performance issue with TypedArray.prototype.lastIndexOf by adding SIMD-accelerated reverse search for numeric types. (312383@main) (175904377)
  • Fixed a performance issue where building a module namespace with many export * statements was significantly slower than necessary. (312370@main) (175949532)
  • Fixed DataView constructor to match specification-defined argument validation order and error throwing behavior. (312483@main) (176110210)
  • Fixed an issue where Array.prototype.concat could produce incorrect results when combining arrays with incompatible indexing types. (312560@main) (176219964)

MathML

New Features

  • Added support for multiple-character operators in MathML. (312867@main) (170907545)

Resolved Issues

  • Fixed padding and border rendering on <msqrt> and <mroot> elements and corrected token sizing for mathvariant. (312902@main) (173081436)
  • Fixed absolute positioning of elements inside MathML by ensuring logical height is updated. (312905@main) (173088146)
  • Fixed tabIndex values not being set correctly for MathML elements. (312681@main) (174734133)

Media

New Features

  • Added support for synchronized video playback on displays using genlock on macOS. (311815@main, 312166@main) (175197574)

Resolved Issues

  • Fixed video playback failing when the declared MIME type in a <source> element does not match the actual content type served by the server. (312399@main) (166181001)
  • Fixed ImageCapture to correctly queue takePhoto() and applyConstraints() requests to avoid concurrent capture session reconfiguration. (312196@main) (174950018)
  • Fixed ::cue() selectors to correctly match the WebVTT root object in addition to child nodes. (312292@main) (175550173)
  • Fixed Media Source Extensions readyState not being updated immediately when playback stalls due to a gap in buffered data. (312694@main) (176330683)

Networking

Resolved Issues

  • Fixed XMLHttpRequest incorrectly dropping the request body during redirects. (312092@main) (98459882)
  • Fixed an issue where partitioned cookies could not be deleted via WKHTTPCookieStore. (312478@main) (174557252)

Rendering

New Features

  • Added support for anchor-valid and anchor-visible as aliases of anchors-valid and anchors-visible in position-visibility. (312080@main) (174438361)
  • Added support for the Dutch IJ digraph in text-transform: capitalize and ::first-letter, correctly titlecasing “ij” to “IJ” at word starts when the content language is Dutch. (312335@main) (175912959)

Resolved Issues

  • Fixed an issue where a block formatting context with margin-start could overlap an adjacent float. (312082@main) (93187697)
  • Fixed boxes in the top layer to use the initial containing block as their static-position rectangle. (312908@main) (155495104)
  • Fixed an issue where form controls with height: 100% in auto-height containers incorrectly resolved to zero height. (312526@main) (161699543)
  • Fixed text being incorrectly truncated in RTL containers when combined with text-overflow: ellipsis and an inline-block pseudo-element. (312799@main) (168875614)
  • Fixed an issue where height: 100% was incorrectly calculated for replaced elements like images serving as grid items nested inside a flexbox. (312281@main) (169431440)
  • Fixed an issue where if a document in an iframe uses @prefers-color-scheme, it does not follow the color-scheme set by grandparents of the iframe. (312212@main) (172229372)
  • Fixed an issue where list markers rendered on the wrong line when list items started with empty inline elements. (312356@main) (172762578)
  • Fixed an issue where a child element with filter: blur() ignored border-radius overflow clipping from its parent. (312531@main) (175519148)
  • Fixed incorrect event type mappings where mousemove and mouseup event listener region types were reversed. (312142@main) (175651369)
  • Fixed an issue where containers with block-in-inline content did not expand when max-height was removed. (312608@main) (175799547)
  • Fixed always-on scrollbar thumbs not rendering on the root element of nested documents with display: flex. (312300@main) (175866046)
  • Fixed scrollbar-gutter placement on the root element in RTL layouts. (312360@main) (175939512)
  • Fixed an issue where numbered list item markers rendered with more spacing between the marker and list content than in other browsers. (312515@main) (176027025)
  • Fixed an issue where block-level boxes nested within inline elements were not properly aligned when using align-content: center. (312514@main) (176173122)
  • Fixed a regression where height: stretch on children of flex items incorrectly resolved to viewport height instead of behaving as auto when the flex container lacked an explicit height. (312916@main) (176288044)
  • Fixed intrinsic sizing for non-replaced elements with percentage dimensions. (312828@main) (176493856)

SVG

Resolved Issues

  • Fixed SMIL repeat(n) event conditions not triggering animations. (312346@main) (173599629)
  • Fixed glyph-orientation-vertical: auto to use UTR#50 Vertical Orientation properties for correct character orientation in vertical text. (312008@main) (175064567)
  • Fixed the SMIL clock value parser to accept hours greater than 99 and reject malformed seconds values. (312350@main) (175593583)
  • Fixed stroke-dasharray interpolation to use least common multiple for list length matching and corrected composition behavior. (312055@main) (175598175)

Security

Resolved Issues

  • Fixed Content-Security-Policy object-src with an empty or invalid source list to block empty <object> and <embed> elements consistently with object-src 'none'. (312899@main) (171298717)

Storage

Resolved Issues

  • Fixed an issue where IndexedDB could incorrectly return a version 0 database after an abort during the initial onupgradeneeded event. (312535@main) (176195526)
  • Fixed FileSystemHandle serialization and deserialization when no storage is available. (312616@main) (176267344)

Web API

New Features

  • Added support for creating dedicated workers inside shared workers per the HTML Standard. (312503@main) (118945089)

Resolved Issues

  • Fixed the parent window’s history.state being set to null when history.pushState is called from a child iframe. (312475@main) (50019069)
  • Fixed clicking on a scrollbar of an overflow container blurring the current activeElement. (312924@main) (92367314)
  • Fixed an issue where an Event object’s target property could lose its JavaScript wrapper due to premature garbage collection. (312017@main) (175439759)
  • Fixed FileSystemDirectoryHandle.resolve() to return the correct path array for child entries. (312061@main) (175645387)
  • Fixed PerformanceNavigationTiming.domInteractive and domContentLoadedEventEnd incorrectly returning 0 instead of the correct timestamps. (312500@main) (175739835)
  • Fixed FileSystemDirectoryHandle.removeEntry() to correctly remove entries. (312193@main) (175745157)
  • Fixed CryptoKey to correctly remain associated with its secure context. (312502@main) (176157712)
  • Fixed: Improved cross-origin isolation enforcement for workers. (312518@main) (176175488)
  • Fixed SharedArrayBuffer cloning and agent cluster ID assignment. (312800@main) (176465817)

Web Extensions

New Features

  • Added support for propagating user gestures through sendMessage(), connect(), postMessage(), and executeScript() APIs, enabling extensions to perform gesture-requiring actions like media playback. (312463@main) (175797617)

Resolved Issues

  • Fixed an issue where web extension service worker registration database files accumulated on each Safari launch, causing performance degradation. (312231@main) (175484888)

Web Inspector

Resolved Issues

  • Fixed an issue where the input field for recording canvas frames in the Graphics tab was sometimes too small to type in and only allowed typing one character at a time. (312433@main) (157787230)
  • Fixed Timeline recordings showing unrelated events incorrectly nested inside longer events. (312649@main) (176309164)

WebAssembly

Resolved Issues

  • Fixed a regression where RegisterSet::normalizeWidths() lost vector-width information, causing v128 argument corruption in WebAssembly SIMD thunks. (312610@main) (176035764)

WebGL

Resolved Issues

  • Fixed compressedTexImage not validating whether the compressed texture format extension has been enabled. (312180@main) (175652171)
  • Fixed some texImage functions reporting errors with incorrect function names. (312156@main) (175652807)
  • Fixed some WebGL context state properties not being correctly reset on context loss. (312605@main) (176190808)

WebGPU

Resolved Issues

  • Fixed GPUDevice.onuncapturederror event handler attribute not working. (312043@main) (149577124)
  • Fixed: Restored maxStorageBuffersInFragmentStage and related WebGPU limits. (312387@main) (160800947)

May 21, 2026 11:00 PM

May 18, 2026

Igalia WebKit Team: WebKit Igalia Periodical #65

Igalia WebKit

Update on what happened in WebKit in the week from May 11 to May 18.

For this week we have quite a collection of news! Ranging a variety of improvements to dialog.requestClose(), rendering fixes, the new Skia-based compositor enabled by default, and proper versioning and improvements to the WebKit Container SDK, there's news for everyone.

Cross-Port 🐱

Update the closeWatcher.requestClose() function to no longer require user activation, aligning with the spec.

Implement actually moving the node in the DOM when moveBefore() is called.

Fix handling of nested calls to dialog.requestClose().

Add missing preliminary checks to dialog.requestClose().

Graphics 🖼️

Fixed an issue where background images were unexpectedly stretched, primarily affecting the reCAPTCHA checkmark image.

The new compositor using Skia API instead of TextureMapper is now enabled by default.

Infrastructure 🏗️

Added opt-in auto-enter for the WebKit Container SDK - the GTK/WPE wrapper scripts (build-webkit, run-webkit-tests, run-api-tests, etc.) now relaunch themselves inside a pinned wkdev-build podman container when WEBKIT_CONTAINER_SDK_ENABLE_AUTOENTER=1 is set. A new .wkdev-sdk-version file at the repo root pins the SDK image, so the image can be bumped in a PR and validated through EWS. Without the flag, wrappers run on the host exactly as before.

Introduced a proper version scheme for the wkdev-sdk image provided by the WebKit Container SDK so consumers can pin to a known revision. The :latest tag, the WKDEV_SDK_TAG/--tag override and the tag/* branch mechanism are replaced by a single machine-checkable format <major>.<minor>-v<count>-<gitsha> (e.g. 2.53-v1-916f9ef), where <major>.<minor> tracks the WebKitGTK/WPE release cycle, v<count> is the per-cycle SDK build counter, and <gitsha> traces the image back to its source commit. wkdev-create gains a --version switch (full or bare <major>.<minor>). wkdev-update supports updating from latest tag to the new versioning scheme, just run it on your host to update to the latest SDK.

Switched the wkdev-build container from a persistent container to ephemeral podman run --rm --init per invocation. This removes the manual podman rm step necessary whenever container creation arguments changed (which the tooling was not handling by itself), the first-run recursive-chown cost, and the podman start step after host reboots.

That’s all for this week!

By Igalia WebKit Team at May 18, 2026 07:39 PM

May 11, 2026

Igalia WebKit Team: WebKit Igalia Periodical #64

Igalia WebKit

Update on what happened in WebKit in the week from May 4 to May 11.

This week we have a bag of exciting updates, such as fixes to crashes, better YouTube playback, a handful of advancements to WebXR, and the development releases of WebKitGTK and WPE WebKit 2.53.2.

Cross-Port 🐱

If the filesystem runs out of space while the NetworkProcess is writing into its network cache, the process will crash with SIGBUS. This would surface to users as the "Internal error fired from WebLoaderStrategy.cpp(559) : internallyFailedLoadTimerFired" error, and would be handled by re-spawning another NetworkProcess that would similarly fail.

This was addressed by using fallocate, if available, to reserve the required size. If fallocate fails to reserve, the NetworkProcess will skip caching, avoiding the crash. If fallocate is not available, the existing behaviour is preserved.

Networking 📶

Networking support, including the libsoup HTTP library.

libsoup now supports the zstd compression encoding.

Multimedia 🎥

GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.

getUserMedia() and getDisplayMedia() support should work better thanks to a couple PipeWire related fixes.

Playback of some YouTube videos (usually at low framerate) has been fixed. Eventually a better solution will involve supporting edit lists in the GStreamer MSE backend.

Graphics 🖼️

A crash when accessing the diagnostics webkit://gpu page was fixed, making sure we handle the case where libGL.so.1 or libOpenGL.so.0 are missing.

Fixed missing glyph before ZWJ/ZWNJ if no font is found for the cluster.

The WebXR implementation based on OpenXR has gained support for quad, equirect and cylinder layers.

Releases 📦️

The second unstable releases for the current development cycle have been published: WebKitGTK 2.53.2 and WPE WebKit 2.53.2. Development releases are intended is to gather early feedback on upcoming changes, and as such issue reports are welcome in Bugzilla.

That’s all for this week!

By Igalia WebKit Team at May 11, 2026 08:04 PM

WebKit Features for Safari 26.5

Surfin’ Safari

Safari 26.5 is here, delivering the :open pseudo-class, the element-scoped keyword for random(), color-interpolation for SVG gradients, the ToggleEvent.source property for popovers, and the Origin API.

Alongside new features, this release continues our ongoing efforts to greatly improve the quality of WebKit. There are 63 bug fixes in total — making this the biggest May release of WebKit yet. The improvements span SVG, WebRTC, networking, editing, and more. Scroll-driven animations and Anchor Positioning both get multiple fixes. Rendering at different zoom levels works better. And work continues improving the handling layout whenever a block-level element lives inside an inline element.

CSS

The :open pseudo-class

The new :open pseudo-class in CSS provides a clean way to style the open state of elements like <details>, <dialog>, <select>, and <input>.

Previously, you might have used the [open] attribute selector for <details> and <dialog>. It works on those elements, but doesn’t work on <select> or <input>. Plus, it’s an attribute selector doing the job better handled by a pseudo-class. Now :open provides a single, consistent pattern that works across all of these element types.

For <dialog>, it now matches when the dialog is showing — whether opened with showModal() or show(). And for <input>, it applies when an associated picker is displayed, like a date or color picker.

For <select>, :open matches when the drop-down is expanded.

select:open {
  border: 1px solid skyblue;
}

This is a practical improvement to everyday CSS. The progressive enhancement is straightforward — browsers that don’t yet support :open simply won’t apply those rules, and the underlying elements still function normally.

Improvements to CSS random()

We were proud to be the first browser to ship the new CSS random() function last December, in Safari 26.2. Since then, the CSS Working Group adjusted how named random values work. Using a named value in the syntax, like random(--size, 100px, 200px), now creates a global result, instead of something scoped to each individual element. Safari 26.5 implements these changes, including a new element-scoped keyword for when you need per-element behavior.

For example, imagine you have eight instances of <div class="box"> and apply the following CSS.

.box {
  width: random(100px, 200px); 
  height: random(100px, 200px);
  border: 2px solid black;
}

You get eight completely differently sized rectangles. This is because each time the random function is used, it generates a brand new number, in this case between 100px and 200px. This is how random() has worked since Safari 26.2.

If, instead, you want all eight boxes to be the same size with the same randomly generated height and width, you can write:

.box {
  width: random(--w, 100px, 200px); 
  height: random(--h, 100px, 200px);
}

This chooses a random number, names it (as --w or separately --h), and reuses the named number on each box. (Before Safari 26.5, a cache name was scoped to the individual element, each single box. Now it is global.)

If you want all eight boxes to be square, you can use:

.box {
  width: random(--s, 100px, 200px); 
  height: random(--s, 100px, 200px);
}

The --s ties the two sizes together. The height and width is random, but it’s the same number. Before Safari 26.5, this would give you eight differently-sized squares. Now it will give you a single random size for all eight squares.

If you want eight differently sized squares, you can now use the element-scoped keyword to scope the name to the element, like this:

.box {
  width: random(--s element-scoped, 100px, 200px); 
  height: random(--s element-scoped, 100px, 200px);
}

Now the name applies only to a single element. You can put element-scoped before or after the name, meaning this works too: random(element-scoped --s, 100px, 200px). Lastly, the element-shared keyword has been removed from Safari 26.5, since the new default behavior covers this use case and the CSS Working Group removed it from the specification.

Improvements to Anchor Positioning

CSS anchor positioning continues to mature in this release, with several fixes addressing real-world usage patterns.

  • Fixed an issue where media queries failed to re-evaluate during viewport resizing when CSS anchor positioning and viewport units were both in use. (172864699)
  • Fixed an issue where chains of three or more anchor-positioned elements didn’t resolve correctly. (173357622)
  • Fixed an issue where anchor() fallback values did not accept unitless zero. (173554237)
  • Fixed an issue where an element with display: contents did not establish an anchor scope when using anchor-scope. (173718365)
  • Fixed an issue where fixed-position boxes anchored to children of sticky-positioned boxes did not stick correctly. (173722628)

Improvements to Hanging Punctuation

This release includes two fixes for hanging-punctuation.

  • Fixed hanging-punctuation to correctly treat U+0027 (apostrophe) and U+0022 (quotation mark) as hangable quote characters. (172668971)
  • Fixed an issue where ideographic space did not hang when using hanging-punctuation: first. (172669250)

Improvements to Scroll-Driven Animations

Scroll-driven animations are a powerful recent addition to CSS, and this release includes four fixes that improve their reliability.

  • Fixed support for the scroll animation timeline range name in scroll-driven animations. (171630023)
  • Fixed an issue where scroll-driven animations were not properly paused when animation-play-state was dynamically set to paused. (171630127)
  • Fixed an issue where view timeline animations near the 0% and 100% thresholds reported incorrect progress values. (171630157)
  • Fixed an issue where animation timelines could fail to restore correctly after navigating back to a page from the back-forward cache. (174561577)

Improvements to Block-in-Inline Layout

Work continues on the layout engine rewrite for block-in-inline contexts, with several fixes in this release.

  • Fixed an issue where content inside inline elements with block-level children and rendering layers was not displayed correctly. (171101386)
  • Fixed an issue where getClientRects() could return rects with zero width and height for spans in multi-column layouts. (171101490)
  • Fixed an issue where an empty <span> with decoration was incorrectly positioned when a sibling block margin was present inside a block-in-inline context. (171101555)
  • Fixed an issue where a <br> element was incorrectly positioned inside a block-in-inline context when a block margin was present. (171101748)
  • Fixed a layout regression where absolutely positioned elements inside block-in-inline containers were incorrectly overlapping adjacent content. (171732203)

Improvements to Grid, Flexbox, Tables, Multicolumn

  • Fixed an issue where a display: grid subgrid inside a grid-lanes container incorrectly contributed its items’ intrinsic sizes to the parent’s track sizing algorithm. (171230544)
  • Fixed an issue in collapsed border tables where the border style of a cell adjacent to a rowspan cell was incorrectly applied across the full length of the spanning cell’s border. (171634786)
  • Fixed an issue where images could appear stretched inside certain flex and grid layout configurations. (172224411)
  • Fixed a regression where content with column-count: 1 could fail to display text. (172306151)

Improvements to zoom

This release includes a focused quality pass on rendering behavior at different zoom levels.

  • Fixed an issue where grid and flex layout could cause elements to shift position at certain zoom levels. (172118478)
  • Fixed an issue where text content could get cut off inside overflow containers when the page was zoomed in. (172118721)
  • Fixed an issue where pinch-to-zoom could cause web content to jump or disappear on some websites. (172507916)
  • Fixed an issue where lh and rlh units resolved with double-zoom when line-height is a number. (173515568)
  • Fixed an issue where the rlh unit was double-zoomed when resolving with evaluation-time zoom for unzoomed properties. (173518838)
  • Fixed an issue where aspect-ratio was not honored correctly when the page was zoomed in. (174498486)

Even more improvements to CSS

  • Fixed an issue where @font-face rules with different styles could incorrectly fall back to glyphs from other faces in the same family, rather than proceeding to the next family as specified by the font matching algorithm. (172390840)
  • Fixed an issue where images inside transformed containers were not properly centered. (172475726)
  • Fixed an issue where user-installed font variants could interfere with system font matching, causing incorrect fonts to be selected. (173345107)
  • Fixed a regression where animating to an implicit value for individual transform properties failed to animate. (173717819)
  • Fixed an issue where :has(:empty) was not invalidated when the content of a child element changed from empty to non-empty. (174501418)

SVG

WebKit for Safari 26.5 adds support for the color-interpolation attribute on SVG gradients, enabling linearRGB color space interpolation.

<linearGradient color-interpolation="linearRGB">
  <stop offset="0%" stop-color="red" />
  <stop offset="100%" stop-color="blue" />
</linearGradient>

By default, SVG gradients interpolate colors in the sRGB color space. Setting color-interpolation="linearRGB" on a gradient element now produces more perceptually even color transitions, especially noticeable in gradients between saturated colors, where sRGB interpolation can produce a darker or muddier midpoint than expected.

Improvements to SVG

The work continues making significant improvements to SVG.

  • Fixed an issue where removeAttribute for width or height on an SVG root element did not reset to the initial default values. (172132798)
  • Fixed event name mapping for onbegin, onend, and onrepeat on SVGAnimationElement and added the missing onend event handler. (172581017)
  • Fixed an issue where an SVG <image> element was not repainted when its href attribute was removed. (172875166)
  • Fixed an issue where UI events such as wheel failed to fire for inner SVG elements. (173009454)
  • Fixed an issue where SVG cursors set via cursor: url() appeared blurry on high DPI displays. (173950927)

Web API

ToggleEvent.source

Safari 26.5 adds support for the source property on ToggleEvent. Now, when a popover or other toggleable element is toggled, the event includes a reference to the element that triggered the action, such as the invoker button that opened a popover. This makes it straightforward to coordinate behavior between a trigger and its target without manually tracking that relationship in your own code.

popover.addEventListener("toggle", (e) => {                                                                           
  if (e.newState === "open") {                                                                                        
    console.log("Opened by:", e.source);                                                                              
  }                                                                                                                   
}); 

Origin API

Safari 26.5 also adds support for the Origin API, which exposes origin information as a structured Origin object rather than requiring string parsing. This also enables you to perform same-site comparisons between origins without having to pull in the Public Suffix List. Origin.from(value) allows you to construct an Origin object from a string or built-in object, such as MessageEvent. When the origins from built-in objects are opaque, you can still compare them. This wasn’t possible before as you only had access to the serialized origin, which is “null” for opaque origins.

const messageOrigin = Origin.from(messageEvent);
const localOrigin = Origin.from("https://social.example");
if (messageOrigin.isSameSite(localOrigin))
    grantAccess();

Improvements to Web API

  • Fixed an issue where DecompressionStream discarded valid decompressed output when extra trailing bytes were present after the compressed stream, instead of enqueuing the output before throwing. (171020155)
  • Fixed an issue where calling preventDefault() on pointerdown events did not prevent page scrolling when only passive touch event listeners are installed. (173988278)

Additional Resolved Issues

In addition to all the fixes described above, WebKit for Safari 26.5 also includes the following improvements:

Accessibility

  • Fixed an issue where the accessibility tree could permanently be empty if built during early page load when only a scroll area and web area were present. (174244620)

Editing

  • Fixed an issue where pressing backspace on a line below an image in a contenteditable region could place the cursor in the wrong position. (171850465)
  • Fixed an issue where emoji images copied from websites and pasted into other sites appeared broken due to cross-origin resource policy blocking the SVG image sources. (172775070)
  • Fixed an issue where pasting text into an empty list item created an extra blank bullet. (173275372)

Forms

  • Fixed an issue where a readonly date <input> could still be edited via keyboard using the date picker. (171535893)
  • Fixed an issue on iOS and iPadOS where datalist suggestions were presented directly over the associated input, obscuring it. (174264299)

HTML

  • Fixed dragenter and dragleave events to include relatedTarget in the event object. (172048448)
  • Fixed an issue on iOS where the drag thumbnail could show an incorrect image after long-pressing an image with an embedded link. (172293971)

Images

  • Fixed a regression where images with srcset and sizes attributes containing calc() expressions with division by zero were not displayed. (173954748)

JavaScript

  • Fixed TypedArray.prototype.sort() failing when the comparison function accesses the .buffer property of the typed array. (172516044)

Media

  • Fixed an issue where the media controls volume button was mispositioned and overlapped with other controls in right-to-left locales. (171182590)
  • Fixed an issue where MediaCapabilities.decodingInfo() always returned false for spatialRendering. (172689752)

Networking

  • Fixed an issue where downloaded files used the file extension from the URL path instead of the HTTP Content-Type header. (173705083)
  • Fixed an issue where downloaded files were saved with incorrect or missing file extensions when the URL path extension did not match the HTTP Content-Type header. (173945210)

Scrolling

  • Fixed an issue where scroll-snap re-snapping after layout changes could cause incorrect scroll positions, resulting in the wrong content being shown. (171541221)

Storage

  • Fixed an issue where IndexedDB connections could become permanently broken until the page was reloaded. (172247569)
  • Fixed an issue where document.hasStorageAccess() could return a Promise that never resolves. (172424614)

Web Extensions

  • Fixed an issue where extensions with a trailing comma in manifest.json failed to load in Safari. (172120877)

WebGL

  • Fixed WebGL shader compilation to properly handle NaN and infinity values. (166699074)

WebRTC

  • Fixed RTCIceCandidate.toJSON() to include the usernameFragment property in its serialized output. (172689343)
  • Fixed RTCRtpSender to allow a maxFramerate value of 0. (172689374)
  • Fixed RTCRtpSynchronizationSource.timestamp to use the correct time base. (172689387)
  • Fixed an issue where remote audio and video track IDs were incorrectly derived from SDP. (172689452)
  • Fixed RTCRtpTransceiver.setCodecPreferences() to accept codecs with case-insensitive mimeType matching. (172689477)
  • Fixed an issue where the camera did not turn on automatically in Google Meet when media permissions were set to “Allow”. (174023905)

Updating to Safari 26.5

Safari 26.5 is available on iOS 26.5, iPadOS 26.5, visionOS 26.5, macOS Tahoe 26.5, plus macOS Sequoia, and macOS Sonoma. On iOS, iPadOS, and visionOS, you can update to Safari 26.5 as part of the OS update in Settings > General > Software Update. On macOS, Safari updates are delivered through System Settings > General > Software Update.

Feedback

We love hearing from you. To share your thoughts, find us online: Jen Simmons on Bluesky / Mastodon, Saron Yitbarek on BlueSky / Mastodon, and Jon Davis on Bluesky / Mastodon. You can follow WebKit on LinkedIn.

If you run into any issues, we welcome your bug report. Filing issues really does make a difference.

You can also find this information in the Safari release notes.

May 11, 2026 05:00 PM

May 07, 2026

Release Notes for Safari Technology Preview 243

Surfin’ Safari

Safari Technology Preview Release 243 is now available for download for macOS Tahoe and macOS Sequoia. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 310600@main…312007@main.

Accessibility

Resolved Issues

  • Fixed an issue where the contextmenu event was not fired for elements inside iframes when triggered by keyboard or assistive technology actions such as VoiceOver’s VO+Shift+M. (310897@main) (164128676)
  • Fixed an issue where color picker inputs could not be activated using VoiceOver’s press action. (311168@main) (172218114)
  • Fixed invalidation of aria-hidden=”true” when focus lands inside the aria-hidden subtree. (311648@main) (174449524)
  • Fixed VoiceOver support for base <select> elements, including closing the popover on selection and correcting accessibility path positioning when CSS transforms are present. (311587@main) (175058883)

Animations

Resolved Issues

  • Fixed an issue where !important declarations did not override CSS animation values when CSS transitions were also running on the same property. (310810@main) (174367827)
  • Fixed an issue where identity matrix decomposition generated invalid quaternions, resulting in incorrect transform animations. (311267@main) (174813328)

CSS

New Features

  • Added support for contain: style applying to CSS quote counters, as specified in CSS Containment Level 2. (311785@main) (84758186)
  • Added support for the insert keyword for the text-autospace property. (311503@main) (175031507)

Resolved Issues

  • Fixed flex layout to use the used flex-basis instead of the specified value for definiteness evaluation. (311579@main) (85707621)
  • Fixed an issue where element positioning was incorrect when the containing block was an anonymous block. (311275@main) (96548847)
  • Fixed an issue where box-shadow did not work on display: table-row elements. (310609@main) (96914376)
  • Fixed text-indent with calc() containing percentages to correctly treat percentage components as zero for intrinsic size contributions. (310759@main) (97025949)
  • Fixed an issue where out-of-flow content had an incorrect height when set to fit-content. (311375@main) (97492632)
  • Fixed an issue with percentage size resolution in flex items in quirks mode. (311590@main) (100183902)
  • Fixed an issue where clip-path: inset() border-radius values did not render correctly at certain element and clip-path sizes. (310643@main) (110847266)
  • Fixed -webkit-box flexbox emulation not sizing children correctly inside <fieldset> elements. (311784@main) (114094538)
  • Fixed: Improved performance on pages using :where and :is selectors. (311212@main) (114904007)
  • Fixed an issue where elements with display: table could have incorrect layout when borders were present. (311276@main) (116110440)
  • Fixed an issue where elements with border, position: absolute, and aspect-ratio: 1 were not rendered as squares. (311310@main) (126292577)
  • Fixed an issue where perspective-origin failed to resolve var() references when used as the second value, preventing animations from being applied. (310650@main) (131288246)
  • Fixed :focus-visible incorrectly matching after a programmatic focus() call triggered by clicking a button with child elements. (311768@main) (134337357)
  • Fixed an issue where the bottom margin of a last child element collapsed out of a parent with min-height. (311274@main) (134356544)
  • Fixed an issue where :has(:empty) continued to match after the targeted element’s content was dynamically changed to no longer be empty. (310932@main) (143864358)
  • Fixed an issue where floats and out-of-flow objects could be incorrectly adjacent to anonymous blocks. (311226@main) (144481961)
  • Fixed an issue where text gradually disappeared when toggling text-transform on elements with ::first-letter styling. (311201@main) (145550507)
  • Fixed an issue where height: max-content resolved to zero on absolutely positioned elements when a child had max-height: 100%. (311084@main) (147333178)
  • Fixed an issue where an inline-flex container with flex-direction: column did not update its width to match the intrinsic size of a child image when the image was not cached. (311141@main) (150260401)
  • Fixed an issue where non-replaced elements with aspect-ratio enforced the automatic minimum size even when min-width was explicitly set to 0. (311096@main) (156837730)
  • Fixed an issue where an element can’t anchor to its previous sibling. (310970@main) (162903640)
  • Fixed a regression where @scope styles did not apply to slotted elements in web components. (311333@main) (171383788)
  • Fixed an issue where dynamically inserting text before existing content did not update ::first-letter styling. (310719@main) (171649994)
  • Fixed an issue where ordered list numbers with large starting values were clipped off-screen. (311332@main) (172515216)
  • Fixed a regression where the ic length unit was incorrectly affected by page scaling. (311238@main) (173198587)
  • Fixed :placeholder-shown to correctly match input elements that have an empty placeholder attribute. (310781@main) (173604635)
  • Fixed computed value of auto insets or margins as returned by getComputedStyle() to be zero, if the element uses position-area or anchor-center. (311016@main) (173885561)
  • Fixed position-area not being able to anchor to an element positioned using anchor functions. (311601@main) (173964030)
  • Fixed CSS variable cycle detection to match the CSS Values Level 5 specification. (310610@main) (174105259)
  • Fixed url() token serialization in CSS custom properties. (310628@main) (174144616)
  • Fixed text-autospace to correctly handle supplementary Unicode characters. (310633@main) (174148315)
  • Fixed an issue where flex items with different order values caused incorrect baseline alignment. (310704@main) (174241817)
  • Fixed an issue where hovering over ::first-letter text showed a pointer cursor instead of the expected I-beam cursor. (310730@main) (174258447)
  • Fixed an issue where display: grid on a <fieldset> element added extra unnecessary space below its content. (311078@main) (174301311)
  • Fixed outline radii rendering for elements with a non-auto outline-style. (310784@main) (174328839)
  • Fixed an issue where aspect-ratio was not honored when the page was zoomed in. (310931@main) (174361289)
  • Fixed replaced elements to use the transferred size through intrinsic aspect ratio for min-content and max-content sizing. (310885@main) (174386310)
  • Fixed an issue where height: 100% on a child element altered the layout when the parent’s height was defined via aspect-ratio. (310877@main) (174448267)
  • Fixed margin collapse to be allowed when the preferred block size behaves as auto, per the CSS Sizing specification. (311011@main) (174547610)
  • Fixed an issue where document.styleSheets and shadowRoot.styleSheets incorrectly included adopted style sheets, which per the CSSOM specification should only appear in the final CSS style sheets list used for style resolution. (311074@main) (174583340)
  • Fixed the CSSOM preferred style sheet set name to be established at sheet creation time based on insertion order rather than tree order. (311077@main) (174586058)
  • Fixed highlight pseudo-elements such as ::selection and ::highlight to disallow vendor-prefixed properties, aligning with the CSS Pseudo-Elements specification. (311073@main) (174590593)
  • Fixed cycle detection and nested function call handling in CSS custom functions. (311178@main) (174609179)
  • Fixed an issue where word-break: break-all incorrectly allowed CJK close punctuation to appear at the start of a line. (311088@main) (174656971)
  • Fixed an issue where word-break: keep-all incorrectly suppressed line break opportunities at CJK punctuation characters. (311090@main) (174658701)
  • Fixed the FontFace constructor to reject with a SyntaxError instead of a NetworkError when a BufferSource fails to parse, per the CSS Font Loading specification. (311146@main) (174669738)
  • Fixed the FontFace family attribute to return the serialization of the parsed value. (311478@main) (174698351)
  • Fixed grid layout to correctly handle percentage and calc() values for the specified size suggestion. (311344@main) (174863227)
  • Fixed :has() sibling invalidation issues related to relation forwarding. (311583@main) (175006235)
  • Fixed an issue where min-width: auto was not correctly computed for flex items. (311578@main) (175157619)
  • Fixed an issue where percentage heights inside flex items did not resolve correctly in quirks mode. (311581@main) (175158571)
  • Fixed an issue where margin-trim: block-start did not apply to blocks nested inside inline boxes. (311584@main) (175162899)
  • Fixed an issue where dynamically changing display: contents on a <fieldset> legend caused incorrect rendering. (311585@main) (175163337)
  • Fixed: Improved :has() invalidation performance by including the full selector context in invalidation selectors. (311642@main) (175177078)
  • Fixed the CSS preload scanner to resolve relative @import URLs against the <base> element URL. (311744@main) (175305190)
  • Fixed -webkit-box flex distribution for children with orthogonal writing modes. (311787@main) (175323734)
  • Fixed calc(infinity) as a flex-grow factor not stretching a flex item to 100% width. (311956@main) (175431146)
  • Fixed :has() sibling invalidation failing due to an internal bitfield overflow, causing stale styles when siblings are added or removed. (311870@main) (175433733)
  • Fixed :has() invalidation for sibling combinators when elements are inserted or removed from the DOM. (311893@main) (175441568)
  • Fixed transition-property not preserving the specified case of <custom-ident> values during serialization. (311909@main) (175467206)
  • Fixed the will-change property not serializing correctly when used with non-property identifiers or identifiers in a non-standard case. (311918@main) (175482352)
  • Fixed percentage top and bottom values on relatively positioned elements not resolving when the containing block has aspect-ratio. (311942@main) (175502356)
  • Fixed: Updated the enhanced <select> element to use self-* keywords for anchor positioning. (311944@main) (175505107)
  • Fixed text-indent computation when tab stop positions are involved. (311971@main) (175529961)
  • Fixed calc() margin computations in flex layout. (311977@main) (175532405)
  • Fixed calc() margin computations for block, fieldset, and table caption layouts. (311987@main) (175548980)
  • Fixed handling of <li> value attributes in reversed ordered lists. (311996@main) (175558324)

Canvas

Resolved Issues

  • Fixed an issue where conic gradients applied to canvas arc strokes were not rendered correctly. (310865@main) (173536875)

Editing

Resolved Issues

  • Fixed an issue where characters styled with ::first-letter were not selectable. (311287@main) (5688237)
  • Fixed font size 13pt being incorrectly mapped to <font size="2"> (10pt) when using rich text editing. (311700@main) (15292320)
  • Fixed an issue where adjusting text selection with touch handles was prevented by JavaScript touch event handling on some websites. (311216@main) (151851274)
  • Fixed an issue where text selection was lost when focus transitioned from a contentEditable element to a non-editable target. (310670@main) (171221909)

Encoding

Resolved Issues

  • Fixed an issue where CJK encoding state was not reset appropriately during text decoding. (311075@main) (174649963)

Forms

Resolved Issues

  • Fixed an issue where <select> control rendering was broken in vertical writing mode. (310622@main) (174068353)
  • Fixed a performance issue where parsing <select> elements with thousands of <option> children via innerHTML caused O(n²) overhead due to repeated list recalculation. (310930@main) (174244946)
  • Fixed an issue where date and time input types without min or max attributes incorrectly matched the :in-range pseudo-class. (311279@main) (174829899)
  • Fixed an issue where cloned <input> and <textarea> elements did not preserve their user-modified state. (311346@main) (174892989)
  • Fixed <option> elements to correctly implement the HTML specification’s dirtiness concept for tracking user-modified selected state. (311746@main) (175306111)
  • Fixed the default display value for <optgroup> and <option> elements to block, matching the behavior of other browsers. (311911@main) (175473184)

HTML

New Features

  • Added support for the shadowrootslotassignment attribute on declarative shadow roots. (311295@main) (173227340)

Resolved Issues

  • Fixed parsing of javascript: URLs to align with the specification. (311381@main) (147612682)
  • Fixed the HTML preload scanner to skip preloading stylesheets that have the disabled attribute. (311776@main) (173378582)
  • Fixed document named item collection to include all <object> elements, aligning with other browser engines. (310974@main) (174537345)
  • Fixed window.open() to correctly consume user activation when creating a new browsing context, aligning with the HTML specification. (311026@main) (174587258)
  • Fixed remaining issues with <img sizes="auto"> to fully align with the specification. (311138@main) (174684058)
  • Fixed an issue where dir=auto on <slot> elements did not update when slotted content changed. (311325@main) (174871706)
  • Fixed an issue where <option> elements rendered incorrectly when the label attribute was empty. (311421@main) (174979446)
  • Fixed an issue where the preload scanner incorrectly skipped <source> elements with an empty type attribute inside <picture>. (311568@main) (175094037)
  • Fixed innerText to emit a newline for empty <option> or <optgroup> inside <select>. (311673@main) (175245381)
  • Fixed HTML floating-point number parsing to correctly handle values with a leading + sign. (311735@main) (175300431)
  • Fixed keyboard focus getting stuck when tabbing through iframes that do not contain any focusable elements. (311922@main) (175375806)

Deprecations

  • Deprecated the href attribute on all MathML elements except <a>. (311550@main) (173996661)

JavaScript

Resolved Issues

  • Fixed multiple top-level await correctness bugs with a rewrite of the ES module loader for standards compliance. (311236@main) (97370038)
  • Fixed regular expressions in Unicode mode to not count non-capturing groups and modifiers toward the number of available backreferences. (311974@main) (167746769)
  • Fixed an issue where regular expressions with non-BMP characters could skip valid match positions when alternating between patterns. (310677@main) (174200307)
  • Fixed an issue where regular expression captures were not properly cleared when backtracking out of fixed-count parenthesized groups and negative lookaheads. (310679@main) (174201284)
  • Fixed an issue where import { "*" as x } was incorrectly treated as a namespace import instead of a named import using the string "*" as a ModuleExportName. (310776@main) (174314099)
  • Fixed an issue where RegExp.prototype.exec and RegExp.prototype.test could match against a stale pattern if lastIndex has a valueOf that calls RegExp.prototype.compile. (310887@main) (174461752)
  • Fixed an issue where async functions using module-scoped variables could fail when the DFG JIT optimized scope resolution. (311044@main) (174626957)
  • Fixed an issue where Intl.Segmenter with granularity: "word" incorrectly reported isWordLike: false for numeric segments. (311507@main) (175057894)
  • Fixed Object.defineProperties to call Proxy traps in the correct order. (311520@main) (175068687)
  • Fixed an issue where Intl.Locale did not canonicalize before overriding the language. (311538@main) (175092327)
  • Fixed time zone identifiers to return primary IANA time zone IDs instead of legacy ICU identifiers. (311604@main) (175098682)
  • Fixed Intl.DateTimeFormat to preserve the original legacy timezone identifier instead of replacing it with the primary IANA ID. (311724@main) (175206605)
  • Fixed Promise.prototype.finally to throw a TypeError when @@species is not a constructor, matching the behavior of other browsers. (311725@main) (175290627)
  • Fixed the regular expression engine to reject dangling hyphens in character class syntax when using the /v flag. (311999@main) (175559808)

Media

New Features

  • Added support for setting TextTrackCue.endTime to Infinity to represent an unbounded cue duration. (311730@main) (71042767)

Resolved Issues

  • Fixed an issue on iPad where exiting fullscreen on a media document incorrectly navigated back to the previous page instead of returning to the inline view. (310718@main) (137220651)
  • Fixed an issue where the darkening overlay on inline video controls made accurate scrubbing difficult and displayed video content incorrectly on macOS. (311473@main) (161271114)
  • Fixed HTMLMediaElement.currentTime to report smoothly progressing values instead of updating only at fixed intervals. (311867@main) (170115677)
  • Fixed Encrypted Media Extensions to check support for the full content type including codecs, rather than only the MIME type. (310696@main) (173852931)
  • Fixed an issue where the volume slider in video media controls could not be dragged. (311328@main) (174179871)
  • Fixed an issue where setting HTMLMediaElement.volume had no effect when the element was connected to an AudioContext. (310958@main) (174278899)
  • Fixed a regression where videos would stop playing and lose audio after a few seconds on some websites. (311698@main) (174966899)
  • Fixed an issue where U+0000 (NULL) characters were not allowed in VTTCue text content. (311539@main) (175084171)
  • Fixed video content disappearing after switching to another tab and back. (311771@main) (175257980)
  • Fixed WebVTT cue settings line parsing failures. (311729@main) (175296476)

Networking

Resolved Issues

  • Fixed redirects to data: URLs to be blocked for subresources such as images and scripts, aligning with the Fetch specification. (311023@main) (74165956)
  • Fixed an issue where the multipart form data parser incorrectly required CRLF after the closing delimiter, causing some web applications to fail to render correctly. (311089@main) (174348783)

PDF

Known Issues

  • Fixed PDF scrollbars to adapt to dark appearance. (311985@main) (174763396)

Rendering

Resolved Issues

  • Fixed an issue where ::first-letter styles caused Range.getClientRects() and Range.getBoundingClientRect() to return incorrect dimensions. (311085@main) (71546397)
  • Fixed incorrect distributed height in table rows when a <td> element has an explicit height set. (311001@main) (78549188)
  • Fixed position: relative on table rows (<tr>) to correctly establish a containing block for absolutely positioned descendants. (310995@main) (94294819)
  • Fixed incorrect box sizing on inline elements when they have no siblings and their padding-left plus margin-left equals zero. (311080@main) (162376969)
  • Fixed an issue where space-taking scrollbars did not trigger a proper re-layout when the box size depends on content size. (311766@main) (166836126)
  • Fixed an issue where images with min-width: fit-content rendered at an incorrect width. (310994@main) (169359566)
  • Fixed an issue where overlay backgrounds would briefly dim incorrectly when de-compositing in a scrollable container. (311198@main) (171024685)
  • Fixed a regression where table content intermittently disappeared and reappeared. (311411@main) (171179878)
  • Fixed an issue with outside list markers when blockification prevents line-box parenting. (311221@main) (173417560)
  • Fixed an inverted Y-axis comparison that could cause incorrect caret positioning. (310626@main) (174144220)
  • Fixed an issue where <br> elements with line-height: 0 still created extra vertical space, failing to respect the declared line height. (310839@main) (174400946)
  • Fixed auto outlines to more closely follow the border radii of elements. (310900@main) (174466854)
  • Fixed rendering of gradients in non-sRGB color spaces. (311406@main) (174880197)
  • Fixed image-orientation being ignored for background-image, border-image, and list-style-image. (311390@main) (174894122)
  • Fixed a white-space: pre-wrap layout issue with justified text. (311385@main) (174937310)
  • Fixed an issue where minimum height was not correctly computed for flex items. (311443@main) (174999995)
  • Fixed an issue with flex-wrap and flex factor computation for wrapping flex items. (311456@main) (175012395)
  • Fixed vertical writing-mode content incorrectly wrapping when the parent has auto height. (311630@main) (175123356)
  • Fixed minimum height calculation for flex items. (311623@main) (175195518)
  • Fixed incorrect bounding box position for newline characters. (311668@main) (175243361)

SVG

Resolved Issues

  • Fixed an issue where backslash-escaped dot characters in SVG animation timing attribute ID references were not parsed correctly. (310805@main) (94260935)
  • Fixed SVG2 systemLanguage attribute to improve parsing and compliance with the specification. (311439@main) (116427520)
  • Fixed the SMIL values attribute to preserve empty values and handle trailing semicolons. (311502@main) (173594455)
  • Fixed SVG2 getStartPositionOfChar and getEndPositionOfChar to be more compliant with the specification. (311450@main) (174145885)
  • Fixed SVG intrinsic aspect ratio being incorrectly suppressed when preserveAspectRatio is set to none. (311723@main) (175173375)
  • Fixed SVG images without complete intrinsic dimensions incorrectly using ratio-based scaling for background-size. (311817@main) (175345107)

Scrolling

Resolved Issues

  • Fixed an issue where tabbing in a scroll container with scroll-padding did not scroll the focused element into view. (311512@main) (147513379)
  • Fixed rubberbanding behaving incorrectly when a site triggers a smooth scroll to the top during a rubberband. (311641@main) (170705188)
  • Fixed an issue on iOS where composited layers would briefly flash blank when window.scrollTo() was called synchronously with a DOM layout change. (310864@main) (173197381)
  • Fixed an issue where calling scrollIntoView() on a scrollable element incorrectly scrolled the element’s own contents. (310734@main) (174173683)
  • Fixed scroll anchoring interfering with rubberbanding on some websites. (311686@main) (175195943)

Spatial Web

Resolved Issues

  • Fixed an issue on visionOS where fullscreen video would sometimes jump when exiting fullscreen if the browser window was narrower than the video. (311116@main) (174454557)

UI

Resolved Issues

  • Fixed: Tab key behaves incorrectly inside a PopupMenu (174468005)

Web API

Resolved Issues

  • Fixed Web IDL bindings to correctly reject SharedArrayBuffer where [AllowShared] is not specified. (311536@main) (107786134)
  • Fixed an issue on visionOS where the gamepadconnected event did not fire unless gamepad permission had already been granted. (311465@main) (141623162)
  • Fixed an issue where CSPViolationReportBody did not include the source line number in Content Security Policy violation reports. (311040@main) (152607402)
  • Fixed an issue where history.replaceState() on a traversed history entry incorrectly changed navigation.currentEntry.key to a new UUID instead of preserving the original key. (310849@main) (173388766)
  • Fixed the Credential Management API to properly define which credential types are allowed in the same get() request. (311988@main) (173918198)
  • Fixed an issue where event.target was not set after dispatching an event in a shadow tree with no listeners. (310621@main) (174136382)
  • Fixed an issue where navigator.credentials.create() and navigator.credentials.get() discarded the AbortSignal reason and always rejected with a generic AbortError. (310782@main) (174220589)
  • Fixed a missing return in the Navigation API’s performTraversal that caused incorrect behavior when traversing to an unknown key. (311161@main) (174513305)
  • Fixed Range.extractContents() to not extract out-of-bounds nodes when the end container is removed during extraction. (310770@main) (174307275)
  • Fixed document.createEvent() to throw an exception for "MutationEvents", "MutationEvent", "PopStateEvent", and "WheelEvent", aligning with other browser engines. (310792@main) (174339775)
  • Fixed ParentNode.append() to correctly de-duplicate nodes when the same node is passed multiple times. (310807@main) (174365465)
  • Fixed an issue where MutationObserver delivered childList records in the wrong order when script ran during node insertion. (310812@main) (174368989)
  • Fixed an issue where setting a URL object’s port property to whitespace behaved incorrectly. (310919@main) (174484035)
  • Fixed Blob.slice() to correctly clamp fractional start and end parameters using round-half-to-even rounding per the File API specification, which may change how edge-case fractional values like 0.5 are rounded. (310992@main) (174555334)
  • Fixed postMessage() to validate transferable object states after serialization, aligning with the HTML specification. (310996@main) (174558047)
  • Fixed structuredClone() and window.postMessage() to correctly throw a DataCloneError when serializing a SharedArrayBuffer outside of cross-origin isolated contexts. (310998@main) (174562553)
  • Fixed an issue where calling Element.blur() on an <iframe> did not reset document.activeElement to <body>. (311452@main) (174591529)
  • Fixed document.styleSheets to be accessible on documents created by DOMParser. (311043@main) (174625774)
  • Fixed innerText getter to correctly handle trailing newlines and blank lines for <p> elements and headings. (311057@main) (174642704)
  • Fixed innerText whitespace handling at inline-block boundaries. (311163@main) (174713114)
  • Fixed XMLSerializer namespace handling to correctly serialize elements with namespace prefixes. (311184@main) (174726401)
  • Fixed the innerText getter to preserve newlines for elements with white-space: pre-line. (311185@main) (174727341)
  • Fixed innerText handling of replaced elements at block boundaries. (311269@main) (174816319)
  • Fixed EventSource to be closed when window.stop() is called. (311281@main) (174830925)
  • Fixed innerText to not fall back to textContent for elements with display: contents. (311339@main) (174883499)
  • Fixed innerText to preserve the contents of <option> elements inside <select>. (311449@main) (175006854)
  • Fixed Element.innerText to collect option text when called directly on a <select> element. (311576@main) (175156630)
  • Fixed worker scripts to always be decoded as UTF-8, as per the specification. (311761@main) (175327455)
  • Fixed an issue where ancestors of TreeWalker.currentNode could be prematurely garbage collected. (311873@main) (175442228)

Web Extensions

New Features

  • Added reporting of uncaught JavaScript exceptions and unhandled promise rejections in Web Extension scripts. (311150@main) (174354070)

Resolved Issues

  • Fixed browser.i18n.getMessage() to correctly substitute named placeholders when they appear adjacent to non-space characters. (311685@main) (169146196)
  • Fixed browser.i18n.getMessage() to correctly substitute two adjacent named placeholders. (311769@main) (175315700)

Web Inspector

New Features

  • Added Subgrid and Grid-Lanes badges to the Elements tab for easier identification of subgrid and grid-lanes layout contexts. (310936@main) (173681497)
  • Added the layout root element to Layout event details in the Timeline tab. (311847@main) (175419350)

Resolved Issues

  • Fixed editing inline style attribute values in the Elements panel resulting in truncated or malformed content. (311717@main) (149523483)
  • Fixed the Network tab filtering by resource type not working after clearing a filter that had no matches. (311947@main) (161570940)
  • Fixed an issue where CSS rules added via the “Add New Rule” button in the Styles panel were intermittently not applied or would vanish after entering a property. (310921@main) (164971557)
  • Fixed an issue by removing a setting that incorrectly blocked the WebInspector service in the sandbox. (310631@main) (173330816)
  • Fixed an issue where the debugger selected the wrong thread when multiple WebAssembly virtual machines stopped simultaneously. (310847@main) (174187067)
  • Fixed the Layers 3D view to correctly map textures to composited bounds and use proper selection highlighting instead of tinting textures. (310891@main) (174355052)
  • Fixed the Layers 3D view to re-snapshot preserved layers after a repaint instead of displaying stale textures. (310892@main) (174358757)
  • Fixed an issue where Web Inspector showed empty source information for WebAssembly modules compiled via WebAssembly.instantiateStreaming. (310852@main) (174362152)
  • Fixed Web Inspector to display meaningful names for WebAssembly modules derived from the name section and source URL instead of address-based fallback names. (310961@main) (174465437)
  • Fixed an issue where all folder tree elements were expanded after filtering for a resource in the Sources panel. (311459@main) (175009135)
  • Fixed an erroneous “There are unread messages that have been filtered” banner appearing in the Console when console.groupCollapsed() is used. (311764@main) (175279759)
  • Fixed an issue in the Storage tab where filtering by storage type did not reveal the popup with options. (311945@main) (175444192)

WebAssembly

Resolved Issues

  • Fixed incorrect IntegerOverflow exceptions thrown by i32.rem_s, i64.rem_s, i32.div_u, i64.div_u, i32.rem_u, and i64.rem_u when both operands are constants. (311898@main) (175122462)

WebGPU

New Features

  • Added support for the clip_distances builtin value in WGSL shaders. (311824@main) (129202606)

Resolved Issues

  • Fixed rendering failing when using direct GPUTexture objects instead of GPUTextureView with multisampled resolve targets in render passes. (311938@main) (175452924)

WebRTC

Resolved Issues

  • Fixed WebRTC VP9 encoders to correctly propagate frame colorspace information. (311065@main) (174008548)
  • Fixed a regression where RTCPeerConnection with iceTransportPolicy: "relay" failed to gather ICE candidates. (311432@main) (174794660)
  • Fixed RTCInboundRtpStreamStats.trackIdentifier to match MediaStreamTrack.id. (311455@main) (174938984)

May 07, 2026 10:06 PM

April 28, 2026

Igalia WebKit Team: WebKit Igalia Periodical #63

Igalia WebKit

Update on what happened in WebKit in the week from April 8 to April 28.

After a short hiatus, we return with a galore of releases, more Web Platform improvements, tricky tweaks to thread scheduling, new niceties in the Web Inspector, and new build options to take advantage of compiler optimizations.

Cross-Port 🐱

Delivered a number of changes that have strengthened WPE WebKit and WebKitGTK's behaviour around real-time thread promotion and demotion:

  • Real-time soft and hard limits are now set both when sched_setscheduler or D-Bus based paths are taken (i.e. through rtkit or the corresponding XDG portal), with the soft limit set at 80% of the hard one.
  • This means WebKit now has time to gracefully handle SIGXCPU, and will do it in an async-signal-safe manner.
  • Additionally, the NetworkProcess' Cache Storage thread is now defined as QOS::UserInitiated on Linux, which no longer maps to real-time priority. Its earlier mapping to real-time was previously reported as a NetworkProcess crash in the logs (it was in practice a kernel-delivered SIGKILL, but WebKit doesn't make any distinction while logging). After limits were adjusted, this thread was successfully demoted, and now that the mapping has changed, this is no longer promoted to real-time to begin with.

Finally, logging around portal-related failures has been updated to reduce noise.

Implemented the connectedMoveCallback() for custom elements to react to moveBefore().

Implemented the scaffolding for the moveBefore() DOM function. This is the first step towards implementing the full feature and is currently behind a runtime feature flag.

The Web Inspector now highlights the layout root element by hovering over a Layout event in the “Layout & Rendering” timeline view and reveals it in the element tree by clicking a little “go to” arrow button.

Releases 📦️

WebKitGTK 2.52.2 and WPE WebKit 2.52.2 have been released, which include a number of fixes. In particular, building for some less tested configurations should now be possible, and the WPE port includes fixes for input event handling in the Qt API bindings.

The releases were quickly followed by WebKitGTK 2.52.3 and WPE WebKit 2.52.3, with further fixes including an important patch for crashes in JavaScriptCore on architectures other than x86_64, support for the scrollbar-color CSS property, and a fix for rendering certain emoji glyphs. Additionally, the WPE port also gained a new setting to disable overlay scroll bars and use always-visible ones, fixed focus handling for touch input in the built-in Wayland platform implementation, and a build fix for the Qt one.

In addition to maintenance for the stable branch, the first unstable releases for the current development cycle are also available: WebKitGTK 2.53.1 and WPE WebKit 2.53.1. These are the first published versions that remove the option to use Cairo for 2D rendering—only Skia will be supported going forward. On the additions front, there are graphics subsystem improvements, a few API additions, and initial support in the CMake build system for builds using Profile-Guided Optimization (PGO, needs Clang for now). The goal of development releases is to gather early feedback on upcoming changes, and issue reports are welcome in Bugzilla.

Infrastructure 🏗️

PGO (Profile-Guided Optimization) builds with Clang are now supported by the CMake build system.

That’s all for this week!

By Igalia WebKit Team at April 28, 2026 08:05 AM

April 23, 2026

Release Notes for Safari Technology Preview 242

Surfin’ Safari

Safari Technology Preview Release 242 is now available for download for macOS Tahoe and macOS Sequoia. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 310187@main…310599@main.

Accessibility

Resolved Issues

  • Fixed an issue where VoiceOver read text within images that have role="presentation". (310483@main) (159304061)
  • Fixed macOS accessibility support for customizable <select> elements using appearance: base-select. (310441@main) (173696010)

CSS

New Features

  • Added support for the CSS attr() function from CSS Values Level 5. (310246@main) (173700363)
  • Added support for the oblique-only value for font-synthesis-style as defined in CSS Fonts Level 4. (310409@main) (173730766)

Resolved Issues

  • Fixed an issue where @media (prefers-color-scheme: dark) inside an iframe did not match when the iframe’s color-scheme was set to dark. (310465@main) (142072593)
  • Fixed position-try-order to interpret logical axis values using the containing block’s writing mode instead of the element’s own writing mode. (310277@main) (169501069)
  • Fixed an issue where percent-height replaced elements computed stale preferred widths in shrink-to-fit containers. (310194@main) (171184282)
  • Fixed an issue where the table cell nowrap minimum width calculation quirk was applied outside of quirks mode. (310195@main) (171410252)
  • Fixed an issue where checkbox outlines appeared misaligned. (310323@main) (172742551)
  • Fixed an issue where anchor-positioned elements anchored to children of sticky-positioned boxes did not stick correctly. (310255@main) (172884148)
  • Fixed an issue where pseudo-elements were not sorted correctly when sorting anchor elements by tree order. (310407@main) (173032203)
  • Fixed an issue where ligatures caused a non-zero layout width for text with font-size: 0. (310394@main) (173840866)
  • Fixed :in-range and :out-of-range pseudo-classes to correctly update when the readonly attribute changes. (310484@main) (173978657)
  • Fixed an issue where view-timeline-inset serialization failed to coalesce identical values. (310590@main) (174096313)

Forms

Resolved Issues

  • Fixed an issue where <select multiple> did not always fire onchange when the mouse button was released far outside the element. (310482@main) (173882861)

HTML

New Features

  • Added support for the closedby attribute on <dialog> elements. (310487@main) (173974767)

Resolved Issues

  • Fixed the HTML parser fast path to correctly process escaped attribute values longer than one character. (310209@main) (173673581)
  • Fixed the HTML parser fast path to correctly detect nested <li> elements. (310492@main) (173983892)
  • Fixed the HTML parser fast path to use the adjusted current node for MathML and SVG integration point checks. (310593@main) (174096305)

Images

Resolved Issues

  • Fixed an issue where inserting an image with a srcset attribute into a dynamically created iframe resulted in an invisible image. (310446@main) (66849050)

JavaScript

Resolved Issues

  • Fixed an issue where class instance field initializers did not have the correct evaluation context when used inside arrow functions and nested scopes. (310594@main) (173296563)
  • Fixed %ArrayIteratorPrototype%.next() to return { done: true } instead of throwing a TypeError when the source TypedArray is detached and the iterator has already completed. (310292@main) (173759106)
  • Fixed an issue where a fixed-count mixed-width character class in a regular expression did not correctly restore the index on backtrack. (310477@main) (173972458)

Media

Resolved Issues

  • Fixed an issue where scrubbing a video in full-screen mode could cause it to exit full-screen. (310415@main) (172682230)
  • Fixed an issue where HTMLMediaElement.preservesPitch = true did not work when the element was connected to an AudioContext. (310521@main) (173727365)

Networking

New Features

  • Added support for secure cookies on loopback hosts. (310542@main) (137604100)

Rendering

Resolved Issues

  • Fixed a regression where nested column flexboxes could cause content to render at the wrong size. (310253@main) (173321492)
  • Fixed a regression where nested empty inline boxes accumulated an incorrect vertical offset, causing inline elements to stack as block-level elements. (310313@main) (173723162)
  • Fixed an issue where pseudo-elements were incorrectly included in outline rect collection. (310531@main) (174033087)

SVG

New Features

  • Added support for the lang and xml:lang attribute in SVG. (310495@main) (143751056)

Resolved Issues

  • Fixed an issue where SMIL animations of href or xlink:href on SVG <image> elements had no visual effect. (310236@main) (96316808)
  • Fixed an issue where SVG gradient animations did not work unless y1 and y2 attributes were explicitly specified. (310257@main) (123457088)
  • Fixed an SVG <tspan> positioning bug with xml:space="preserve" that caused multi-line text to render incorrectly. (310347@main) (143722975)
  • Fixed an issue where URL fragments were not percent-decoded before being used for SVG references. (310443@main) (169582378)

Scrolling

Resolved Issues

  • Fixed an issue where sticky-positioned elements could flicker rapidly after scrolling. (310545@main) (173680821)
  • Fixed an issue where scroll anchoring could cause a page to scroll to the top or bottom automatically. (310574@main) (173885027)

Tables

Resolved Issues

  • Fixed an issue with a collapsed border color mismatch when the table cell has a different writing-mode. (310303@main) (173655092)

Web API

New Features

  • Added support for getAllRecords() and IDBGetAllOptions in the IndexedDB API. (310462@main) (173881825)

Resolved Issues

  • Fixed an issue where document.open() incorrectly aliased the caller’s security origin. (310543@main) (173369038)
  • Fixed an issue where Object.prototype could not be serialized by structuredClone(). (310363@main) (173728983)
  • Fixed an issue where backslashes were not handled correctly in non-special URLs. (310290@main) (173757759)
  • Fixed a URL parsing bug in the special relative or authority state. (310300@main) (173772241)
  • Fixed an issue where event listener once and passive flags were not preserved when copying listeners between elements. (310355@main) (173834642)
  • Fixed `Preserve existing listener options (such as passive defaulting) when overwriting event handler attributes. (310362@main) (173842822)

WebRTC

New Features

  • Added support for RTCRtpReceiver.jitterBufferTarget. (310285@main) (173676035)
  • Added support for video source width and height in RTC stats. (310295@main) (173677615)

Resolved Issues

  • Fixed an issue where I420 BT709 VideoFrame was encoded in an incorrect color space when encoding to VP9. (310306@main) (169425608)
  • Fixed an issue where RTCRtpSender.setParameters did not clear parameters that were unset by the web application. (310286@main) (173678165)

April 23, 2026 08:04 PM

April 08, 2026

Release Notes for Safari Technology Preview 241

Surfin’ Safari

Safari Technology Preview Release 241 is now available for download for macOS Tahoe and macOS Sequoia. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 309287@main…310186@main.

Accessibility

Resolved Issues

  • Fixed an issue where calling speechSynthesis.cancel() removed utterances queued by subsequent speechSynthesis.speak() calls. (309349@main) (46151521)
  • Fixed an issue where incorrect bounding boxes were computed for MathML table rows and cells. (309640@main) (172851295)
  • Fixed an issue where comboboxes did not forward focus to their aria-activedescendant, preventing assistive technologies from interacting with list items. (309641@main) (172931277)
  • Fixed an issue where aria-owns was not respected when computing the accessible name from element content. (310020@main) (173249317)

Animations

Resolved Issues

  • Fixed an issue where animation-fill-mode did not correctly apply viewport-based units after the viewport was resized. (310007@main) (80075191)

CSS

New Features

  • Added support for the stretch keyword in box sizing properties. (309405@main) (132539604)
  • Added stable support for CSS scroll anchoring. (310113@main) (171840378)

Resolved Issues

  • Fixed an issue where U+2028 LINE SEPARATOR was not rendered as a forced line break per the CSS specification. (309701@main) (88470339)
  • Fixed an issue where outline-offset was inflated for outline: auto on macOS. (309812@main) (94116168)
  • Fixed font-family serialization to preserve quotes around family names that match CSS-wide keywords or generic families. (309959@main) (125334960)
  • Fixed an issue where a font was downloaded despite no characters in the document falling within its unicode-range. (309360@main) (140674753)
  • Fixed an issue where a flex item containing a percentage-height image did not shrink correctly around the image. (309544@main) (156902823)
  • Fixed an issue where View Transition snapshots were incorrectly stored in sRGB, causing rendering issues with non-sRGB colors. (310012@main) (167634138)
  • Fixed a performance issue where contain: layout caused significantly slower forced layouts when all siblings created their own formatting context. (310173@main) (171545381)
  • Fixed an issue where underlines were split when a ruby base was expanded due to long ruby text. (309356@main) (171653095)
  • Fixed an issue where changing color-scheme did not repaint the background of composited iframes. (309567@main) (171658244)
  • Fixed an issue where nested children of a popover element failed to render when using position: absolute. (310019@main) (171735933)
  • Fixed an issue where color: initial resolved to the wrong color in dark appearance mode. (309430@main) (172320282)
  • Fixed an issue where an element with display: contents did not establish an anchor scope when using anchor-scope. (309946@main) (172355302)
  • Fixed a regression where media queries could fail to resolve correctly in the presence of viewport units and anchors, causing styles to not update on viewport changes. (309470@main) (172385594)
  • Fixed a regression where absolutely positioned elements with intrinsic height incorrectly resolved percentage heights, causing content to not render on some sites. (309513@main) (172513516)
  • Fixed <general-enclosed> in media queries to reject content with unmatched close brackets per the <any-value> grammar. (309497@main) (172575115)
  • Fixed an issue where a right-floated table could overlap another table. (309316@main) (172655655)
  • Fixed an issue where grid containers failed to avoid float boxes. (309317@main) (172655720)
  • Fixed an issue where checkboxes could overlap with adjacent text. (310015@main) (172741572)
  • Fixed an issue where the rlh unit was double-zoomed with evaluation-time CSS zoom. (309654@main) (172798163)
  • Fixed outline: auto to correctly respect zoom. (309926@main) (173068660)
  • Fixed :active, :focus-within, and :hover pseudo-classes to correctly account for elements in the top layer. (309755@main) (173145294)
  • Fixed the shape() function to omit default control point anchors in computed value serialization per the CSS Shapes specification. (309851@main) (173233716)
  • Fixed: Updated SVG and MathML user agent style sheets to use :focus-visible instead of :focus. (309912@main) (173321368)
  • Fixed an issue where view transition snapshots could capture stale transform values for accelerated CSS transform animations. (309982@main) (173323193)
  • Fixed an issue where lh and rlh units resolved with double-zoom when line-height was a number value. (310043@main) (173448638)
  • Fixed outline-width to be ignored when outline-style is auto, matching the specification. (310145@main) (173567890)
  • Fixed :in-range and :out-of-range pseudo-classes for time inputs with reversed ranges. (310156@main) (173589851)

Canvas

Resolved Issues

  • Fixed an issue where a 2D canvas element unnecessarily forced a compositing layer. (309599@main) (172864747)

Forms

Resolved Issues

  • Fixed an issue where keyboard commands such as paste did not work in form fields that restrict input to numbers. (309300@main) (4360235)
  • Fixed: Made the <input type="checkbox" switch> control behave more like other controls with regards to native appearance CSS properties. (310143@main) (173487610)

HTML

New Features

  • Added support for the auto keyword in the sizes attribute on <img> elements, enabling automatic size calculation based on the rendered layout width. (309476@main) (172827205)

Resolved Issues

  • Fixed an issue where an HTML map element without a name attribute did not match its associated image using the id attribute. (309490@main) (12359382)
  • Fixed an issue where replaceWith() stopped processing remaining nodes if a script in the replacement removed a sibling. (309411@main) (172753019)
  • Fixed an issue where HEIC images were incorrectly converted to JPEG when uploaded via drag-and-drop. (309872@main) (173206598)
  • Fixed an issue where setting the rel attribute on an <a> element multiple times did not clear prior link relations. (310144@main) (173567839)

Images

Resolved Issues

  • Fixed an issue where adopting a standalone img element did not update its image data. (309523@main) (172856773)

JavaScript

Resolved Issues

  • Fixed Array.prototype.concat to correctly handle arrays with indexed accessors, preventing getter reentry from bypassing Symbol.isConcatSpreadable checks. (309423@main) (172237596)
  • Fixed an issue where a greedy or non-greedy non-BMP character class in a regular expression could advance the index past the end of input. (309968@main) (172978772)
  • Fixed TypedArray [[Set]] to check the receiver before writing to the typed array. (309967@main) (173386404)

MathML

Resolved Issues

  • Fixed an issue where symmetric non-stretchy large operators were not centered around the math axis. (309778@main) (170905663)
  • Fixed an issue where minsize and maxsize defaults and percentages did not use the unstretched size as specified. (309764@main) (170908253)

Media

Resolved Issues

  • Fixed an issue where preservesPitch and playbackRate were not correctly handled on an HTMLMediaElement connected to an AudioContext via createMediaElementSource. (310098@main) (93275149)
  • Fixed an issue where video captions could be hidden behind other elements during inline playback. (309505@main) (171023402)
  • Fixed an issue where seeking in a WebM video did not work correctly while content was still loading. (309373@main) (172473039)
  • Fixed an issue where media playback could not move to the next item in a playlist when the tab was in the background. (309879@main) (172676372)
  • Fixed an issue where HDR video content appeared washed out due to colorspace information being lost during processing. (309502@main) (172721079)

Networking

Resolved Issues

  • Fixed a regression where the referrer could be missing after a process-swap navigation. (309382@main) (169006635)

Printing

Resolved Issues

  • Fixed an issue where animations were not rendered during print, causing missing content on animated pages. (309488@main) (36901701)
  • Fixed an issue where printing light text on a dark background with backgrounds disabled could result in invisible text. (309604@main) (170070133)

Rendering

Resolved Issues

  • Fixed an issue where the document background color did not align with the CSS specification, causing incorrect background colors when pinch-zooming out. (309332@main) (23607800)
  • Fixed an issue where WidthIterator consumed the first character twice, potentially causing incorrect text measurement. (309437@main) (128492167)
  • Fixed a regression where hovering over elements could leave repaint artifacts on the page. (309861@main) (169112402)
  • Fixed an issue where Find in Page scrolled to the wrong location when matching text inside elements with user-select: none. (309923@main) (170477571)
  • Fixed an issue where remote snapshotting could flip part of the page. (309441@main) (170868383)
  • Fixed an issue where an anonymous block created for list markers was not properly collapsed when block content prevented line-box parenting. (309466@main) (172686060)
  • Fixed an issue where elements flickered when scrolling. (309493@main) (172701438)
  • Fixed an issue where U+2029 PARAGRAPH SEPARATOR was not treated as a forced line break. (309706@main) (173106856)
  • Fixed an issue where tiles were missing after navigating back in history. (309966@main) (173288233)

SVG

Resolved Issues

  • Fixed an issue where SVG animation did not clear the animated CSS property when attributeName was dynamically changed. (309621@main) (97097883)
  • Fixed an issue where a CSS filter referencing an SVG filter via url(#id) was not invalidated when the filter content changed. (309495@main) (101870430)
  • Fixed an issue where invalid SVG filter effects were rendered instead of being suppressed. (309463@main) (130951885)
  • Fixed an issue where getScreenCTM() did not include CSS transforms and zoom contributions in the legacy SVG rendering path. (309354@main) (171525696)
  • Fixed an issue where an SVG <image> element was not repainted when the href attribute was removed. (309397@main) (172530834)
  • Fixed an issue where an invalid attribute type in one SVG animation group prevented all subsequent animation groups from running. (309436@main) (172593109)
  • Fixed a regression where wheel events were not dispatched to an empty <svg> root element. (310013@main) (172909441)
  • Fixed an issue where SMIL parseClockValue did not reject out-of-range minutes and seconds values per the SMIL timing specification. (310184@main) (173577212)

Deprecations

  • Removed the SVGLocatable and SVGTransformable interfaces to align with the SVG2 specification. (309396@main) (104668934)
  • Removed the viewTarget property from SVGViewSpec to align with the SVG2 specification. (309424@main) (172590438)

Storage

New Features

  • Added support for setting maxAge in the Cookie Store API via cookieStore.set(). (309738@main) (166301541)

Web API

New Features

  • Added support for fractional coordinates in PointerEvent and TouchEvent properties such as clientX/clientY, pageX/pageY, offsetX/offsetY, and screenX/screenY, while MouseEvent values remain whole. (298383@main) (9564176)

Resolved Issues

  • Fixed NavigateEvent.navigationType to return "replace" when navigating to a URL that matches the active document’s URL. (309871@main) (169999046)
  • Fixed an issue where navigation.currentEntry.key did not change in private browsing windows after calling history.pushState(). (309489@main) (171147417)
  • Fixed a performance issue where ResizeObserver callbacks became increasingly sluggish over time. (309376@main) (172718139)
  • Fixed an issue where IntersectionObserver could get sluggish over time due to O(n^2) iteration when observing many elements. (309387@main) (172727210)
  • Fixed an issue where navigation.currentEntry.id did not change in private browsing windows after calling history.replaceState(). (309560@main) (172897962)

Web Inspector

Resolved Issues

  • Fixed an issue where an active recording in the Timelines tab would stop when navigating or reloading the current page even when the setting to stop recording once the page loads was turned off. (309566@main) (169732727)
  • Fixed an issue where clicking the resource type selectors in the Network and Sources tabs did not show any options and just used the previously selected option. (310437@main) (173857179)

WebRTC

New Features

  • Added support for the RTCRtpCodec dictionary and related constructs. (309610@main) (172745579)

Resolved Issues

  • Fixed an issue where RTCDataChannel did not check the SCTP buffered amount synchronously. (309322@main) (172386678)
  • Fixed an issue where MediaStreamTrack could have incorrect settings if the source settings changed while the track was being transferred. (309458@main) (172657570)
  • Fixed an issue where a remote WebRTC track was not unmuted when the first packet was received. (309987@main) (172904930)
  • Fixed validation of RTC send encodings to better align with the specification. (310055@main) (172997814)

April 08, 2026 09:21 PM

April 07, 2026

Igalia WebKit Team: WebKit Igalia Periodical #62

Igalia WebKit

Update on what happened in WebKit in the week from March 31 to April 7.

Support for iOS dialog light dismiss, a new API to obtain page icons, WebKit nightly builds for Epiphany Canary produced by GNOME GitLab, and more conservative checks for MPEG-4 Audio object types are all part of this week's edition of the WebKit periodical.

Cross-Port 🐱

A new API to obtain page icons (a.k.a. “favicons”) has been added to the GTK port. The new functionality reuses the recently added WebKitImage class and provides access to multiple page icons at once through the added WebKitImageList type, allowing applications to better choose an icon that suits their needs. Changes to the WebKitWebView.page-icons property are guaranteed to be done once per page load, when all icon images are available to be used. This new API has been also enabled for the WPE port, and the plan is to deprecate the old page favicon functionality going forward.

Added iOS support for dialog light dismiss, part of the experimental closedby attribute implementation.

Multimedia 🎥

GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.

canPlayType() is now more conservative regarding MPEG-4 Audio object types. This primarily affects AAC extensions: In the past, as long as there was an AAC decoder installed, WebKit was accepting any codec string that started with mp4a. Now it only accepts codec strings that correspond to object types that have widespread support. This can prevent accidental playback of newer formats like xHE-AAC, which many decoders don't yet support — for example, as of writing, FFmpeg support for xHE-AAC is only very recent and still incomplete.

canPlayType() now also reports support for Dolby AC-4 in systems with a decoder capable of handling it.

The GStreamer WebRTC backend now rejects SDP including rtpmap attributes in the disallowed range of 64-95 payload types. Compliance with RFC 7587 was also improved.

Infrastructure 🏗️

The WebKitGTK nightly builds for Epiphany Canary are now handled entirely by the GNOME GitLab infrastructure, many thanks to them! The previous approach was not optimal, producing release builds without debug symbols. With the new builds, it is now easier to get crash stack traces including more information.

That’s all for this week!

By Igalia WebKit Team at April 07, 2026 05:07 PM

March 30, 2026

Igalia WebKit Team: WebKit Igalia Periodical #61

Igalia WebKit

Update on what happened in WebKit in the week from March 23 to March 30.

This week comes with a mixed bag of new features, incremental improvements, and a new release with the ever important security issue fixes. Also: more blog posts!

Cross-Port 🐱

Implemented initial support for closedby=any on dialog elements, which adds light dismiss behaviour. This is behind the ClosedbyAttributeEnabled feature flag.

Added the remaining values for the experimental closedby attribute implementation.

MiniBrowser now has a --profile-dir=DIR command line option that can be used to specify a custom directory where website data and cache can be stored, to test, for example, behavior in a clean session.

Multimedia 🎥

GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.

Video decoding limits had been enforced on HTMLMediaElement.canPlayType() so far, but they are now also enforced in MediaCapabilities queries.

Graphics 🖼️

Fixed several OpenGL state restoration bugs in BitmapTexture . These could cause a mismatch between the GL state assumed by Skia and the actual one, leading to rendering artifacts with certain GPU drivers and configurations.

The SKIA_DEBUG CMake option has been enabled for Debug builds, enabling Skia's internal assertions, debug logging, and consistency checks (e.g. bounds checking, resource key diagnostics). It remains off by default for Release and RelWithDebInfo builds, and can still be explicitly configured via -DSKIA_DEBUG=ON|OFF.

WPE WebKit 📟

WPE Platform API 🧩

New, modern platform API that supersedes usage of libwpe and WPE backends.

The new WPE_SETTING_OVERLAY_SCROLLBARS setting is now available, and disabling it will use a more traditional, always visible scrollbar style.

Releases 📦️

A new USE_GSTREAMER build option may now be used to toggle the features that require GStreamer at once. This can be used to effectively disable all multimedia support, which previously needed toggling four CMake options.

WebKitGTK 2.52.1 and WPE WebKit 2.52.1 have been released. On top of a small corrections typical of the first point releases in a new stable series, this one includes a number of fixes for security issues, and it is a recommended update. The corresponding security advisory, WSA-2026-0002 (GTK, WPE) has been published as well.

Community & Events 🤝

Simón Pena wrote a blog post showing how to create a minimal WPE launcher, which uses a Fedora Podman container with pre-built WPE WebKit libraries and a launcher with barely 10 lines of code to display a web view. This complements Kate Lee's custom HTML context menu blog post from last week.

That’s all for this week!

By Igalia WebKit Team at March 30, 2026 09:46 PM

March 20, 2026

Simón Pena: Getting started with WPE WebKit: a minimal launcher

Igalia WebKit

My colleague Kate recently demonstrated on her blog how simple it is to write a WPE Platform-based launcher, and did so by building it side-by-side with MiniBrowser, inside the WebKit tree.

This entry takes one step back, and demonstrates the same concepts assuming you are not building WPE WebKit yourself, but rather getting it from your distribution. Many of the steps below would apply if you were using a Yocto/OpenEmbedded-based image, but that can be the focus of another post.

Getting WPE WebKit

Get WPE lists a number of options to get WPE from your preferred distribution. At the moment of writing, Fedora, Debian and ArchLinux are your best choices to get a recent version of WPE:

  • 2.52 on Fedora
  • 2.50 on Debian Forky, 2.52 on Debian Sid
  • 2.50 on ArchLinux

However, since WPE Platform hasn’t officially been released, we need to use Fedora, where my colleague Philippe maintains a Copr repository with it enabled.

sudo dnf copr enable -y philn/wpewebkit
sudo dnf install wpewebkit-devel

Alternatively, you can use a container. Here is a Containerfile based on Fedora 42:

FROM fedora:42

RUN dnf install -y \
    dnf-plugins-core \
    && dnf copr enable -y philn/wpewebkit \
    && dnf install -y \
    gcc-c++ \
    cmake \
    pkg-config \
    wpewebkit-devel

WORKDIR /src

Build and run it with:

podman build -t wpe-dev .
podman run -it -e WAYLAND_DISPLAY=$WAYLAND_DISPLAY \
-e XDG_RUNTIME_DIR=/run/user/$(id -u) \
-v $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY:/run/user/$(id -u)/$WAYLAND_DISPLAY \
-v /dev/dri:/dev/dri \
wpe-dev bash

The build system

Kate’s post builds the launcher as part of the WebKit tree using WebKit’s own CMake infrastructure. For a standalone project, we need a self-contained CMakeLists.txt that finds WPE WebKit through pkg-config:

cmake_minimum_required(VERSION 3.16)
project(wpe_sample CXX)

set(CMAKE_CXX_STANDARD 17)

find_package(PkgConfig REQUIRED)

# The Wayland WPE Platform already depends on wpe-platform-2.0
pkg_check_modules(WebKitDeps REQUIRED
    IMPORTED_TARGET
    wpe-webkit-2.0
    wpe-platform-wayland-2.0
)

add_executable(wpe_sample main.cpp)

target_link_libraries(wpe_sample
    PRIVATE
        PkgConfig::WebKitDeps
)

The launcher

Here is a minimal launcher — the smallest amount of code needed to display a web page with WPE WebKit:

#include <wpe/webkit.h>

int main(int argc, const char *argv[]) {
    g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, false);
    g_autoptr(WebKitWebView) view = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
        nullptr));
    webkit_web_view_load_uri(view,
        (argc > 1) ? argv[1] : "https://wpewebkit.org");
    g_main_loop_run(loop);
    return EXIT_SUCCESS;
}

This snippet relies heavily on default behaviours: it will create a default WPE view, with default top levels, with the default display selection behaviour (Wayland), default context, settings…

Again, Kate’s post does a more realistic job at showing how the various pieces are created and connected together.

Building and running

cmake -B build
cmake --build build
./build/wpe_sample https://wpewebkit.org/

WPE WebKit minimal launcher

Display backends

WPE WebKit can render to different display backends depending on your environment, which you can select through environment variables:

# Wayland (e.g. desktop, Weston).
WPE_DISPLAY=wpe-display-wayland WAYLAND_DISPLAY=wayland-1 ./build/wpe_sample https://wpewebkit.org/

# DRM/KMS (e.g. embedded, no compositor)
WPE_DISPLAY=wpe-display-drm ./build/wpe_sample https://wpewebkit.org/

# Headless (e.g. testing, CI)
WPE_DISPLAY=wpe-display-headless ./build/wpe_sample https://wpewebkit.org/

You can take a look at wpe_display_get_default() in WPEPlatform/wpe/WPEDisplay.cpp to understand how the automatic selection takes place in the absence of an explicit WPE_DISPLAY request.

(In our example, we are only listing Wayland as a CMake dependency. If libwpewebkit was compiled without DRM or headless support, the environment variable approach would not work.)

Next steps

This is all for now. The next entry in the series will cover classic kiosk features: preventing navigation to unwanted sites, controlling whether new windows can be opened, and intercepting requests through policy decisions.

For a more complete example that includes a custom HTML context menu and JavaScript injection, see Kate’s post.

By Simón at March 20, 2026 12:00 AM

March 18, 2026

Igalia WebKit Team: WebKit Igalia Periodical #60

Igalia WebKit

Update on what happened in WebKit in the week from March 10 to March 18.

The big ticket item in this week's update are the 2.52.0 releases, which include the work from the last six-month development period, and come with a security advisory. Meanwhile, WPE-Android also gets a release, and a number of featured blog posts.

WPE WebKit 📟

Last week we added support to WPE MiniBrowser to load settings from a key file. This extended the existing --config-file=FILE feature, which previously only loaded WPEPlatform settings under the [wpe-platform] group. Now the feature uses webkit_settings_apply_from_key_file() to load properties such as user-agent or enable-developer-extras from the [websettings] group as well.

Releases 📦️

WebKitGTK 2.52.0 and WPE WebKit 2.52.0 are now available. These include the results of the effort made by the team during the last six months, including rendering improvements and performance optimizations, better security for WebRTC, a more complete WebXR implementation, and a second preview of the WPEPlatform API for the WPE port—among many other changes.

More information about the changes and improvements brought by these major releases can be found at the blog post about WebKitGTK 2.52, and the corresponding one for WPE WebKit 2.52.

Accompanying these releases there is security advisory WSA-2026-0001 (GTK, WPE), with information about solved security issues. As usual, we encourage everybody to use the most recent versions where such issues are known to be fixed.

Bug reports are always welcome at the WebKit Bugzilla.

WPE Android 0.3.3 has been released, and prebuilt packages are available at the Maven Central repository. This is a maintenance release which updates the included WPE WebKit version to 2.50.6 and libsoup to 3.6.6, both of which include security fixes.

Community & Events 🤝

Kate Lee wrote a very interesting blog post showing how to create a small application using the WPEPlatform API to demonstrate one of its newly available features: the Context Menu API. It is rendered entirely as an HTML overlay, enabling richer and more portable context menu implementations.

WebXR support for WebKitGTK and WPE has been reworked and aligned with the modern multi-process architecture, using OpenXR to enable XR device integration on Linux and Android. Sergio Villar wrote a blog post that explains all the work done in the last months around it.

That’s all for this week!

By Igalia WebKit Team at March 18, 2026 07:46 PM

WPE WebKit Blog: WPE WebKit 2.52 highlights

Igalia WebKit

The WebKit team at Igalia is happy to announce a new release series of WPE WebKit. This is a summary of the most noteworthy changes from the latest release cycle.

Graphics improvements

WPE’s graphics support has seen numerous improvements with a positive impact in rendering performance, resource usage, and better rendering. Let’s have a look at some of the most significant changes:

  • Compute the layers tile size, using a different strategy depending on whether GPU rendering is enabled. This optimizes resource usage depending on both hardware and software rendering mode.
  • WPE now uses run-loop observers to properly schedule layer flushing and composition, which results in snappier and better performing rendering and animation.
  • 2D-canvas acceleration has now improved performance, as operations are recorded for batched replay.
  • Text rendering has better performance too.
  • In non-composite mode, it’s now also possible to use damage propagation.
  • Asynchronous scrolling has also seen performance improvements.

On top of this, as usual, many rendering issues have been fixed, making this release of WPE one of the best in terms of graphics support.

Multimedia improvements

WebRTC

When using GstWebRTC, WebRTC network access has been moved to the network process. This also requires librice, and building with the CMake USE_LIBRICE option. When this is enabled, it is still possible to choose the older libnice-based implementation at runtime by setting WEBKIT_GST_DISABLE_WEBRTC_NETWORK_SANDBOX=1 in the environment.

Having WebRTC network access in the network process is a security improvement, as it reduces the surface of attack in other more sensitive processes.

Other multimedia improvements

  • Videos with BT2100-PQ colorspace are now tone-mapped to SDR, ensuring colours do not appear washed out.
  • Support for the Audio Output Devices API, which allows Web content to enumerate audio devices and decide which one to use for output. This feature is disabled by default, and may be previewed using the ExposeSpeakers, ExposeSpeakersWithoutMicrophone, and PerElementSpeakerSelection feature flags.
  • Many code improvements to the GStreamer backend that will result in a more stable multimedia experience.

WebXR

WebXR support through OpenXR has seen substantial development this cycle:

API Changes

The future of the WPE API

The traditional libwpe-based API remains in WPE for this release cycle, but we are planning to sunset it starting with the following one (2.54). This applies to Cog, which is no longer in active development and won’t have any more stable releases beyond the 0.18.x series. While both libwpe and Cog will remain available, we encourage developers to transition to the new WPEPlatform API, which will be considered stable by then.

This means that it is the perfect time to test the WPEPlatform API and provide feedback, as it’s still possible to make changes to it to better suit users’ needs.

WPEPlatform API changes

New platform APIs include:

  • wpe_display_create_toplevel(). This way it’s possible to create a toplevel using the common API which allows the inspector to work when the application is handling toplevels.
  • A new WPEDisplay::disconnected signal has been added, which allows platform implementations to notify when the native display gets “disconnected” and thus no longer usable. Applications can handle it to attempt recovery, or to know when they may free resources.
  • A new WPEView::buffers-changed signal, alongside the associated WPEViewClass.buffers_changed virtual function, have been added. These may be used to know in advance which graphics buffers will be used for rendering the content for a given web view. This feature is mainly useful for platform implementations which may need to perform additional setup in advance, before updated web view contents are provided in the buffers configured by WebKit.
  • Two new functions, wpe_clipboard_content_get_text() and wpe_clipboard_content_get_bytes(), allow applications to obtain the contents held in the clipboard.

The public API has received the following changes, which might require changes to existing platform implementations:

  • Multiple callbacks are now supported for WPEScreenSyncObserver, the API has changed from wpe_screen_sync_observer_set_callback() to a pair of wpe_screen_sync_observer_add_callback()/_remove_callback() functions. The functions to start/stop the observer are no longer available, and instead the observer will be activated automatically when there are one or more callbacks attached to it.

The WPEPlatform API can now be used on Android.

Legacy API

The legacy libwpe-based API can be disabled at build time, by toggling the ENABLE_WPE_LEGACY_API CMake option. This allows removal of uneeded code when an application is exclusively using the new WPEPlatform API.

New WebKit API

Web Standards support

As usual, this list is not exhaustive as WebKit continuously progresses in its support for new standards. Some of the highlights for this release are:

Other notes

The Flatpak-based development SDK has been removed. Developers are encouraged to use the WebKit Container SDK instead.

March 18, 2026 12:00 AM

March 17, 2026

Sergio Villar: Implementing WebXR in WebKit for WPE

Igalia WebKit

Since 2022, my main focus has been working on the Wolvic browser, still the only open source WebXR-capable browser for Android/AOSP devices (Meta, Pico, Huawei, Lenovo, Lynx, HTC…) out there. That’s an effort that continues to this day (although to a much lesser extent nowadays). In early 2025, as a consequence of all that work in XR on the web, an opportunity emerged to implement WebXR support in WebKit for the WPE port, and we decided to take it.

March 17, 2026 08:46 AM

March 16, 2026

Hironori Fujii: Async Scrolling Improvements

Igalia WebKit

WPE WebKit and WebKitGTK support async scrolling for wheel events. I landed several improvements for the upcoming 2.52 release.

  • Bug 305451 – wheel event async scrolling doesn’t start while the main thread is blocked
  • Bug 305560 – rendering glitches for unpainted tiles
  • Bug 305561 – Paint scrollbars in the scrolling thread for async scrolling

Here are videos of before and after the changes. This is the test content.

There is still room for further improvement.

  • The scrollbar hiding animation timer is still running in the main thread.
    • It can use CoordinatedPlatformLayer::setAnimations.
    • Or CoordinatedPlatformLayer::setOpacity.
  • Add the showing animation and transition animations of mouse hover states like GTK Adwaita theme
  • Support touch and gesture events async scrolling

March 16, 2026 12:00 AM

Kate Lee: Building a Custom HTML Context Menu with the New WPEPlatform API

Igalia WebKit

WPE WebKit is a WebKit port optimized for embedded devices — think set-top boxes, digital signage, kiosk displays, and in-vehicle infotainment systems. It is developed by Igalia and powers web experiences on millions of devices worldwide, from set-top boxes to smart TVs and beyond.

WPE WebKit has recently introduced a brand-new platform API called WPEPlatform, which replaces the legacy libwpe + wpebackend-fdo stack. In this post, I will walk you through building a minimal WPE browser launcher using only the new WPEPlatform API, and demonstrate one of its newly available features: the Context Menu API — rendered entirely as an HTML overlay.

Why a New API? #

The legacy stack (libwpe + wpebackend-fdo + Cog platform plugins) had several pain points: nested Wayland compositor complexity, dependency on Mesa’s now-deprecated EGL_WL_bind_wayland_display extension, rigid C function-pointer tables, and platform code scattered across three libraries.

The new WPEPlatform API replaces all of this with a single, clean GObject-based layer — providing automatic backend creation, DMA-BUF direct buffer sharing, unified window management (fullscreen, maximize, resize, title), and easy language bindings via GObject Introspection.

Timeline: The stable release of WPEPlatform is planned for September 2026. At that point, the legacy API will be officially deprecated. We strongly recommend new projects to adopt the WPEPlatform API from the start.

WPEPlatform Launcher: A Minimal Browser in ~250 Lines #

To demonstrate the new API, I built WPEPlatformLauncher — a minimal but functional WPE WebKit browser that uses only the WPEPlatform API. No legacy libwpe, no wpebackend-fdo, no Cog — just the new API.

The full source code is available at: kate-k-lee/WebKit@aed6402

How Simple Is It? #

Here is the core of the launcher — creating a WebView with the new API:

/* WPEPlatform backend is created automatically — no manual setup needed */
auto* webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
"web-context", webContext,
"network-session", networkSession,
"settings", settings,
"user-content-manager", userContentManager,
nullptr));

/* Get the WPEPlatform view — this is where the new API shines */
auto* wpeView = webkit_web_view_get_wpe_view(webView);
auto* toplevel = wpe_view_get_toplevel(wpeView);

/* Window management: fullscreen, resize, title — all built-in */
wpe_toplevel_fullscreen(toplevel);
wpe_toplevel_resize(toplevel, 1920, 1080);
wpe_toplevel_set_title(toplevel, "WPEPlatform Launcher");

/* Input events: just connect a GObject signal */
g_signal_connect(wpeView, "event", G_CALLBACK(onViewEvent), webView);

Compare this with the legacy API, which required:

  1. Manually creating a WPEToolingBackends::ViewBackend
  2. Wrapping it in a WebKitWebViewBackend with a destroy callback
  3. Creating a C++ InputClient class and registering it
  4. Having no window management (no maximize, minimize, title, etc.)

The new API handles backend creation, display detection, and input forwarding automatically.

Keyboard Shortcuts #

Handling keyboard events is straightforward with the WPEPlatform event system:

static gboolean onViewEvent(WPEView* view, WPEEvent* event, WebKitWebView* webView)
{
if (wpe_event_get_event_type(event) != WPE_EVENT_KEYBOARD_KEY_DOWN)
return FALSE;

auto modifiers = wpe_event_get_modifiers(event);
auto keyval = wpe_event_keyboard_get_keyval(event);

/* Ctrl+Q: Quit */
if ((modifiers & WPE_MODIFIER_KEYBOARD_CONTROL) && keyval == WPE_KEY_q) {
g_application_quit(g_application_get_default());
return TRUE;
}

/* F11: Toggle fullscreen via WPEToplevel */
if (keyval == WPE_KEY_F11) {
auto* toplevel = wpe_view_get_toplevel(view);
if (wpe_toplevel_get_state(toplevel) & WPE_TOPLEVEL_STATE_FULLSCREEN)
wpe_toplevel_unfullscreen(toplevel);
else
wpe_toplevel_fullscreen(toplevel);
return TRUE;
}

return FALSE;
}

HTML-Based Context Menu: Solving the “No Native UI” Challenge #

WPE WebKit is designed for embedded environments where there is no native UI toolkit — no GTK, no Qt. This means features like context menus (right-click menus) that desktop browsers take for granted need to be implemented by the application.

The approach: intercept WebKit’s context-menu signal, read the menu items, and render them as an HTML/CSS overlay injected into the page DOM.

The Architecture #

User right-clicks
  → WebKit emits "context-menu" signal
  → onContextMenu() handler:
      1. Reads menu items via webkit_context_menu_get_items()
      2. Gets position via webkit_context_menu_get_position()
      3. Builds JavaScript that creates DOM elements
      4. Injects via webkit_web_view_evaluate_javascript()
      5. Returns TRUE (suppresses default menu)

User clicks a menu item
  → JS: window.webkit.messageHandlers.contextMenuAction.postMessage(actionId)
  → C: onContextMenuAction() receives the action ID
      → Executes: webkit_web_view_go_back(), execute_editing_command("Copy"), etc.

User clicks outside the menu
  → JS: overlay click handler removes the DOM elements

Reading Context Menu Items #

The Context Menu API provides everything we need:

static gboolean onContextMenu(WebKitWebView* webView,
WebKitContextMenu* contextMenu, gpointer /* event */,
WebKitHitTestResult* hitTestResult, gpointer)
{
/* Save hit test result for link-related actions */
savedHitTestResult = WEBKIT_HIT_TEST_RESULT(g_object_ref(hitTestResult));

/* Iterate through menu items */
GList* items = webkit_context_menu_get_items(contextMenu);
for (GList* l = items; l; l = l->next) {
auto* item = WEBKIT_CONTEXT_MENU_ITEM(l->data);

if (webkit_context_menu_item_is_separator(item)) {
/* Render as a horizontal line */
continue;
}

const char* title = webkit_context_menu_item_get_title(item);
auto action = webkit_context_menu_item_get_stock_action(item);
/* Build HTML element with title and action ID */
}

/* Get position for menu placement */
gint posX = 0, posY = 0;
webkit_context_menu_get_position(contextMenu, &posX, &posY);

return TRUE; /* Suppress default menu */
}

The HTML Menu: Dark Theme for Embedded #

The context menu is rendered with a dark theme CSS, designed for embedded/kiosk displays:

#__wpe_ctx_menu {
position: fixed;
min-width: 180px;
background: #2b2b2b;
border: 1px solid #505050;
border-radius: 6px;
padding: 4px 0;
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
font-family: system-ui, sans-serif;
font-size: 13px;
color: #e0e0e0;
}

.__wpe_ctx_item:hover {
background: #0060df;
color: #ffffff;
}

Handling Actions via Script Message Handler #

Communication between the HTML menu and the C application uses WebKit’s script message handler mechanism:

/* Register message handler */
auto* ucm = webkit_user_content_manager_new();
webkit_user_content_manager_register_script_message_handler(
ucm, "contextMenuAction", nullptr);
g_signal_connect(ucm, "script-message-received::contextMenuAction",
G_CALLBACK(onContextMenuAction), nullptr);
// In the generated HTML menu item:
item.addEventListener('click', function() {
window.webkit.messageHandlers.contextMenuAction.postMessage(actionId);
});
/* Handle the action in C */
static void onContextMenuAction(WebKitUserContentManager*, JSCValue* value, gpointer)
{
int actionId = jsc_value_to_int32(value);

switch (actionId) {
case WEBKIT_CONTEXT_MENU_ACTION_RELOAD:
webkit_web_view_reload(webView);
break;
case WEBKIT_CONTEXT_MENU_ACTION_COPY:
webkit_web_view_execute_editing_command(webView, "Copy");
break;
case WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK:
webkit_web_view_load_uri(webView,
webkit_hit_test_result_get_link_uri(savedHitTestResult));
break;
/* ... more actions ... */
}
}

Demo #

Here is the WPEPlatformLauncher in action, showing the HTML context menu with various actions:

WPEPlatformLauncher context menu demo

Right-clicking shows the HTML context menu. Clicking “Reload” triggers an actual page reload.

Context menu on a link

Right-clicking a link shows link-specific actions like “Open Link” and “Copy Link Address”.

Building and Running #

I built and ran the WPEPlatformLauncher inside a container using the WebKit Container SDK, which provides a pre-configured development environment with all the dependencies needed to build WPE WebKit.

The WPEPlatformLauncher integrates into the WebKit build system:

# Build WPE WebKit with the launcher
Tools/Scripts/build-webkit --wpe --release

# Run
./WebKitBuild/WPE/Release/bin/WPEPlatformLauncher https://wpewebkit.org

# Run in fullscreen (kiosk mode)
./WebKitBuild/WPE/Release/bin/WPEPlatformLauncher --fullscreen https://your-app.com

The full source is a single main.cpp file (~600 lines including the context menu), integrated into the WebKit tree alongside MiniBrowser:

WebKit/Tools/
├── MiniBrowser/wpe/          ← Existing (supports both old + new API)
├── WPEPlatformLauncher/      ← New (WPEPlatform API only)
│   ├── main.cpp
│   └── CMakeLists.txt
└── PlatformWPE.cmake         ← Modified to add WPEPlatformLauncher

Summary #

The new WPEPlatform API makes building WPE WebKit applications significantly simpler:

  • No manual backend setup — the platform is detected and configured automatically
  • GObject-based — signals, properties, and ref counting instead of C function pointers
  • DMA-BUF direct sharing — no dependency on Mesa’s deprecated EGL extensions
  • Unified window management — fullscreen, maximize, minimize, resize, and title
  • Language binding friendly — works with Python, JavaScript, and more via GObject Introspection

For embedded browser developers building kiosk UIs, set-top box interfaces, or digital signage with WPE WebKit — now is the time to adopt the new API. The stable release is coming in September 2026, and the legacy stack (libwpe, wpebackend-fdo, Cog) will be deprecated at that point.

Resources #

March 16, 2026 12:00 AM

March 11, 2026

Hironori Fujii: Building WebKit and libsoup with AddressSanitizer (ASan)

Igalia WebKit

I built libsoup and WebKit with ASan today. It works almost out of the box. I used Clang. GCC also supports ASan, but WebKit has a problem with it. WebKit Container SDK is based on Ubuntu 20.04 LTS at the moment. It contains clang 18 by default.

Installed required packages.

sudo apt install libclang-rt-18-dev llvm-18-dev

Set env vars.

export CC=clang CXX=clang++

Passed some flags to libsoup.

--- /jhbuild/webkit-sdk-deps.modules.orig
+++ /jhbuild/webkit-sdk-deps.modules
@@ -149,7 +149,7 @@
</dependencies>
</meson>

- <meson id="libsoup" mesonargs="-Dtests=false">
+ <meson id="libsoup" mesonargs="-Dtests=false -Db_sanitize=address -Db_lundef=false">
<branch repo="github.com"
checkoutdir="libsoup"
module="GNOME/libsoup.git" tag="3.6.6"/>

Then, build and install libsoup.

jhbuild buildone -f libsoup

Then, build WebKit with ASan.

./Tools/Scripts/build-webkit --gtk --release --cmakeargs=-DENABLE_SANITIZERS=address

WebKit has a lot of memory leaks by design. Don’t detect leaks.

export ASAN_OPTIONS=detect_leaks=0

For run-webkit-tests, I had to modify a script a bit.

diff --git a/Tools/Scripts/webkitpy/port/driver.py b/Tools/Scripts/webkitpy/port/driver.py
index eb12801a455b..c9f74eeab4e2 100644
--- a/Tools/Scripts/webkitpy/port/driver.py
+++ b/Tools/Scripts/webkitpy/port/driver.py
@@ -482,7 +482,7 @@ class Driver(object):
else:
environment['DUMPRENDERTREE_TEMP'] = str(self._driver_tempdir)
environment['LOCAL_RESOURCE_ROOT'] = str(self._port.layout_tests_dir())
- environment['ASAN_OPTIONS'] = "allocator_may_return_null=1"
+ environment['ASAN_OPTIONS'] = "allocator_may_return_null=1:detect_leaks=0"
environment['__XPC_ASAN_OPTIONS'] = environment['ASAN_OPTIONS']

# Disable vnode-guard related simulated crashes for WKTR / DRT (rdar://problem/40674034).

That’s it. Enjoy.

March 11, 2026 12:00 AM

March 09, 2026

Igalia WebKit Team: WebKit Igalia Periodical #59

Igalia WebKit

Update on what happened in WebKit in the week from March 2 to March 9.

As part of this week's handful of news, WebKitGTK and WPE WebKit now have support for Gamepad's "VibationActuator" property, the video decoding limit is now configurable at runtime in addition to build time, and an interesting fix that makes WebKit render fonts like other browsers by making it blend text incorrectly (!).

Cross-Port 🐱

Using libmanette's rumble support, enabled Gamepad VibrationActuator for WebKitGTK and WPE WebKit.

With these changes, playEffect() can be used to play dual-rumble vibration effects.

Multimedia 🎥

GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.

VIDEO_DECODING_LIMIT is now configurable at runtime, in addition to build time. That will allow vendors that share a single binary build on different platforms to fine-tune their needs without a rebuild.

Graphics 🖼️

Landed a change that tweaks the text rendering done with Skia. With this change, the text looks more natural now - just like in other browsers. However, this is done by blending text incorrectly as a compromise.

Releases 📦️

One more set of release candidates for the upcoming stable branch, WebKitGTK 2.51.93 and WPE WebKit 2.51.93, have been published. For those interested in previewing the upcoming 2.52.x series this release is expected to be quite stable. Reporting issues in Bugzilla are, as usual, more than welcome.

That’s all for this week!

By Igalia WebKit Team at March 09, 2026 08:02 PM

March 02, 2026

Igalia WebKit Team: WebKit Igalia Periodical #58

Igalia WebKit

Update on what happened in WebKit in the week from February 23 to March 2.

This installment of the periodical brings news about support for Qualcomm qtivdec2 and qtivenc2 on GStreamer, GPU texture atlas creation and replay substitution, enhancement of the scroll gesture in WPE, and two new releases: WebKitGTK 2.51.92 and WPE WebKit 2.51.92.

Cross-Port 🐱

Multimedia 🎥

GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.

Work on adding support for the Qualcomm GStreamer qtivdec2 and qtivenc2 elements is on-going

Graphics 🖼️

Implemented GPU texture atlas creation and replay substitution in the Skia painting engine on GTK/WPE. After recording, raster images are packed into GPU atlases via BitmapTexture, with two upload paths: an optimized DMA-buf path that memory-maps GPU buffers and dispatches uploading to a dedicated worker thread, and a synchronous GL fallback using BitmapTexture::updateContents(). Atlas uploads are synchronized across workers using a countdown-latch fence. During replay, SkiaReplayCanvas intercepts raster image draws and substitutes them with atlas texture draws, mapping source coordinates into atlas space.

WPE WebKit 📟

WPE Platform API 🧩

New, modern platform API that supersedes usage of libwpe and WPE backends.

The recent WPE WebKit 2.51.92 release is the first one to have its WPEPlatform documentation online, but it was not included in the tarball. This issue has been corrected and tarballs for future releases will also include this documentation.

Scrolling using touch input with WPEPlatform would result in scrolling faster when more than one touch point was in effect. The gesture detector has been fixed to make scrolling have always a consistent speed.

Releases 📦️

The third —and likely the last— release candidates for the upcoming stable branch, WebKitGTK 2.51.92 and WPE WebKit 2.51.92, have been published. For those interested in previewing the upcoming 2.52.x series this release is expected to be quite stable; but there might be still some rough edges. Reporting issues in Bugzilla are, as usual, more than welcome.

That’s all for this week!

By Igalia WebKit Team at March 02, 2026 08:11 PM

February 23, 2026

Igalia WebKit Team: WebKit Igalia Periodical #57

Igalia WebKit

Update on what happened in WebKit in the week from February 9 to February 23.

In this week we have a nice fix for video streams timestamps, a fix for a PDF rendering regression, support for rendering video buffers provided by Qualcomm video decoders, and a fix for a font selection issue. Also notable we had a new WPE Android release, and the libsoup 3.6.6 release.

Cross-Port 🐱

Added a new webkit_feature_list_find() convenience function to the public API, which searches for a WebKitFeature given its identifier.

Multimedia 🎥

GStreamer-based multimedia support for WebKit, including (but not limited to) playback, capture, WebAudio, WebCodecs, and WebRTC.

Graphics 🖼️

Fixed a PDF rendering regression caused by the canvas 2D operation recording feature, where switching between the recording canvas and the GPU surface canvas failed to preserve the full save/restore nesting, clip stack, and transparency layer state. Replaced the fragile state-copying approach with a state replay mechanism in GraphicsContextSkia that tracks the full sequence of save restore, clip, and transparency layer operations, then reconstructs the exact nesting on the target canvas when flushing a recording.

Added support for rendering video buffers provided by Qualcomm hardware-accelerated decoders, with aid from the EXT_YUV_target OpenGL extension.

Fixed the font selection issue that the system fallback font cache mixed up different font styles.

Releases 📦️

WPE Android 0.3.2 has been released, and prebuilt packages are available at the Maven Central repository. This is a stable maintenance release which updates WPE WebKit to 2.50.5, which is the most recent stable release.

libsoup 3.6.6 has been released with numerous bug and security fixes.

That’s all for this week!

By Igalia WebKit Team at February 23, 2026 07:52 PM