February 22, 2024

Release Notes for Safari Technology Preview 189

Surfin’ Safari

Safari Technology Preview Release 189 is now available for download for macOS Sonoma and macOS Ventura. 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: 273602@main…274066@main.


New Features

  • Added support for light-dark() function for color values. (273634@main) (117033939)
  • Added support for @starting-style. (273740@main) (121918611)

Resolved Issues

  • Fixed named at-rule container getting skipped when the container is named in a :host selector. (273987@main) (120428386)
  • Fixed the name for a ::slotted pseudo-element in a container query getting resolved against the wrong scope. (274050@main) (122224135)


  • Removed :-webkit-full-screen-ancestor pseudo-class. (273620@main) (100782937)
  • Removed :-webkit-full-screen-controls-hidden pseudo-class. (273619@main) (121323330)
  • Removed :-webkit-full-page-media pseudo-class. (273618@main) (121752962)
  • Removed :-webkit-full-screen-document pseudo-class. (273639@main) (121816310)


Resolved Issues

  • Fixed parsing a self-closing SVG script element. It now successfully executes. (273697@main) (121887875)
  • Fixed “about:blank” document.referrer initialization. (273830@main) (97689906)


Resolved Issues

  • Fixed throwing a RangeError if Set methods are called on an object with negative size property. (274009@main) (121310940)
  • Fixed eval() function from another realm to not cause a direct eval call. (273782@main) (121546048)
  • Fixed eval() call with ...spread syntaxt to be a direct call. (273788@main) (121547890)
  • Fixed programming style for bitwise and in setExpectionPorts. (273960@main) (122138733)


Resolved Issues

  • Fixed Greek uppercase transforms failing for some characters. (274036@main) (90364897)
  • Fixed lingering boxes with content-visibility: hidden. (273602@main) (117916396)
  • Fixed parts of the content disappear when interacting with overflow:scroll, z-index and positioning (including Heroku apps). (273999@main) (120373474)
  • Fixed align-content and justify-content on scroll containers causing overflowing content to become inaccessible. (273737@main) (121366949)
  • Fixed line break at gaps between two inline elements in a container with white-space: nowrap. (121859917)
  • Fixed a floating element causing the latter half of a hyphenated word to disappear. (273836@main) (121889487)
  • Fixed cropped first letter for custom fonts that report negative advance width. (121891210)


Resolved Issues

  • Fixed SVG title to have display: none as the default UA style rule. (273991@main) (122185838)


Resolved Issues

  • Fixed history.pushState() and history.replaceState() to ignore the title argument. (273650@main) (75695791)
  • Fixed lang attribute in no namespace to only apply to HTML and SVG elements. (273726@main) (117795695)
  • Fixed spelling of clonable. (274063@main) (121516711)


  • Removed which from KeyboardEvent. (273701@main) (106580687)

Web Inspector

New Features

  • Added a Media details panel to Web Inspector when selecting a <video> or <audio> element in the Elements tab. (273777@main) (118865793)

Resolved Issues

  • Fixed font sizes in the Audits tab. (273615@main) (76162927)


Resolved Issues

  • Fixed navigator.credentials.create() rejects with “NotAllowedError: Operation Failed” after a conditional UI request is aborted. (273918@main) (109936742)


New Features

  • Enabled support for EXT_texture_mirror_clamp_to_edge, WEBGL_render_shared_exponent, and WEBGL_stencil_texturing. (273645@main) (121835897)


New Features

  • Added support for missing WebRTC stats. (273643@main) (121594743)

Resolved Issues

  • Fixed a bug that prevented HTML canvas elements from always being marked dirty on initialization. This could cause some video effects to have choppy animations. (273897@main) (121257960)
  • Fixed VideoTrackGenerator writer to close when its generator track (and all its clones) are stopped. (273778@main) (121835553)

February 22, 2024 01:10 AM

February 20, 2024

Carlos García Campos: A Clarification About WebKit Switching to Skia

Igalia WebKit

In the previous post I talked about the plans of the WebKit ports currently using Cairo to switch to Skia for 2D rendering. Apple ports don’t use Cairo, so they won’t be switching to Skia. I understand the post title was confusing, I’m sorry about that. The original post has been updated for clarity.

By carlos garcia campos at February 20, 2024 06:11 PM

February 19, 2024

Carlos García Campos: WebKitGTK and WPEWebKit Switching to Skia for 2D Graphics Rendering

Igalia WebKit

In recent years we have had an ongoing effort to improve graphics performance of the WebKit GTK and WPE ports. As a result of this we shipped features like threaded rendering, the DMA-BUF renderer, or proper vertical retrace synchronization (VSync). While these improvements have helped keep WebKit competitive, and even perform better than other engines in some scenarios, it has been clear for a while that we were reaching the limits of what can be achieved with a CPU based 2D renderer.

There was an attempt at making Cairo support GPU rendering, which did not work particularly well due to the library being designed around stateful operation based upon the PostScript model—resulting in a convenient and familiar API, great output quality, but hard to retarget and with some particularly slow corner cases. Meanwhile, other web engines have moved more work to the GPU, including 2D rendering, where many operations are considerably faster.

We checked all the available 2D rendering libraries we could find, but none of them met all our requirements, so we decided to try writing our own library. At the beginning it worked really well, with impressive results in performance even compared to other GPU based alternatives. However, it proved challenging to find the right balance between performance and rendering quality, so we decided to try other alternatives before continuing with its development. Our next option had always been Skia. The main reason why we didn’t choose Skia from the beginning was that it didn’t provide a public library with API stability that distros can package and we can use like most of our dependencies. It still wasn’t what we wanted, but now we have more experience in WebKit maintaining third party dependencies inside the source tree like ANGLE and libwebrtc, so it was no longer a blocker either.

In December 2023 we made the decision of giving Skia a try internally and see if it would be worth the effort of maintaining the project as a third party module inside WebKit. In just one month we had implemented enough features to be able to run all MotionMark tests. The results in the desktop were quite impressive, getting double the score of MotionMark global result. We still had to do more tests in embedded devices which are the actual target of WPE, but it was clear that, at least in the desktop, with this very initial implementation that was not even optimized (we kept our current architecture that is optimized for CPU rendering) we got much better results. We decided that Skia was the option, so we continued working on it and doing more tests in embedded devices. In the boards that we tried we also got better results than CPU rendering, but the difference was not so big, which means that with less powerful GPUs and with our current architecture designed for CPU rendering we were not that far from CPU rendering. That’s the reason why we managed to keep WPE competitive in embeeded devices, but Skia will not only bring performance improvements, it will also simplify the code and will allow us to implement new features . So, we had enough data already to make the final decision of going with Skia.

In February 2024 we reached a point in which our Skia internal branch was in an “upstreamable” state, so there was no reason to continue working privately. We met with several teams from Google, Sony, Apple and Red Hat to discuss with them about our intention to switch from Cairo to Skia, upstreaming what we had as soon as possible. We got really positive feedback from all of them, so we sent an email to the WebKit developers mailing list to make it public. And again we only got positive feedback, so we started to prepare the patches to import Skia into WebKit, add the CMake integration and the initial Skia implementation for the WPE port that already landed in main.

We will continue working on the Skia implementation in upstream WebKit, and we also have plans to change our architecture to better support the GPU rendering case in a more efficient way. We don’t have a deadline, it will be ready when we have implemented everything currently supported by Cairo, we don’t plan to switch with regressions. We are focused on the WPE port for now, but at some point we will start working on GTK too and other ports using cairo will eventually start getting Skia support as well.

By carlos garcia campos at February 19, 2024 01:27 PM

February 16, 2024

How to use Media Source Extensions with AirPlay

Surfin’ Safari

Media Source Extensions (MSE) is a popular way to provide streaming video on the web. It gives JavaScript control of the way bytes are sent to the browser for playback. At the 2023 Worldwide Developer conference, Apple announced a new Managed Media Source API that improves on MSE with efficient video streaming and longer battery life for iOS and other devices.

However, MMS and MSE, by nature, are not compatible with AirPlay, which requires a unique playback URL. AirPlay allows you to start playback of your favorite videos on your phone, move them to your TV and then switch off that phone. An AirPlay-compatible URL can be of any format such as an mp4, mpeg-ts, or HTTP Live Streaming (HLS).

This post will guide you through providing both sources and, in the process, build out a demo example.

WebKit MSE + AirPlay Demo

Since MMS/MSE uses binary blobs appended to a SourceBuffer it won’t work with AirPlay. But, if you create an alternative source that can be served as an AirPlay-compatible URL, there is a way to get them to work together.

When it comes to an AirPlay-compatible alternative, HLS is an option that offers a great deal of efficiency for users. There are numerous resources that can guide you in converting your video content to serve it with HLS. Apple offers a toolkit you can use and there are many other options as well.

const airplayURL = './video/demo.m3u8';
const videoURL = './video/demo.mp4';
const mediaType = 'video/mp4; codecs="avc1.640028"';

// Create a video element
const video = document.createElement('video');

// Set video element properties for the demo
video.controls = true;
video.loop = true;
video.muted = true;
video.autoplay = true;

In setting up the MediaSource, it’s easy to use feature detection to use Managed Media Source API to offer power-efficient streaming on browsers that support it and gracefully fallback to MSE where its not available:

// Feature detect MMS and fallback to MSE
const MediaSource = self.ManagedMediaSource || self.MediaSource;
const source = new MediaSource();

We also need a way to offer both our Media Source and AirPlay sources at the same time. The HTML video element’s ability to define multiple sources will do just that. It was originally intended to allow a user-agent to choose the best format of the video to be played and fallback should it not be supported.

  <source src="format/video.m3u8" type="application/x-mpegURL">
  <source src="format/video.ogg" type="video/ogg">
  <source src="format/video.mp4" type="video/mp4">
  <source src="format/video.webm" type="video/webm">

The browser will look over the list of available formats from top to bottom. If no matches are found, or if decoding fails along the way, it will select the next element in the list.

We can make use of this behavior, combining the ability for the user-agent to choose the best format and allowing AirPlay to select a playable source. You’ll create a URL from your Media Source and add it to a video element as the first source. This URL is local to the user-agent and can’t be shared, as it has no meaning outside the local context. Then you add the AirPlay-compatible URL as the second source.

// Add MSE/MMS streaming source
const videoSource1 = document.createElement('source');
videoSource1.type = 'video/mp4';
videoSource1.src = URL.createObjectURL(source); // Create URL from MediaSource

// Add AirPlay-compatible HLS source
const videoSource2 = document.createElement('source');
videoSource2. type = 'application/x-mpegURL';
videoSource2.src = airplayURL;

Now when Safari detects that an alternative source is available in addition to the MediaSource URL object, it will display the familiar AirPlay icon to the video player control. Should the user select AirPlay, it will switch over from MSE to the AirPlay-compatible URL.

The streaming code for this demo is very basic and serves as an example of getting the bytes from the video to the browser.

document.onload = async () => {
  if (!MediaSource.isTypeSupported(mediaType)) {
    return console.log('Media type is not supported.');

  await new Promise((resolve) => {
    source.addEventListener("sourceopen", resolve, { once: true });

  const sourceBuffer = source.addSourceBuffer(mediaType);

  async function loadSegment() {
    const response = await fetch(videoURL);
    const buffer = await response.arrayBuffer();
    await new Promise((resolve) => {
      sourceBuffer.addEventListener("updateend", resolve, { once: true });

  if (typeof(source.onstartstreaming) !== 'undefined') {
    source.onstartstreaming = async () => {
  } else loadSegment();


Offering support for MMS/MSE and AirPlay is gives users the best of all worlds and the video element makes it easy to offer multiple sources. You can learn more about the Managed Media Source API proposal at the W3C, and learn about HTTP Live Streaming from Apple’s documentation.

We love to hearing from you. Send a tweet to @webkit to share your thoughts on this feature. Find us on Mastodon at @jensimmons@front-end.social and @jondavis@mastodon.social. If you run into any issues, we welcome your WebKit bug reports on WebKit-powered features like this. Reporting issues and sharing your feedback makes an enormous difference.

February 16, 2024 06:35 PM

February 07, 2024

Release Notes for Safari Technology Preview 188

Surfin’ Safari

Safari Technology Preview Release 188 is now available for download for macOS Sonoma and macOS Ventura. 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: 272449@main…273601@main.


New Features

  • Added support for the new CSS content alternative text syntax. (272455@main) (26942023)

Resolved Issues

  • Fixed role assignment for <header> inside <main> and sectioning elements. (273188@main) (48370244)
  • Fixed text missing from accessibility labels for many common shadow DOM scenarios. (272531@main) (120223342)
  • Fixed the mapping for the iOS accessibility framework to speak “switch button”, “on”, and “off” for <input type=checkbox switch>. (273206@main) (121215059)
  • Fixed comboboxes to expose their linked objects correctly. (273542@main) (121242926)


Resolved Issues

  • Fixed the transition property to produce the shortest serialization. (272513@main) (119822401)
  • Fixed the animation property to produce the shortest serialization. (272629@main) (120439368)

Browser Changes

Resolved Issues

  • Fixed loading a ⌘Click fragment link in a background tab. (272906@main) (119079650)
  • Fixed saving linked subresources when saving web page resources. (272925@main) (120491493)


New Features

  • Added supports() syntax for @import rules. (273591@main) (109060734)

Resolved Issues

  • Fixed getComputedStyle() for invalid pseudo-elements. (272543@main) (98504661)
  • Fixed oklab and oklch lightness value clamping. (272501@main) (116195533)
  • Fixed poor performance with :has(+ :not(.class)) pseudo-class selector. (272678@main) (119819247)
  • Fixed CSS content computed value serialization. (272476@main) (120061551)
  • Fixed pseudo-element parsing in getComputedStyle() and KeyframeEffect.prototype.pseudoElement so they require them starting with :: (or : for 4 legacy pseudo-elements). (272499@main) (120170550)
  • Fixed CSS linear() easing. (272613@main) (120290721)
  • Fixed: Aliased :-webkit-full-screen pseudo-class to :fullscreen. (272577@main) (120335917)
  • Fixed: Aliased :-webkit-any-link to :any-link and :matches() to :is(). (272559@main) (120337922)
  • Fixed getComputedStyle() pseudo-element parsing to support the full range of CSS syntax. (272649@main) (120471227)
  • Fixed :not(:has(:not(foo))) getting misclassified as scope breaking. (273177@main) (120492012)
  • Fixed @supports to correctly handle support for some -webkit prefixed pseudo-elements that were incorrectly treated as unsupported. (272726@main) (120577690)
  • Fixed updating media-query sensitive meta tags after style changes. (272947@main) (120854167)


  • Removed -webkit-alt and alt properties. (272480@main) (120051066)
  • Removed the non-standard resize: auto property. (273035@main) (120138995)
  • Made -apple- prefixed pseudo-elements no longer valid. (272538@main) (120268884)
  • Removed :-webkit-animating-full-screen-transition pseudo-class. (273529@main) (121302758)
  • Removed :-khtml-drag pseudo-class. (273261@main) (121303391)


Resolved Issues

  • Fixed text-indent to affect the selected file(s) label for file inputs. (272837@main) (105223868)


Resolved Issues

  • Fixed navigator.cookieEnabled to return false when cookies are blocked. (273522@main) (121284878)

Lockdown Mode

Resolved Issues

  • Fixed Lockdown Mode disabling on sites with COOP and COEP HTTP headers. (273243@main) (119503109)


Resolved Issues

  • Fixed HLS video captions where there are multiple text tracks available. (272784@main) (119839950)
  • Fixed fullscreen video not scaling to display size when the Safari window is in Full Screen App Mode. (272733@main) (119893556)
  • Fixed handling key renewal requests that cause playback errors for some DRM content. (272592@main) (120230860)
  • Fixed paint-on captions shifting during playback. (272966@main) (120847946)
  • Fixed blocked encrypted sampled not getting enqueued after a CDM is attached to a SourceBuffer. (273340@main) (120879185)


Resolved Issues

  • Fixed resizing a <textarea> element with 1rem padding. (273029@main) (90639221)
  • Fixed incorrectly oriented Traditional Mongolian script characters. (272454@main) (93426525)
  • Fixed handling images with color spaces not supported by the backend to fallback to render in sRGB. (273204@main) (118238178)
  • Fixed check boxes and radio buttons to avoid floats. (273047@main) (118660695)
  • Fixed rendering issues when editing text. (273320@main) (119833765)
  • Fixed a floating element causing a list item bullet to be orphaned on constrained lines. (272451@main) (120022893)
  • Fixed fully repainting form controls with visual overflow and writing-mode: vertical-rl. (272799@main) (120066970)
  • Fixed incorrect inline box (hugging) outline painting in vertical writing modes. (272512@main) (120217559)
  • Fixed incorrect ch unit value in vertical-rl and vertical-lr when text-orientation is not upright. (272536@main) (120293590)
  • Fixed the color correctness of the color matrix filter. (272891@main) (120795573)
  • Fixed overflow: hidden preventing CSS Subgrid. (273134@main) (120848131)
  • Fixed the repaint area for underline text decorations. (273126@main) (121082290)
  • Fixed rendering floats and an out-of-flow <br> element with clear. (273407@main) (121444267)


Resolved Issues

  • Fixed unusable horizontal scrollbars with a right-to-left element. (272466@main) (109858866)
  • Fixed wheel overflow behavior with Shadow DOM elements. (273181@main) (118496293)
  • Fixed keyboard scrolling beyond the page getting stuck at a bad scroll offset. (272957@main) (120053910)
  • Fixed a variety of issues by disabling Scroll Anchoring. (273238@main) (121236706)


Resolved Issues

  • Fixed cases where website data is unexpectedly evicted. (272951@main) (119818267)


  • Removed support for AppCache. (273297@main) (113343269)


Resolved Issues

  • Fixed negative SVGTransform scale values to be correctly stringified. (272885@main) (118656892)
  • Fixed the layout of an SVG when it is inside an <iframe> without affecting the size of the <iframe>. (272503@main) (120178866)
  • Fixed displaying an SVG element inside a <switch> element. (272831@main) (120732837)


New Features

  • Added support for CustomStateSet in custom elements and :state() pseudo-class. (272474@main) (120072599)

Resolved Issues

  • Fixed: Added support for AES-GCM in WebCrypto. (273488@main) (101040216)
  • Fixed removing highlights in the Custom Highlights API. (272723@main) (119531671)
  • Fixed unnecessarily unsetting the iframe fullscreen flag. (272462@main) (120052751)
  • Fixed getElementsByName() to only return HTML elements, not SVG, MathML, or other types of elements. (272530@main) (120275680)
  • Fixed the button value for a pointerup event not matching the pointerdown event. (273263@main) (120429508)
  • Fixed a wheel event to fire on an element that has been re-inserted after document.open. (272960@main) (120893136)
  • Fixed Scroll To Text Fragment Text Directives to find text with additional unrendered white space in their node data. (273016@main) (120913588)
  • Fixed Media Capture API to capture the camera. (273258@main) (121256297)


  • Removed support for KeyboardEvent.altGraphKey. (273379@main) (102980723)
  • Removed AES-CFB support from WebCrypto. (272615@main) (120000331)
  • Removed the non-standard KeyboardEvent.keyLocation. (273457@main) (121564228)

Web Extensions

Resolved Issues

  • Fixed browsing.scripting.executeScript to handle all valid argument types. (120727491)


Resolved Issues

  • Fixed getClientCapabilities to align with WebAuthn standards to use a record type with camelCase values. (272998@main) (120442670)


New Features

  • Enabled support for EXT_conservative_depth and NV_shader_noperspective_interpolation. (272979@main) (120907578)

Resolved Issues

  • Fixed WebGL to be available in nested workers. (272765@main) (120279728)


Resolved Issues

  • Fixed triggering resolution scaling in the case of WebRTC maintain-framerate degradationPreference. (273172@main) (121041723)

February 07, 2024 10:57 PM

February 01, 2024

The web just gets better with Interop 2024

Surfin’ Safari

The web is amazing. It makes collaborating, learning, and connecting easy for billions of people, because it’s intentionally designed to run on radically different devices.

It’s your job as a web developer to ensure your project works in every browser and for every user — and that can be hard to do. It’s a far easier undertaking when browsers have identical implementations of the web technology you use.

Identical implementations are accomplished through the web standards process, where people collaborate together to write extremely detailed technical documents that define each new web technology — right down to how website bugs should work.

One way to check and see if browsers follow the web standards is through automated testing. There are several shared repositories of such tests, including Web Platform Tests. WPT contains over 1.8 million tests, of which over 95% pass in all of the major browsers.

The Interop Project

The Interop project aims to improve interoperability by encouraging browser engine teams to look deeper into specific focus areas. Now, for a third year, Apple, Bocoup, Google, Igalia, Microsoft, and Mozilla pooled our collective expertise and selected a specific subset of automated tests for 2024.

Some of the technologies chosen have been around for a long time. Other areas are brand new. By selecting some of the highest priority features that developers have avoided for years because of their bugs, we can get them to a place where they can finally be relied on. And by selecting exciting new technology, we can ensure it’s interoperable from the beginning.

To better understand where interoperability is going in the future, let’s first take a look at the impact of Interop 2023.

Interop 2023

Interop 2023 was even more of an overwhelming success than Interop 2022. In January 2023, 48% of the chosen tests passed in all three of the major browser engines (in those shipped to users: Chrome and Firefox for desktop Linux, and Safari on macOS Monterey). A year later, that pass rate rose to 95% (in Chrome Dev and Firefox Nightly for desktop Linux, and Safari Technology Preview on macOS Ventura).

Screenshot of the graph of results from January to December 2023, available at https://wpt.fyi/interop-2023The success of Interop 2023, seen on the “Experimental” dashboard. The “Interop” line, in dark green, shows the percentage of tests that passed in all three — Chrome Dev, Firefox Nightly, and Safari Technology Preview.

What did Interop 2023 accomplish?

  • It ensured that all browsers have full support for P3 color, seven years after it started shipping.
  • Form controls now support vertical writing modes, for the first time in the web’s history.
  • CSS border-image now works as originally intended.
  • Subgrid, Container Queries, :has(), Motion Path, CSS Math Functions, inert and @property are now supported in every modern browser.
  • Improved Web APIs include Offscreen Canvas, Modules in Web Workers, Import Maps, Import Assertions, and JavaScript Modules.
  • The entire Media Queries 4 specification is now supported everywhere, with easier to use syntax.
  • Web Components got a boost with adoptedStyleSheets, ElementInternals, Form-Associated Custom Elements, and the basic behavior of Shadow DOM and Custom Elements.
  • Useful CSS pseudo-classes can now be relied on, with consistent cross-browser support for :nth-child(), :nth-last-child(), :modal, :user-valid, and :user-invalid.
  • Feature queries now have new support for detecting font features.
  • Font Palettes provide robust support for color fonts.
  • Significant progress made improving CSS Masking, HTML Forms, Pointer and Mouse Events, Scrolling, Transforms, URL, WebCodecs, and a bucket of bugs causing web compat issues.
  • And more.

We hope this work gives you a renewed sense of confidence to use these technologies. If you found any of them hard-to-use in the past, give them another try.

Interop 2023 had twenty-six focus areas, twenty of which are being retired as a success. Work will continue on Custom Properties, Pointer and Mouse Events, URL, and a new grouping called “Layout” — consisting of Flexbox, Grid, and Subgrid.

Interop 2024

Now, we are doing it all again for 2024. Ninety-six focus area proposals were submitted for consideration. Ultimately, sixteen were chosen. Grouping some of the new proposals together, and continuing some of the work from 2023, gives Interop 2024 a total of seventeen focus areas.

The Interop 2024 dashboardThe Interop 2024 dashboard, looking at the “stable” browsers (those currently in the hands of everyday people). Coincidentally, the overall Interop score is once again starting at 48%.

New this year, Microsoft Edge now has its own column on the Interop dashboard. This currently represents Edge and Edge Dev running on Windows 10.

The 2024 Focus Areas


Interop 2023 included an Accessibility Investigation project. Led by Apple’s accessibility team, the group worked diligently to create new accessibility testing infrastructure for WPT, and write over 1300 new accessibility tests. These tests have now been included in Interop 2024 as a focus area, encouraging browsers to increase their support.

The majority of new accessibility tests cover WAI-ARIA, in particular, the Roles Model and the Accessible Name and Description Computation (AccName). Together, these provide a consistent mechanism for conveying the purpose or intent of an element so assistive technology users understand what it is and what they can do with it.

Other new accessibility tests cover how those technologies are incorporated into host languages. For example, the HTML Accessibility API Mappings specification (HTML-AAM) defines the default accessibility semantics of HTML elements, along with related rules for how browsers work with features like the <label> element and image alt text. (See the html-aam/roles WPT tests as an example.)

Another new set of tests cover the accessibility of display: contents. This display mode in CSS provides a useful mechanism for removing the box around content — helpful when wanting to adjust the parent/child/grandchild relationships of content for the purposes of Flexbox or Grid. But it was off limits for use for years, because of the lack of accessibility in early implementations. Removing the box on an item completely removed all the contents of that box from the accessibility tree. Sighted users could still see the child content, but many users of assistive technology experienced it completely disappearing. Most of these problems have been fixed in browsers, but not all, not for every situation. These new tests are the next step toward full accessibility and interoperability.

By including these new Accessibility tests in Interop 2024, the hope is to fix every issue in all browsers. We want it to be easier for developers to create accessible sites and make the web better for everyone, including people with disabilities.

CSS Nesting

CSS Nesting is a focus area for Interop 2024 to ensure any differences are ironed out, and to provide you with the confidence to use it. The ability to nest CSS shipped in all four major browsers in 2023 — first in Chrome, Edge, and Safari in April/May. And then in Firefox in August.

The web standard changed slightly between May and August, relaxing the original requirement that every nested selector start with a symbol. Developers can now simply write article, rather than needing to use & article.

All of the implementations have since been updated, but there are still small bits that could benefit from attention to interoperability, especially as the final complex details of how Nesting works are settled in the CSS Working Group. Most of Safari’s test failures, for example, are about how nested CSS interacts with the Shadow DOM via :host.

Custom Properties

The @property at-rule started shipping in browsers over the last few years. As part of Interop 2023, the Custom Properties focus area rose from 4% of tests passing in all stable browsers to 7.6% passing — with 90.7% passing in all of the preview browsers. Firefox is the last browser to add support, which is currently in progress in Firefox Nightly. Since this work isn’t done yet, the focus area is being continued in 2024.

With @property, developers can declare CSS custom properties in a fashion similar to how browser engines define CSS properties — specifying its syntax, inheritance behavior, and initial value.

@property --size {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;

This allows you to do things in CSS that were impossible before, like animating gradients or certain parts of transforms.

Declarative Shadow DOM

Declarative Shadow DOM is a declarative API that lets you create reusable widgets and components by using only HTML — no JavaScript is necessary. It’s been supported in Safari 16.4 since March 2023, and in Chrome 90 since April 2021. Firefox has an implementation in Firefox Nightly.

Declarative Shadow DOM was one of the often-asked-for features in the State of HTML 2023 survey, so it was chosen to be part of Interop 2024 to ensure it becomes interoperable across all browsers.

Font size adjust

The font-size-adjust property is a great example of the usefulness of placing attention on older technology. Firefox first implemented font size adjust in 2008, but it was rarely used or even discussed by web designers and developers. The early spec evolved over time, adding support for more languages through the two-value syntax, and becoming easier to use with the from-font value.

The WebKit team implemented the basic version in Safari 16.4 and added the updates in September’s Safari 17.0. Mozilla updated their implementation in Firefox 118, also in September 2023. Both Safari and Firefox now pass 100% of all tests. Chrome began an experimental implementation in 2015, but has yet to ship it. Now with Interop 2024, it’s likely every browser will gain complete support.

Font size adjust provides a simple way to conform all the fonts used in a string of text to be the same visual size — so every character in 1.4rem-sized text, for example, has the same x-height — or cap height, ch width, ic width, or ic height. The two value syntax allows you to choose which measurement to conform.

This property is especially useful when you are mixing code with regular text, or mixing multiple languages together, and the different fonts within the same sentence have different sizes. With font size adjust you can avoid weirdly-big letters. No more fussing with font metrics to find a magic number that makes them all look the same size.

a code demo showing the visual consistency of the inked x-height of multiple font familiesThe CSS `font-size-adjust: from font` makes the Courier font adjust its size to match its x-height with that from Iowan Old Style, instead of typesetting the code to be visually larger. The size uniformity holds even when fallback fonts are used instead.

Learn more about font-size-adjust by watching What’s new in CSS from WWDC23.

HTTPS URLs for WebSocket

A quirky aspect of the WebSocket API is that you need to use non-HTTP(S) schemes: ws: (insecure) and wss:. As the URLs function otherwise identically to HTTP(S) URLs, this makes the API a bit frustrating to deal with. Based on web developer feedback the WebKit team decided to address this last year by making the API support HTTP(S) URLs as well. We shipped support in Safari 17.0.

This means that instead of writing:

function webSocketHandle(path) {
  const url = new URL(path, location);
  url.protocol = location.protocol === "https:" ? "wss:" : "ws:";
  return new WebSocket(url);
// ...
const ws = webSocketHandle(path);

You can now write the much more ergonomic:

const ws = new WebSocket(path);

By bringing this to Interop 2024, we hope other browsers will adopt it as well, making it universally available for web developers.


IndexedDB is an API that provides powerful ways to store data client-side, in an object-oriented database. It started shipping in browsers in 2011, and over the years the web standard has kept evolving. Both version 2 and version 3 are supported by all major browsers. Version 2 is fully interoperable, but version 3 needs a bit of attention to bring up the quality of implementations. Being part of Interop 2024 will help ensure implementations are completed and aligned.


CSS Grid and Flexbox were both included in the original Interop project in 2021. Subgrid was added in Interop 2023. While all three layout methods are now in great shape, they still aren’t quite perfect. The tests for these three areas are now being combined into one Focus Area called Layout. Work will continue to ensure complex edge cases are more interoperable. Meanwhile, developers should absolutely feel confident using these three technologies, since all browsers have solid support for Flexbox, Grid, and now Subgrid.

Pointer and Mouse Events

Pointer events are DOM events that are fired for a pointing device. They create a single DOM event model to handle pointing input devices such as a mouse, pen/stylus, or touch with one or more fingers. This API first started shipping in browsers in 2012, and landed everywhere by 2019, but still had rocky interoperability.

In 2022, the Interop team launched an Investigation Project to look deeper into the current state of Pointer and Mouse Events, in an effort to clarify consensus and write tests that captured the state of that consensus. For Interop 2023, those tests enabled Pointer and Mouse Events to be a Focus Area, where the test pass rate was part of the Interop 2023 dashboard and score. Over the year, Pointer and Mouse Events rose from a test pass rate of 34% to 81% — the most significant progress of any area.

While passing 81% of tests is a significant improvement, there is more work to do, therefore Pointer and Mouse Events will continue to be a Focus Area for 2024.


The new popover attribute in HTML provides a built-into-the-browser way to have an element pop into view on the top layer of a page. If you are creating an overlay over the entire web page, the dialog element is the best option. But when you want to turn any other element into a popup message, user interface, or other kind of content that appears and disappears, the popover element provides a framework to do it.

Support for popover shipped in Chrome 114 and Safari 17.0 in 2023. Firefox currently has support in progress in Firefox Nightly. Being part of Interop 2024 will ensure this highly desired feature has a fantastic start.

Relative Color Syntax

Relative Color Syntax is a new way to define colors in CSS that allows you do so while referring to another color. You can, for instance, lighten or darken an existing color by a certain amount. You can take a color variable, adjust the saturation, and assign the new color to a second variable. Relative Color Syntax can be especially powerful when creating a design system.

a still from the WWDC session teach how relative color syntax worksLearn more about Relative Color Syntax by watching What’s new in CSS from WWDC23.

Safari 16.4 was the first browser to ship support, in March 2023. Chrome 119 and Edge 119 shipped support in Oct and Nov 2023. Currently, none of the implementations have support for using currentcolor with Relative Color Syntax.

The Relative Color Syntax focus area for Interop 2024 doesn’t test overall support of Relative Color Syntax. It’s narrowly focused on whether or not currentcolor is supported, and includes tests of out-of-gamut behavior — checking to see what happens on displays that don’t have support for P3 color. Inclusion in Interop 2024 will help these final bits get done.


The <video> element provides powerful functionality for putting video on the web. But often, developers want and need to do more. The HTMLVideoElement interface provides special properties and methods for manipulating video objects in JavaScript. And one of those methods is requestVideoFrameCallback(). It lets you perform per-video-frame operations on video in an efficient manner — operations like video processing or analysis, painting to canvas, and synchronization with audio sources.

Supported since Chrome 83 and Safari 15.4, inclusion in Interop 2024 will help browsers complete and polish our implementations.

Scrollbar styling

The scrollbar styling focus area includes two CSS properties that can be used to style scrollbars. The scrollbar-width property provides three values: auto, thin, and none. The auto value is the default width; thin provides a thinner scrollbar; and none hides the scrollbar while still allowing content to scroll. Firefox 64 implemented support in December 2018, and it just shipped in Chrome 121 and Edge 121.

The scrollbar-gutter property lets you reserve space for the scrollbar, so the layout is the same whether or not a scrollbar is present. The scrollbar-gutter: stable rule lets you tell the browser to reserve space for a scrollbar, even when a scrollbar isn’t there. This can prevent layout shifts from happening between states where scrollbars are needed or not needed. It shipped in Chrome 94, Edge 94 and Firefox 97, in 2021–22.

Safari has the most work to do to complete this Focus Area. Chrome and Firefox already pass 100% of the tests. Ironically, it was Safari who first provided the ability to style scrollbars with nine pseudo-elements, ::-webkit-scrollbar-*, back in 2009. However that approach to styling scrollbars never became an official CSS web standard. The CSS Working Group instead opted for a far simpler approach.

@starting-style and transition-behavior

This Focus Area brings attention to two new features for controlling animation. Both shipped in Chrome 117 and Edge 177 in Sept 2023.

The @starting-style rule in CSS lets you define starting values for a particular element. This is needed when the element is about to go through a transition. It also provides a way for transitioning in or out of display:none.

.alert {
  transition: background-color 2s;
  background-color: green;
  @starting-style {
    background-color: transparent;

In the above example, the background-color will transition from transparent to green when the element is appended to the document.

Previously, only animations could animate discretely-animatable properties. The transition-behavior property in CSS expands that capability to CSS transitions, paving the way for transitioning the display property when showing or hiding elements.

Text Directionality

The direction in which text flows is a vital aspect of typesetting on the web. Some languages flow from left-to-right, while others flow from right-to-left. One of the many bits of technology supporting text direction is the dir attribute. It lets you specifically mark any HTML element with the direction: left, right, or auto — where auto asks the browser to guess from the first letter. The interaction of directionality and shadow trees was not well-defined until recently. Now that it’s been addressed at a standards level, adding it to Interop 2024 helps us ensure implementations align as well.

text-wrap: balance

Web designers have long wished for a way to prevent very short or one-word lines of text — often known as widows or orphans. Since the advent of responsive web design and the lack of control over the width of columns, this desire has gotten even more challenging. The text-wrap property provides you with multiple options for telling the browser how to wrap text with different methods for calculating line breaks for specific use cases.

The text-wrap: balance rule is a great solution for headlines. It balances a few lines of text so that each line has about the same amount of text as the others. It shipped in Chrome 114 and Firefox 121, and is implemented in Safari Technology Preview.

Interop 2024 also includes tests of how text-wrap-mode, text-wrap-style, and white-space-collapse behave. The CSS Working Group recently changed to how these longhands interact with each other, so support is currently uneven between browsers. Interop 2024 will help ensure all browser engines update to the latest web standards.


URLs are one of the most fundamental parts of the web. Without them, the web would not exist. But like many things invented very early in the history of the web, support has yet to be fully interoperable. To improve this, the WHATWG wrote the URL Living Standard packed with details on exactly how URLs should work. The tests supporting this web standard were a focus area for Interop 2023, improving the pass rate from 77% to 85%. To ensure interoperability, the work in this area will continue in 2024.

Safari is proud to lead the pack, passing 99.7% of the tests. Improvements in other browsers will help ensure websites work correctly everywhere.

The 2024 Investigation projects

Interop 2024 also includes three investigation areas. These are “homework projects” for the Interop team to work on. All three this year are about writing and making it possible to run more tests — Accessibility Testing, Mobile Testing, and WebAssembly Testing. The Mobile Testing investigation project aims to complete the infrastructure needed at WPT to be able to test browsers on mobile operating systems, potentially to include those scores on the Interop project dashboard in the future.

While two of the three investigations are projects continuing from last year, they all are starting 2024 at zero percent done. Each team involved will set new goals for this year, and the dashboard will report progress on those goals.

Track the progress

Keep up with the progress of Interop 2024 throughout the year, on the Interop 2024 dashboard.

Our Commitment

We continue to believe that interoperability is one of the fundamental pillars that makes the web such a success. Our efforts in Interop 2022 and 2023 demonstrate how deeply we care about the web. We are excited to again collaborate with our colleagues in seizing this opportunity help the web work better for everyone.

February 01, 2024 05:00 PM

WPE WebKit Blog: Use Case: Server-side headless rendering

Igalia WebKit

WPE and server-side headless rendering

In many distributed applications, it can be useful to run a light web browser on the server side to render some HTML content or process images, video and/or audio using JavaScript.

Some concrete use-cases can be:

  • Video post-production using HTML overlays.
  • Easy 3D rendering with WebGL that can be broadcasted as a video stream.
  • Reusing the same JavaScript code between a frontend web application and the backend processing.

WPE WebKit is the perfect solution for all those use cases as it offers a lightweight solution which can run on low-end hardware or even within a container. It provides a lot of flexibility at the moment of choosing the backend infrastructure as WPE WebKit can, for instance, run from within a container with a very minimal Linux configuration (no need for any windowing system) and with full hardware acceleration and zero-copy of the video buffers between the GPU and the CPU.

Additionally, the fact that WPE WebKit is optimized for lower-powered devices, makes it also the perfect option for server-side rendering when scaling commercial deployments while keeping cost under control, which is yet another important factor to take into account when considering cloud rendering.

February 01, 2024 12:00 AM

January 29, 2024

WPE WebKit Blog: A New WPE Backend Using EGLStream

Igalia WebKit

What is a WPE Backend?

Depending on the target hardware WPE may need to use different techniques and technologies to ensure correct graphical rendering. To be independent of any user-interface toolkit and windowing system, WPE WebKit delegates the rendering to a third-party API defined in the libwpe library. A concrete implementation of this API is a “WPE backend”.

WPE WebKit is a multiprocess application, the end-user starts and controls the web widgets in the application process (which we often call “the UI process” while the web engine itself uses different subprocesses: WPENetworkProcess is in charge of managing network connections and WPEWebProcess (or “web process”) in charge of the HTML and JavaScript parsing, execution and rendering. The WPE backend is at a crossroads between the UI process and one or more web process instances.

Diagram showing a box for the WPE backend in between the UI process and WPEWebProcess

The WPE backend is a shared library that is loaded at runtime by the web process and by the UI process. It is used to render the visual aspect of a web page and transfer the resulting video buffer from the web process to the application process.

Backend Interfaces

The WPE backend shared library must export at least one symbol called _wpe_loader_interface of type struct wpe_loader_interface as defined in the libwpe API. Presently its only member is load_object, a callback function that receives a string with an interface name and returns concrete implementations of the following interfaces:

The names passed to the .load_object() function are the same as those of the interface types, prefixed with an underscore. For example, a .load_object("_wpe_renderer_host_interface") call must return a pointer to a struct wpe_renderer_host_interface object.

Example C code for a load_object callback.
static struct wpe_renderer_host_interface = { /* ... */ };
static struct wpe_renderer_backend_egl_interface = { /* ... */ };

static void*
my_backend_load_object(const char *name)
    if (!strcmp(name, "_wpe_renderer_host_interface"))
        return &my_renderer_host;
    if (!strcmp(name, "_wpe_renderer_backend_egl_interface"))
        return &my_renderer_backend_egl;

    /* ... */

    return NULL;

struct wpe_loader_interface _wpe_loader_interface = {
    .load_object = my_backend_load_object,

Each of these interfaces follow the same base structure: the struct members are callback functions, all interfaces have create and destroy members which act as instance constructor and destructor, plus any additional “methods”. The pointer returned by the create callback will be passed as the object “instance” of the other methods:

struct wpe_renderer_host_interface {
  void* (*create)(void);
  void  (*destroy)(void *object);
  /* ... */

In the UI process side WPE WebKit will create:

  • One “renderer host” instance, using wpe_renderer_host_interface.create().
  • Multiple “renderer host client” instances, using wpe_renderer_host_interface.create_client(). These are mainly used for IPC communication, one instance gets created for each web process launched by WebKit.
  • Multiple “view backend” instances, using wpe_view_backend_interface.create(). One instance is created for each rendering target in the web process.

In each web process—there can be more than one—WPE WebKit will create:

  • One “renderer backend EGL” instance, using wpe_renderer_backend_egl_interface.create().
  • Multiple “renderer backend EGL target” instances, using wpe_renderer_backend_egl_target_interface.create(). An instance is created for each new rendering target needed by the application.
How about wpe_renderer_backend_egl_offscreen_target_interface?

The rendererBackendEGLTarget instances may be created by the wpe_renderer_backend_egl_target_interface, or the wpe_renderer_backend_egl_offscreen_target_interface depending on the interfaces implemented in the backend.

Here we are only focusing on the wpe_renderer_backend_egl_target_interface that is relying on a classical EGL display (defined in the rendererBackendEGL instance). The wpe_renderer_backend_egl_offscreen_target_interface may be used in very specific use-cases that are out of the scope of this post. You can check its usage in the WPE WebKit source code for more information.

These instances typically communicate with each others using Unix sockets for IPC. The IPC layer must be implemented in the WPE backend itself because the libwpe interfaces only pass around the file descriptors to be used as communication endpoints.

From a topological point of view, all those instances are organized as follows:

From an usage point of view:

  • The rendererHost and rendererHostClient instances are only used to manage IPC endpoints on the UI process side that are connected to each running web process. They are not used by the graphical rendering system.
  • The rendererBackendEGL instance (one per web process) is only used to connect to the native display for a specific platform. For example, on a desktop Linux, the platform may be X11 where the native display would be the result of calling XOpenDisplay(); or the platform may be Wayland and in this case the native display would be the result of calling wl_display_connect(); and so on.
  • The rendererBackendEGLTarget (on the web process side) and viewBackend (on the UI process side) instances are the ones truly managing the web page graphical rendering.

Graphics Rendering

As seen above, the interfaces in charge of the rendering are wpe_renderer_backend_egl_target_interface and wpe_view_backend_interface. During their creation, WPE WebKit exchanges the file descriptors used to establish a direct IPC connection between a rendererBackendEGL (in the web process), and a viewBackend (in the UI process).

During the EGL initialization phase, when a new web process is launched, WebKit will use the native display and platform provided by the wpe_renderer_backend_egl_interface.get_native_display() and .get_platform() functions to create a suitable OpenGL ES context.

When WebKit’s ThreadedCompositor is ready to render a new frame (in the web process), it calls the wpe_renderer_backend_egl_target_interface.frame_will_render() function to let the WPE backend know that rendering is about to start. At this moment, the previously created OpenGL ES context is made current to be used as the target for GL drawing commands.

Once the threaded compositor has finished drawing, it will swap the front and back EGL buffers and call the wpe_renderer_backend_egl_target_interface.frame_rendered() function to signal that the frame is ready. The compositor will then wait until the WPE backend calls wpe_renderer_backend_egl_target_dispatch_frame_complete() to indicate that the compositor may produce a new frame.

What happens inside the .frame_will_render() and .frame_rendered() implementations is up to the WPE backend. As en example, it could set up a Frame Buffer Object to have the web content draw offscreen, in a texture that can be passed back to the UI process for further processing, or use extensions like EGLStream, or DMA-BUF exports to transfer the frame to the UI process without copying the pixel data.

Typically the backend sends each new frame to the corresponding view backend in in its .frame_rendered() function. The application can use the frame until it sends back an IPC message to the renderer target (in the web process) to indicate that the frame is not in use anymore and may be be freed or recycled. Although it is not a requirement to do it at this exact point, usually when a renderer backend receives this message it calls the wpe_renderer_backend_egl_target_dispatch_frame_complete() function to trigger the rendering of a new frame. As a side effect, this mechanism also allows controlling the pace at which new frames are produced.

Using EGLStream

EGLStream is an EGL extension that defines a mechanism to transfer hardware video buffers from one process to another efficiently, without getting them out of GPU memory. Although the extension is supported only in Nvidia hardware, it makes for a good example as it transparently handles some complexities involved, like buffers with multiple planes.

This backend uses the EGLStream extension to transfer graphics buffers from the web process, which acts as a producer, to the UI process acting as a consumer. The producer extension EGL_KHR_stream_producer_eglsurface allows creating a surface that may be used as target for rendering, then using eglSwapBuffers() finishes drawing and sends the result to the consumer. Meanwhile, in the consumer side, the EGL_NV_stream_consumer_eglimage extension is used to turn each buffer into an EGLImage.

The reference source code for this WPE backend is available in the WPEBackend-offscreen-nvidia repository, which has been tested with WPE WebKit 2.38.x or 2.40.x, and libwpe version 1.14.x.

Behold, the Future Belongs to DMA-BUF!

With the growing adoption of DMA-BUF for sharing memory buffers on modern Linux platforms, the WPE WebKit architecture will be evolving and, in the future, the need for a WPE Backend should disappear in most cases.

Ongoing work on WPE WebKit removes the need to provide a WPE backend implementation for most hardware platforms, with a generic implementation using DMA-BUF provided as an integral, built-in feature of WebKit. It will still be possible to provide external implementations for platforms that might need to use custom buffer sharing mechanisms.

From the application developer point of view, in most cases writing programs that use the WPE WebKit API will be simpler, with the complexity of the communication among multiple processes handled by WebKit.

Stream Setup

The steps needed to set up EGLStream endpoints need to be done in a particular order:

  1. Create the consumer.
  2. Get the stream file descriptor for the consumer.
  3. Send the stream file descriptor to the producer.
  4. Create the producer.

First, the consumer needs to be created:

EGLStream createConsumerStream(EGLDisplay eglDisplay) {
    static const EGLint s_streamAttribs[] = {
    return eglCreateStreamKHR(eglDisplay, s_streamAttribs);

The EGL_STREAM_FIFO_LENGTH_KHR parameter defines the length of the EGLStream queue. If set to zero, the stream will work in “mailbox” mode and each time the producer has a new frame it will empty the stream content and replace the frame by the new one. If non-zero, the stream works work in “FIFO” mode, which means that the stream queue can contain up to EGL_STREAM_FIFO_LENGTH_KHR frames.

Here we configure a queue for one frame because in this case the specification of EGL_KHR_stream_producer_eglsurface guarantees that calling eglSwapBuffers() on the producer the call will block until the consumer retires the previous frame from queue. This is used as implicit synchronization between the UI process side and the web process side without needing to rely on custom IPC, which would add a small delay between frames.

The EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR parameter defines the maximum timeout in microseconds to wait on the consumer side to acquire a frame when calling eglStreamConsumerAcquireKHR(). It is only used with the EGL_KHR_stream_consumer_gltexture extension because the EGL_NV_stream_consumer_eglimage extension allows setting a timeout on each call to eglQueryStreamConsumerEventNV() function.

Second, to initialize the consumer using the EGL_NV_stream_consumer_eglimage extension it is enough to call the eglStreamImageConsumerConnectNV() function.

Once the consumer has been initialized, you need to send the EGLStream file descriptor to the producer process. The usual way of achieving this would be using IPC between the two processes, sending the file descriptor in a SCM_RIGHTS message through an Unix socket—although with recent kernels using pidfd_getfd() may be an option if both processes are related.

When the file descriptor is finally received, the producer endpoint can be created using the EGL_KHR_stream_producer_eglsurface extension:

const EGLint surfaceAttribs[] = {
    EGL_WIDTH, width,
    EGL_HEIGHT, height,
EGLStream eglStream = eglCreateStreamFromFileDescriptorKHR(eglDisplay, consumerFD);
EGLSurface eglSurface = eglCreateStreamProducerSurfaceKHR(eglDisplay, config, eglStream, surfaceAttribs);

As with pbuffer surfaces, the dimensions need to be specified as surface attributes. When picking a frame buffer configuration with eglChooseConfig() the EGL_SURFACE_TYPE attribute must be set to EGL_STREAM_BIT_KHR. From this point onwards, rendering proceeds as usual: the EGL surface and context are made active, and once the painting is done a call to eglSwapBuffers() will “present” the frame, which in this case means sending the buffer with the pixel data down the EGLStream to the consumer.

Consuming Frames

While on the producer side rendering treats the EGLStream surface like any other, on the consumer some more work is needed to manager the lifetime of the data received: frames have to be manually acquired and released once they are not needed anymore.

The producer calls eglQueryStreamConsumerEventNV() repeatedly to retire the next event from the stream:

  • EGL_STREAM_IMAGE_ADD_NV indicates that there is a buffer in the stream that has not yet been bound to an EGLImage, and the application needs to create a new one to which the actual data will be bound later.
  • EGL_STREAM_IMAGE_AVAILABLE_NV indicates that a new frame is available and that it can be bound to the previously created EGLImage.
  • EGL_STREAM_IMAGE_REMOVE_NV indicates that a buffer has been retired from the stream, and that its associated EGLImage may be released once the application has finished using it.

This translates roughly to the following code:

static constexpr EGLTime MAX_TIMEOUT_USEC = 1000 * 1000;
EGLImage eglImage = EGL_NO_IMAGE;

while (true) {
    EGLenum event = 0;
    EGLAttrib data = 0;

    // WARNING: The specification states that the timeout is in nanoseconds
    // (see: https://registry.khronos.org/EGL/extensions/NV/EGL_NV_stream_consumer_eglimage.txt)
    // but in reality it is in microseconds, at least with the version 535.113.01 of the NVidia drivers.
    if (!eglQueryStreamConsumerEventNV(display, eglStream, MAX_TIMEOUT_USEC, &event, &data))

    switch (event) {
      case EGL_STREAM_IMAGE_ADD_NV: // Bind an incoming buffer to an EGLImage.
          if (eglImage) eglDestroyImage(display, eglImage);
          eglImage = eglCreateImage(display, EGL_NO_CONTEXT, EGL_STREAM_CONSUMER_IMAGE_NV,
                                    static_cast<EGLClientBuffer>(eglStream), nullptr);
          continue; // Handle the next event.

      case EGL_STREAM_IMAGE_REMOVE_NV: // Buffer removed, EGLImage may be disposed.
          if (data) {
              EGLImage image = reinterpret_cast<EGLImage>(data);
              eglDestroyImage(display, image);
              if (image == eglImage)
                  eglImage = EGL_NO_IMAGE;
          continue; // Handle the next event.

      case EGL_STREAM_IMAGE_AVAILABLE_NV: // New frame available.
          if (eglStreamAcquireImageNV(display, eglStream, &eglImage, EGL_NO_SYNC))

          continue; // Handle the next event.

    /*** Use the EGLImage here ***/

    eglStreamReleaseImageNV(display, eglStream, eglImage, EGL_NO_SYNC);

The application is free to use each EGLImage as it sees fit. An obvious example would be to use it as the contents for a texture, which then gets painted in the “content” area of a web browser; or as the contents of the screen for an in-game computer that the player can interact with, enabling display of real, live web content as part of the gaming experience—now that would be a deeply embedded browser!

One Last Thing

There is a small showstopper to have EGLStream support working: currently when WPE WebKit uses surfaceless EGL contexts it sets the surface type to EGL_WINDOW_BIT attribute, while EGL_STREAM_BIT_KHR would be needed instead. A small patch is enough to apply this tweak:

diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp b/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp
index d5efa070..5f200edc 100644
--- a/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp
+++ b/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp
@@ -122,9 +122,11 @@ bool GLContextEGL::getEGLConfig(EGLDisplay display, EGLConfig* config, EGLSurfac
         attributeList[13] = EGL_PIXMAP_BIT;
     case GLContextEGL::WindowSurface:
-    case GLContextEGL::Surfaceless:
         attributeList[13] = EGL_WINDOW_BIT;
+    case GLContextEGL::Surfaceless:
+        attributeList[13] = EGL_STREAM_BIT_KHR;
+        break;

     EGLint count;

January 29, 2024 06:00 AM

January 25, 2024

Bringing Back Horizontal Rules in Select Elements

Surfin’ Safari

In September 2023, Safari 17.0 on macOS shipped a small but interesting change to the <select> element. You can now put an <hr> element, known as a horizontal rule, inside a <select> element, which will draw a horizontal line again. Again, because Safari used to support this over a decade ago — more on that story later.

The horizontal rule creates visual breaks between options to help users scan and compare against similar options.

Depicts a select menu with Choose paper size label and the following options with visual separators between groups: 5.5 x 8.5 in, 8.5 × 11.0 in, 8.5 × 14.0 in, 11.0 x 17.0 in, A3, A4, A5, A6, Envelope #10, Envelope B5, Envelope C5, Envelope MonarchSelect element without separators; Select element with horizontal rule separators.

It’s a small change, but it’s been getting attention lately. Simply add an <hr> between <option> elements to insert a line:

<label for="papersize">Select Paper Size:</label>
<select name="papersize">
    <option>Select a paper size</option>
    <option>5.5 × 8.5 in</option>
    <option>8.5 × 11.0 in</option>
    <option>8.5 × 14.0 in</option>
    <option>11.0 × 17.0 in</option>
    <option>Envelope #10</option>
    <option>Envelope B5</option>
    <option>Envelope C5</option>
    <option>Envelope Monarch</option>

Interactive demo of Horizontal Rule elements in a Select element.

So why did this work for years but then stop? Where did it go?

An HTML parser regression story

Well over a decade ago, WebKit adopted a new HTML parser. It was based on the HTML5 standardization effort, which attempted to unify the HTML language, as it had diverged quite a bit in implementations. And it was a far cry from the SGML dialect some in the standardization community pretended HTML to be. It represented a huge milestone in the development of HTML. We were finally on a path where all implementations would agree on what any arbitrary byte stream of HTML represented.

Replacing WebKit’s HTML parser was a large undertaking. It brought huge benefits, but it was still missing a few bits when it shipped. In fact, one feature from WebKit had been hidden from HTML. You could still see it by manipulating the DOM or using XML, but apart from a couple of experts nobody knew.

That feature was hr elements nested in select elements. Originally it was added by Adele Peterson at Apple in 2006 to support a common UI paradigm on the web: separators between select box options. We discovered this while doing some maintenance work on the HTML parser and agreed this was still a desirable feature. We also re-discovered that in 2018 a feature request was opened against the HTML Standard for this exact feature.

To introduce this feature again at this stage required some careful changes to the HTML parser portion of the HTML Standard as well as some corresponding semantic and conformance changes. After all, we wanted to fix this regression while preserving HTML parser interoperability. At the same time we wanted to ensure the feature was properly standardized as well. With the help from others in the HTML standardization community we managed to make this change and it’s now part of multiple browsers and harder to accidentally regress again due to improved cross-browser test coverage.

That’s the story of how we lost access to separators in select boxes for a decade and then got them back, fully standardized, in Safari 17.0 (commit 263624).

If you’re still reading, you might wonder what other maintenance work we did on the HTML parser. It included a bunch of small fixes that made WebKit more standards compliant. Where it was appropriate, cross-browser test coverage was improved as well:

  • Handle comments directly that follow the body closing tag (commit 262222).
  • Correct a minor bug in CDATA handling (applies to SVG and MathML when used inside HTML) (commit 262408).
  • Fully remove support for command, layer, and nolayer elements (commits 262431 & 262553).
  • Corrected SVG and MathML attribute handling (commit 262502).
  • Added support for the search element (commit 264110).

Some notes about separators in select elements

It’s important to be aware that this feature adds visual separators. They are not announced by assistive technologies like VoiceOver.

It’s also worth noting that the HTML parser only supports <hr> as a child of <select> elements, not as a child of <optgroup> elements.

Lastly, when using a <select> element with a size attribute value greater than 1, the separators are instead rendered as blank space, similar to the space added for <optgroup> elements.


Using <hr> in <select> gives authors another choice in how to visually separate options for users. Instead of the blank space rendered with <optgroup>, now authors can use lines too.

We love to hearing from you. Send a tweet to @webkit to share your thoughts on this feature. Find us on Mastodon at @jensimmons@front-end.social and @jondavis@mastodon.social. If you run into any issues, we welcome your WebKit bug reports on WebKit features like this. Reporting issues makes an enormouse difference.

You can also download the latest Safari Technology Preview to try out new web platform features like this before they appear in a Safari beta.

January 25, 2024 06:18 PM

January 24, 2024

Release Notes for Safari Technology Preview 187

Surfin’ Safari

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

This release includes a select set of bug fixes not associated with a range of commits. These release notes have been updated to reflect the correct set of WebKit changes.


Resolved Issues

  • Fixed text missing from accessibility labels for many common shadow DOM scenarios. (272531@main) (120223342)


Resolved Issues

  • Fixed HLS video captions where there are multiple text tracks available. (272784@main) (119839950)
  • Fixed fullscreen video not scaling to display size when the Safari window is in Full Screen App Mode. (272733@main) (119893556)
  • Fixed handling key renewal requests that cause playback errors for some DRM content. (272592@main) (120230860)

January 24, 2024 10:56 PM

January 22, 2024

WebKit Features in Safari 17.3

Surfin’ Safari

Last month, Safari 17.2 brought our biggest December release of web technology ever — with 39 new features and 169 bug fixes. Now, in January, Safari 17.3 brings bits of polish to that release.



  • Fixed nested @supports queries with declarations. (113652033)
  • Fixed the caret color on iOS following an explicitly-set CSS color attribute. (118401826)


  • Fixed cookies not always working as expected with Samesite=Lax. (119362503)
  • Fixed an issue causing sign in to fail on Delta.com. (120431796)


  • Fixed to not loop if current time or duration is zero. (118902468)
  • Fixed in-band captions wrapping unnecessarily. (119138261)


  • Fixed unauthenticated cross-site Fetch requests to not use the global HSTS cache. (119047103)

Web Animations

  • Fixed: Prevent scheduling for an effect targeting an element with display: none. (119191813)

Updating to Safari 17.3

Safari 17.3 is available for iOS 17, iPadOS 17, macOS Sonoma, macOS Ventura and macOS Monterey.

If you are running macOS Ventura or macOS Monterey, you can update Safari by itself by going to Software Update, and clicking “More info”. On macOS Ventura, that’s  > System Settings > General > Software Update > More info.

To get the latest version of Safari on your iPhone or iPad, go to Settings > General > Software Update, and tap to update.


We love hearing from you. To share your thoughts on Safari 17.3, find us on Mastodon at @jensimmons@front-end.social and @jondavis@mastodon.social. Or send a reply on X to @webkit. If you run into any issues, we welcome your feedback on Safari UI, or your WebKit bug report about web technologies or Web Inspector. Filing issues really does make a difference.

Download the latest Safari Technology Preview to stay at the forefront of the web platform and to use the latest Web Inspector features.

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

January 22, 2024 06:30 PM

January 11, 2024

Release Notes for Safari Technology Preview 186

Surfin’ Safari

Safari Technology Preview Release 186 is now available for download for macOS Sonoma and macOS Ventura. 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: 271832@main…272448@main.


New Features

  • Added support for invalidating :any-link, :link, and :-webkit-any-link inside :has() (271919@main) (116616425)
  • Added support for @scope invalidation. (271897@main) (119313058)
  • Added support for an implicit scoping root when there is no <scope-start>. (272390@main) (119659940)
  • Added support for :scope inside @scope prelude. (272065@main) (119661541)
  • Added support for align-content on table cells. (272373@main) (119701629)
  • Added support for content-visibility to be animate-able. (272364@main) (119940258)

Resolved Issues

  • Fixed querySelector() to not throw an exception for -webkit- prefixed pseudo-elements. (272337@main) (99299129)
  • Fixed :user-invalid triggering while typing a date. (272346@main) (110687369)
  • Fixed nested @supports queries with declarations. (271989@main) (113652033)
  • Fixed contain: inline-size breaking grid-template-rows: auto. (272085@main) (113915953)
  • Fixed parsing and serialization of -webkit- prefixed pseudo-elements. (272411@main) (118081134)
  • Fixed incorrect scoping roots with nested @scope. (271941@main) (119511916)
  • Fixed @scope prelude to take nesting into account. (272117@main) (119711922)
  • Fixed grid with size containment and min-height not sizing row correctly. (272276@main) (119736473)
  • Fixed @scope to be able to have orphaned declarations with an implicit enclosing style rule. (119937025)


New Features

  • Added focus ring support for <input type="checkbox" switch>. (271884@main) (119499785)
  • Added vertical rendering support for <input type="checkbox" switch>. (272405@main) (119940157)


Resolved Issues

  • Fixed missing support for the direction attribute in the list of attributes whose values are matched case-insensitively with attribute selectors. (272219@main) (119432066)


Resolved Issues

  • Fixed async functions and generators to properly handle promises with throwing “constructor” getter. (272291@main) (119734587)


Resolved Issues

  • Fixed rendering for a <div> within a transformed parent <div> with overflow: hidden. (271894@main) (118901069)
  • Fixed offsetHeight and offsetWidth are 0 for an inline box wrapping a block. (272386@main) (119955792)

Service Workers

Resolved Issues

  • Fixed duplicate load requests with an active service worker. (272024@main) (104377727)

Web Animations

Resolved Issues

  • Fixed percentage transform animations when width and height are animated. (272022@main) (63309680)
  • Fixed a paused animation where currentTime is changed to 0 not restarting when unpaused. (271872@main) (118826588)


New Features

  • Added a “gamepad” permission policy defaulting to * for the allow list. (272199@main) (83219098)
  • Added ShadowRoot clonable attribute. (272096@main) (119707278)

Resolved Issues

  • Fixed invalid coordinates on wheel and gesturechange events inside an iframe. (272176@main) (105243167) (FB11986074)
  • Fixed HTMLAreaElement to align with the HTML Standard. (272217@main) (110028213)
  • Fixed Scroll To Text Fragment to not scroll after dynamic stylesheet loads and the user has scrolled. (272151@main) (112608578)
  • Fixed toggling the spellcheck attribute not toggling spelling markers on input elements. (271927@main) (119269616)

January 11, 2024 01:36 AM

January 10, 2024

Announcing MotionMark 1.3

Surfin’ Safari

We are glad to announce an update to MotionMark, Apple’s graphics benchmark for web browsers, taking it to version 1.3. This is a minor update, intended to improve the behavior of the benchmark on a wider variety of hardware devices and to improve the reliability of the scores.

Motion Mark 3 logo

The most significant behavior change is that the benchmark now adapts to the browser’s refresh rate, automatically detecting the frequency of requestAnimationFrame callbacks and using the result to set the target frame rate. Previously, it assumed that the target was 60 frames per second; if the browser called requestAnimationFrame at some different frequency, the results would be invalid.

With this change, comparing benchmark scores between browsers on devices with non-60Hz displays requires some care; a browser that chooses to run requestAnimationFrame at 120Hz has less time to do work per frame, so it will return a lower score. We advise that the display refresh rate is set to 60Hz for such comparisons, as described in the About page.

Minor changes were made to various sub-tests for improved reliability. The maximum complexity of the Multiply subtest was increased because newer, faster configurations could easily hit the cap. The way that elements are hidden in Multiply was changed to ensure only the visible elements contribute to the rendering cost. The Paths test was tweaked to ensure a more consistent workload. And finally, the Design subtest was changed to avoid text getting clipped out at the stage boundary, which would lead to artificially high scores.

There are two scoring-related changes. First, scoring previously chose between a “flat” profile and a “slope” profile based on which had the lowest variance, but the “flat” profile often produced inaccurate scores; it was removed. Second, this version changes which particular frames contribute to the score. The benchmark works by ramping the scene complexity and measuring the duration of some number of frames at each complexity level. In the HTML and SVG sub-tests, the changes in scene complexity involve DOM mutations, in the form of removing elements (since complexity ramps down as the test runs). So there are two types of frames: “mutation” frames and “animation” frames. Previously, the durations of both types of frames fed into the score computation. However, “mutation” frames generally take longer because DOM mutation tends to be more expensive than just animating existing elements, and this adds noise to the results. Since MotionMark is intended to be an animation benchmark, it makes sense for only “animation” frames to contribute to the score.

MotionMark development is now in GitHub; we welcome your participation there.

Change log

January 10, 2024 04:45 PM

December 21, 2023

WebGPU now available for testing in Safari Technology Preview

Surfin’ Safari

WebGPU is a new standards-compliant API that enables high-performance 3D graphics and general-purpose computations on the Web. WebGPU programs are written in JavaScript but expose GPU functionality, allowing GPU computing to be used in Web content for the first time. Starting in Safari Technology Preview 185, WebGPU can be enabled for early testing and development.

To enable WebGPU, turn on the “WebGPU”, “GPU Process: DOM Rendering” and “GPU Process: Canvas Rendering” feature flags in the Feature Flags tab in Safari Preferences. If you don’t see the Feature Flags tab, you need to first check “Show features for web developers” in the Advanced tab.

Once you have WebGPU enabled in Safari Technology Preview 185, try out this example of WebGPU. It utilizes many of the best features of WebGPU.

WebGPU JavaScript API

The WebGPU API is accessed through JavaScript, similar to WebGL.

Creating a GPUDevice

In order to use WebGPU, a device must be created. Resources and pipeline state are created from a GPUDevice instance. To create a device with default limits and features which are supported on all devices supporting WebGPU, we can pass zero parameters to the invocations of requestAdapter and requestDevice.

const adapter = await navigator.gpu.requestAdapter();
device = await adapter.requestDevice();

Configuring a GPUCanvasContext

The GPUCanvasContext is an interface that allows you to configure how your content will be displayed in the corresponding HTMLCanvas element on the page.

context = canvas.getContext('webgpu');
const canvasFormat = "bgra8unorm";

const contextConfiguration = {
    device: device,
    format: canvasFormat,
    alphaMode: 'opaque',

Creating a GPURenderPipeline

A GPURenderPipeline or a corresponding GPUComputePipeline are used to configure the pipeline state of the graphics driver. This pipeline state is then used in a GPURenderPassEncoder or GPUComputePassEncoder as later illustrated.

const shaderModule = device.createShaderModule({ code: wgslSource });
const vertexStageDescriptor = { module: shaderModule, entryPoint: "vsmain" };
const fragmentStageDescriptor = { module: shaderModule, entryPoint: "fsmain" };
const renderPipelineDescriptor = {
    layout: 'auto',
    vertex: vertexStageDescriptor,
    fragment: fragmentStageDescriptor,
    primitive: {topology: "triangle-list" },
const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);

Issuing draw calls

A GPURenderPassEncoder is created to send draw calls to the graphics driver. In the below example, we draw a simple triangle which contains three vertices. A GPURenderPassEncoder can also draw multiple instances of the same geometry or draw from an offset of a vertex buffer.

const colorAttachmentDescriptor = {
    view: renderAttachment,
    loadOp: "clear",
    storeOp: "store",
    clearColor: { r: 0.15, g: 0.15, b: 0.5, a: 1 }
const renderPassDescriptor = { colorAttachments: [colorAttachmentDescriptor] };
const commandEncoder = device.createCommandEncoder();
const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
const vertexBufferSlot = 0;
renderPassEncoder.setVertexBuffer(vertexBufferSlot, vertexBuffer, 0);
renderPassEncoder.draw(3, 1, 0, 0); // 3 vertices, 1 instance, 0th vertex, 0th instance.
const commandBuffer = commandEncoder.finish();
const queue = device.queue;

WebGPU Shading Language

WebGPU introduces WGSL, a platform independent shading language for the web. Here is an example of a WGSL shader source that would be passed in place of wgslSource in the above API call:

const wgslSource = `
    struct Vertex {
        @builtin(position) Position: vec4<f32>,
        @location(0) color: vec4<f32>,

    @vertex fn vsmain(@builtin(vertex_index) VertexIndex: u32) -> Vertex
        var pos: array<vec2<f32>, 3> = array<vec2<f32>, 3>(
            vec2<f32>( 0.0,  0.5),
            vec2<f32>(-0.5, -0.5),
            vec2<f32>( 0.5, -0.5));
        var vertex_out : Vertex;
        vertex_out.Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
        vertex_out.color = vec4<f32>(pos[VertexIndex] + vec2<f32>(0.5, 0.5), 0.0, 1.0);
        return vertex_out;

    @fragment fn fsmain(in: Vertex) -> @location(0) vec4<f32>
        return in.color;

Try WebGPU and file bugs!

We’re very excited to have an early version of WebGPU and WGSL in the latest version of Safari Technology Preview. Please do try it out. Check out the public repository of WebGPU samples. And file bugs or issues you discover at bugs.webkit.org.

December 21, 2023 05:30 PM

December 20, 2023

Release Notes for Safari Technology Preview 185

Surfin’ Safari

Safari Technology Preview Release 185 is now available for download for macOS Sonoma and macOS Ventura. 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: 271111@main…271831@main.


Resolved Issues

  • Fixed: Removed the default ARIA-level heading for a heading role, matching removal from ARIA specifications. (271497@main) (119059172)


New Features

  • Added support for align-content on block containers. (271818@main) (114740670)
  • Added percentage values support for letter-spacing and word-spacing. (271357@main) (116562262)
  • Added support for scroll anchoring with overflow-anchor. (271790@main) (118365809)
  • Added support for @scope. (271670@main) (119261250)
  • Added support for ::grammar-error and ::spelling-error (271684@main) (119314048)

Resolved Issues

  • Fixed CSS counters to update properly on style changes. (271451@main) (109416780)
  • Fixed: Updated Full Size Kana table to Unicode 15. (271276@main) (111508663)
  • Fixed missing user-agent stylesheet rules for MathML fractions. (271337@main) (116922348)
  • Fixed selection gaps to get painted with the expected ::selection pseudo-element color. (271129@main) (117796745)
  • Fixed ::backdrop to be allowed after ::slotted(). (271366@main) (119015204)
  • Fixed: Removed support for special first track handling to align with the evolving Masonry standard. (271757@main) (119041053)


New Features

  • Added support for <input type="checkbox" switch>. (271736@main) (119378678)


New Features

  • Added WebCodecs VP9 profile 2 support. (271238@main) (118879753)

Resolved Issues

  • Fixed an HEVC decoder issue when translating annexb data. (271728@main) (116768196)
  • Fixed AV1-in-MP4 codec string not shown in Show Media Stats. (271187@main) (118850797)
  • Fixed getDisplayMedia frameRate always at 30 regardless of constraints. (271233@main) (118874132)
  • Fixed to not loop if current time or duration is zero. (271256@main) (118902468)
  • Fixed in-band captions wrapping unnecessarily. (271415@main) (119024855)


Resolved Issues

  • Fixed: Removed the <font> text-decoration color override quirk. (271338@main) (102920597)
  • Fixed shadow invalidation issue in vertical-rl mode. (271416@main) (111915460)
  • Fixed text in flex items not breaking under specific conditions. (271122@main) (118796634)


New Features

  • Added Element.prototype.setHTMLUnsafe(), ShadowRoot.prototype.setHTMLUnsafe(), and Document.parseHTMLUnsafe() methods. (271423@main) (115345128)

Resolved Issues

  • Fixed updating resizeBy and resizeTo to use int rather than float to align with specifications. (271194@main) (118872048)
  • Fixed the CookieChangeEvent to not be exposed when the Cookie Store API is disabled. (271243@main) (118902989)
  • Fixed <progress> to use the page’s preferred rendering update interval. (271431@main) (118976548)
  • Fixed Element.prototype.setAttributeNode() to not treat attribute names case insensitively. (271363@main) (119013600)
  • Fixed WebGL OffscreenCanvas returning the previously created WebGL1 context when asking for WebGL2. (271546@main) (119028794)
  • Fixed to allow :checked and :indeterminate to match at the same time. (271430@main) (119075969)

Web Inspector

New Features

  • Expanded OffscreenCanvas support for bitmaprenderer, webgl, and webgl2. (271300@main) (109594232)
  • Added Grid overlay support for Masonry layout. (271332@main) (118833618)


New Features

  • Added support for getClientCapabilities(). (271584@main) (119058559)


New Features

  • Enabled WebGPU to be testable via a feature flag. (271725@main) (119262908)

December 20, 2023 10:17 PM

December 11, 2023

WebKit Features in Safari 17.2

Surfin’ Safari

Web technology is constantly moving forward, with both big new features and small subtle adjustments. Nowadays, web developers expect web browsers to update multiple times a year, instead of the once or twice a year typical of the late 2000s — or the once every two-to-five years typical before then. Over the last several years, you’ve let us know that you want our updates to ship more often. In response, we changed the way Safari is numbered just over two years ago.

A new version of Safari shipped 17 times in the last 28 months — version 15.0, 15.1, 15.2, 15.3, 15.4, 15.5, 15.6, 16.0, 16.1, 16.2, 16.3, 16.4, 16.5, 16.6, 17.0, 17.1, and now, today’s Safari 17.2. This makes it possible to spread out the arrival of new web technology more widely across the year, and get it into the hands of your users that much sooner.

With 39 features and 169 fixes, today’s release is Safari’s biggest December release of web technology ever. Let’s take a look at what’s inside.


Exclusive accordions

Safari 17.2 now supports the name attribute on the <details> element — which moves some commonly-used functionality from needing JavaScript to being baked into HTML. If you use the name attribute to give a series of details elements the same name, then only one in the series will be open at a time.

<details name="foobar" open>
  <summary>Item 1</summary>
  <p>Since this first item has an open attribute, it’s open by default.</p> 
<details name="foobar">
   <summary>Item 2</summary>
  <p>When you clicked this item, any other open item will automatically close.</p>
<details name="foobar">
   <summary>Item 3</summary>
  <p>Now, developers don’t have to write any JavaScript to get this behavior.</p> 

Any time a user clicks to open a second item, the first item automatically closes. This can make for a better user experience, although do make sure it’s right for your project. Think about users who might want or need to compare one item to another. Consider whether to not it will be annoying to have to tap over and over on each bit of content in order to read each one. This pattern can be frustrating when used inappropriately — even inaccessible.

Here’s an example to compare the difference. Try it in a browser with support.

One time codes

Previously supported in Safari through other technology, the ability to have input fields that suggest received one-time codes is now available in WKWebView for iOS 17.2 and iPadOS 17.2.

<label for="onetimecode">Enter code:</label>
<input name="onetimecode" id="onetimecode" type="text" autocomplete="one-time-code" />



Support for CSS Nesting shipped in Safari 16.5, with one caveat. You could not nest element selectors without using an & (or another technique) to make sure every nested line started with a symbol. Starting in Safari 17.2, that restriction is no longer necessary. You can now write:

article {
  h1 { 
    font-size: 1.8rem;
  p {
    font-size: 1.2rem;  

If you would like to continue to use the &, you may. Any code already written with an & will keep working long into the future. In fact, & continues to be an important tool for other nesting techniques. Learn more in CSS Nesting and the Cascade.

New units

If you’ve ever written CSS, you’ve most certainly used the em and rem units for sizing typography and layout. That’s where 1em is equal to the computed value of font-size on the current element, and 1rem is equal to the “root em”, the font size set on the root <html> element. Having both em and rem gives us a lot of flexibility and power in defining consistent sizing across wide portions of the page. Similarly the lh unit shipped with rlh, for line height and root line height.

Safari 17.2 extends the flexibility and power of root units by adding support for rcap, rex, ric, and rch. These units are the equal to the cap, ex, ic, and ch size set on the root <html> element.

What are cap, ex, ic, and ch? Powerful units that refer to different measurements of the glyphs in a font.

Diagram showing cap unit measuring from top to bottom of a capital letter A. The ex unit measures the height of a lowercase letter a. The ch unit is either the height or width of a 0. And the ic unit is either the height or width of the Chinese character for water.

CSS cap is the newest of these units, also now added to WebKit in Safari 17.2. The measurement of 1cap is equal to the cap-height of the first available font.

The ex unit refers to the x-height of the font. The ch unit is equal to the inline size of the zero character in a font — where inline size is the width in a horizontal writing mode, or height in a vertical writing mode. The ic unit stands for “ideographic character” in CJK scripts (for Chinese, Japanese, Korean, and related writing systems). Like ch, the ic unit measures the inline size, in this case where 1ic is equivalent to the width or height of one character. (Since typically all ideographic characters in a CJK font take up the same amount of space, it doesn’t matter which character is measured.)

Safari 17.2 also adds all the typed OM factory functions for font and root font relative units, which can be used to construct CSS typed values from JavaScript.

Motion Path and Shapes

WebKit first shipped support for CSS Motion Path in Safari 16.0, providing web developers the ability to animate objects along a custom path of any shape.

With a rocky start and repeated renaming of properties, support for the offset-* properties was still painfully uneven cross browsers by the end of 2022. Interop 2023 chose Motion Path as a focus area, pulling the attention of the three major browser engines towards improving implementations. This attention also resulted in many changes to the CSS Motion Path specification. Safari 17.2 lands the last of the updates to WebKit needed to match the revised web standard.

Safari 17.2 adds support for:

  • offset-position support for circle() and ellipse()
  • offset-position: normal
  • coord-box and <position> parameters inside ray()
  • rect() and xywh() shapes

The rect() and xywh() shapes also work with clip-path and shape-outside. If you remember, for years we’ve been able to define a shape for clipping or moving content around or along. These shapes have included circle, ellipse, polygon and path. Now, both the rect() and xywh() shapes allow for quick ways to define a rectangle. The rect() function uses four lengths to define the position of the top, right, bottom, and left edges of a rectangle, respectively, as insets from the top and left edges of the reference box. While xywh() takes its inspiration from the viewBox attribute in SVG, and defines the rectangle via offsets from the top and left edge of the reference box, with a specified width and height.

Additionally, for increased usefulness, the size of offset-path shapes are now calculated relative to the containing block instead of the element itself, and take into account the border-radius of the containing block as well.


Safari 17.2 adds support for the linear() function in CSS animations and transitions. It lets you define an easing function that interpolates linearly between a set of points. For example, linear(0, 0.25, 1) produces an easing function that moves linearly from 0, to 0.25, then to 1.

Graph of linear easing, showing a straight line moving up to the right, hitting a point, and turning to a steeper angle, also straight.Diagram of linear easing between multiple points, from the CSS Easing Functions Level 2 web standard.

Math functions

Safari 17.2 also adds support for mixing percentage and length arguments in the CSS Math functions rem(), mod(), round().

CSS Math functions originally shipped in Safari 15.4. The round() function returns a rounded number based on a selected rounding strategy. And the rem() and mod() functions returns a remainder or modulus left over when the first parameter is divided by the second parameter, similar to the JavaScript remainder operator.

Being able to mix percentage-based measurements with other kinds of lengths means, for example, you can set a width to 50% rounded up to the nearest 100px. Or in this demo, rounding 100% down to the nearest 8ric.

body {
  --rounding-interval: 8ric;
aside {
  width: round(down, 100%, var(--rounding-interval));

Try it out, resizing the window to see the effect. The box is fluid, but jumps from 80ric to 88ric to 96ric wide, and so on, skipping over the sizes in-between.


In September, Safari 17.0 improved WebKit’s support for CSS Counters by providing the ability to style counters through @counter-style. Now, in Safari 17.2, WebKit adds support for the counter-set property, providing the ability to change the number in the count without creating a new counter. (That’s what counter-reset does, create a new counter scope.)

Safari 17.2 also adds basic support for the list-item value to the counter-set, counter-reset and counter-increment properties, allowing manipulation of the automatic numbering provided by display: list-item. While list-item has been available inside both counter() and counters() for some time, now you can override the value through counter-set: list-item X and counter-increment: list-item X (where “X” is a number of your choosing), instead of just reading the value.

Learn more about CSS Counters in What’s New in CSS from WWDC23.

Mask border

For years, -webkit-mask-box-image has provided a way to apply a mask to the edges of an element, through its border. This idea was originally implemented by WebKit, and ended up in the Chromium-based browsers as well. Now in Safari 17.2, we are proud to be the first browser to unprefix this property, and officially make it mask-border.

The mask-border property is a shorthand for six properties:

Use mask border to create complex custom border edges, and mask the edge of the box in a unique fashion.

The values for mask-border work the same fashion as border-image.

Custom Highlights

Safari 17.2 adds support for the Custom Highlights API. There are pseudo-elements for styling certain kinds of UA-provided highlights, like ::selection. Now you can create your own highlights for any arbitrary Range objects in JS, and style them with the new CSS ::highlight() pseudo-element, allowing for custom highlighting of Web content without altering the markup or DOM.

The highlight pseudo-elements support text decorations like underlining and shadows as well as color and background-color changes.

Images and video

Responsive images

Safari 17.2 adds support for preloading responsive images. This code asks the browser to assess which size image is the appropriate one to use from the list of options, and to preload that one image.

  <title>Your page title</title>
  <!-- other items -->
  <link rel="preload" 
        imagesrcset="flowers-small.jpg 400w, 
                     flowers-medium.jpg 800w, 
                     flowers-large.jpg 1200w,
                     flowers-extralarge.jpg 1500w" 

The imagesrcset and imagesizes attributes work just like the srcset and sizes attributes from familiar responsive image techniques.

Image Orientation

When rendering an image on a web page, Safari uses the EXIF metadata in the image file, when available, to determine the image’s correct orientation. This process ensures that images are displayed as intended by their creators.

In earlier versions, this automatic orientation behavior was represented by the term "none" in the ImageBitmapOptions’s imageOrientation property. In Safari 17.2, to better reflect the actual functionality, this keyword is changed to "from-image". The previous term, "none", is now considered deprecated. It’s important to note that this change is a renaming for clarity and does not introduce new functionality.

Additionally, there is an ongoing discussion about reintroducing "none" with a literal interpretation in future updates to HTML. However, this proposal is still under consideration, mainly due to potential issues with backward compatibility.


Safari 17.2 adds support for SVG <image crossorigin>, enabling read back when CORS-equipped cross-origin images are drawn on canvas.

Safari 17.2 also adds support for the missing default value translate for animateTransform.


Safari 17.2 adds support for H264 L1T2 to WebCodecs. WebKit originally shipped support for video WebCodecs in Safari 16.4. It gives web developers complete control over how media is processed by providing low-level access to the individual frames of a video stream. It’s especially useful for applications that do video editing, video conferencing, or other real-time processing of video. By adding WebCodecs support for H264 L1T2 in Safari 17.2, developers now have more flexibility in which formats they want to offer to their users.

Media element

Previous only available as webkitPreservesPitch, Safari 17.2 adds support for the unprefixed version of HTMLMediaElement.preservesPitch. This property determines whether or not the browser should adjust the pitch of the audio to compensate for changes to the playback rate made in HTMLMediaElement.playbackRate.

And Safari 17.2 also adds support for automatic text track selection for 'metadata' tracks.


Import attributes

Safari 17.2 adds support for import attributes. It provides a way to add type information to module import statements, enabling you to import JSON modules like this:

import json from "./foobar.json" with { type: "json" };
import("foobar.json", { with: { type: "json" } });

Number Format

Safari 17.2 adds support for Intl.NumberFormat’s FormatApproximately operation. The formatRange() method of Intl.NumberFormat instances formats a range of numbers according to the locale and formatting options of this Intl.NumberFormat object. For example, a format range such as formatRange(3, 5) for the USD currency will be rendered as “$3.00 – $5.00”. If the two numbers are very close to each other, the FormatApproximately feature will add a tilde sign in front of the number, so that formatRange(2.9, 3.1) will be represented as “~$3”, aka approximatively 3 USD.


Fetch Priority

Safari 17.2 adds support for Fetch Priority. It allows developers to set the priority of a resource (e.g., an image, script, or linked resource such as a style sheet) in relation to other resources. This can be done via the fetchpriority HTML attribute or as an option passed via the Fetch API. The supported values are “high”, “low”, or “auto” (default). For example:

  <!-- Prioritize this! -->
  <img fetchpriority="high" alt="I'm really important!" src="logo.png">
  <!-- it's ok if this image loads later, it's below the fold -->
  <img fetchpriority="low" alt="I'm not very important" src="social.png">

And via the Fetch API, you can do:

async function fetchHighPriorityData(){
  const response = await fetch("important/data.json", { priority: "high" });
  // process the high priority response data...

Forms validation

Safari 17.2 adds support for the title pattern attribute for validation errors. When set, it shows the value of the title attribute when a validation error occurs. This is active only if you submit the form or call reportValidity on the event of your choice.

<form action="/login" id="form">
    title="Must be shorter than 10 characters and lowercase only">
const form = document.getElementById("form");
form.addEventListener("keypress", () => form.reportValidity());
Password input field with error message in a bubble above, reading


Safari 17.2 adds support for CanvasRenderingContext2D.prototype.reset(), which resets the canvas context to its default state (i.e., it clears it to transparent black along with setting everything back related to the canvas to its default state).

DOM Events

Safari 17.2 adds support for sending certain mouse events to disabled form controls. Disabled form controls now receive mouseenter, mouseleave, mousemove, mouseover, and mousewheel events. A disabled form controls continue to not receive click, mouseup, and mousedown events.

Web Apps

Login cookies

When we brought web apps to Mac, we designed the experience of “Add to Dock” so that macOS copies the website’s current cookies over to the new web app. That way, if someone is logged into their account in the browser, they will remain logged in within the web app. They don’t have to log in again.

Now, that same behavior works on iOS 17.2 and iPadOS 17.2. Whether a user is in Safari, Safari View Controller, or another browser on iPhone or iPad, when they tap “Add to Home Screen” and create a web app, the cookies are copied over, including the information about login state. This will only work if the authentication state is stored within cookies. No other kind of local storage is copied over.

After a user adds a web app to the Home Screen or Dock, no other website data is shared, which is great for privacy.

Web App icons

On macOS, users expect app icons to look sharp at a variety of sizes, ranging from 16x16px to 1024x1024px. With Safari 17.2 on macOS Sonoma, we have improved how web app icons are loaded, resolving many common issues such as icons appearing blurry with jagged edges, lacking padding, or failing to load. If you supply multiple size variants for the preferred icon kind, all sizes are saved on macOS, allowing the most appropriate size to be displayed based on context. For example, the largest size variant is shown in Finder’s Gallery view and Quick Look, a medium sized variant is shown in the Dock and Launchpad, while a smaller sized variant is shown in Spotlight.

To provide the best user experience on macOS, supply at least one opaque, full-bleed maskable square icon in the web app manifest, either as SVG (any size) or high resolution bitmap (1024×1024). If the web app manifest doesn’t specify any high resolution full-bleed icon, Safari may fall back to the apple-touch-icon if it improves the user experience. If you specify apple-touch-icon through link elements on the webpage, do not omit these link elements based on user agent detection. When you only supply a transparent icon with custom shape, Safari automatically adjusts spacing between your icon and the standard system background on macOS to avoid tangents.

User options

Now, in a web app on Mac, you can enable the status bar by going to View > Show Status Bar. Once you do so, anytime you hover over a link, the URL will appear in the bottom left of the web app window.

Also new to web apps on Mac, a user can just click a button to easily change which web page is loaded by default in new web app windows. Navigate to the page, open Settings, and click “Set to Current Page”.

Settings panel for a web app. Showing the button to


Safari 17.2 adds support for two new WebGL extensions, EXT_blend_func_extended and WEBGL_clip_cull_distance.


Safari 17.2 adds blob partitioning. Blobs are now partitioned by top-level site to prevent them from being a possible cross-site tracking vector.

Web Inspector

Color Palette with color variables

Many sites today use CSS Custom Properties to define a set of variables for colors used on the site. But it can be hard to remember what the variable names are, and annoying to have to go look them up.

Color picker showing four rows of boxes at the bottom — each box holding a different color that's been defined in a CSS variable by the web developer.

The Color Picker, shown whenever you click on an inline color swatch in the Styles sidebar panel, now provides a color palette with all the applicable color variables for the selected element. This makes it easy to see at a glance all of the predefined colors intended to be reused. Click on any of the color swatches from the “Variables” color palette use the corresponding color variable.


The progression of a CSS animation or transition is controlled by a timing function, which defines aspects like acceleration, deceleration or smoothness of playback. Web Inspector shows an inline swatch next to animation-timing-function and transition-timing-function CSS properties Styles sidebar panel. Clicking on the swatch triggers a specialized editor which offers a preview of the effect of the given timing function value as well as input controls for adjusting specific parameters.

Web Inspector already provides previews and editing of timing function values like cubic-bezier(), linear , ease-in , ease-out and ease-in-out . With the introduction of the steps() timing function in Safari, there’s now a specialized preview and editor for it in Web Inspector.

Screenshot of animation timing function steps editor, in Web Inspector. A dropdown box that shows the shape of the timing function — it looks like a set of steps in a staircase.

A CSS declaration like animation-timing-function: steps(5) tells the browser to split the animation time in five segments of equal length and play it in discrete steps instead of a smooth progression, an effect similar to stop motion animation. The specialized editor for the steps() timing function shows the effect preview as a step-like illustration and provides controls to adjust the number of steps and the step position.

Fixes for Interop 2023 and more

WebKit for Safari 17.2 also includes a lot of bug fixes and feature polish. Many of these fixes are part of our contribution to improving the 26 focus areas of Interop 2023.

The Interop Project is an ongoing collaboration between Apple, Bocoup, Igalia, Google, Microsoft, and Mozilla to focus on a specific set of web technologies and get them to work exactly the same in every browser. At the beginning of 2023, less than 49% the selected automated tests were passing in the released versions all three major browser engines. Now over 93% of them pass in the preview versions.

Current Interop 2023 scores: Chrome, 99%. Firefox, 95%. Safari 97%.Graph of Interop 2023 scores, starting at 59-81% and going up across the year to 93-99%.

This fall, as part of Interop 2023, subgrid and CSS Masking shipped in Chrome, while :has() and @property shipped in Firefox. Since our last release, Safari greatly improved our implementation of Pointer and Mouse events, added support for more codecs to Web Codecs, re-engineered CSS Motion Path, and dedicated a significant portion of our WebKit engineering work to fixing dozens of remaining test failures scattered across the focus areas.

You can learn more about the accomplishments of Interop 2023 by studying the Interop dashboard.

Resolved Issues


  • Fixed parsing the ARIA role attribute to ignore leading and trailing whitespace, including line breaks. (113923520)
  • Fixed form landmarks being incorrectly exposed when they are missing a label. (115462091)
  • Fixed non-group and non-tree-item children of role="treeitem" elements becoming stale after dynamic changes. (115936550)
  • Fixed Play Animation and Pause Animation animated image context menu items sometimes not appearing after setting toggle. (117215059)
  • Fixed VoiceOver not announcing button labels if the button is in a shadow root. (118118138)

Apple Pay

  • Deprecated enabled in favor of available in shippingContactEditingMode. (113159800)


  • Fixed clipping the “Strong Password” button after selecting “Suggest New Password”. (113701243)


  • Fixed the font-family descriptor for @font-palette-values to accept multiple values. (105975619)
  • Fixed :has(:scope) matching. (106524140)
  • Fixed container selection for container units in pseudo-elements. (106739553)
  • Fixed container query with font units to invalidate when the font changes. (106739736)
  • Fixed :nth-child() invalidation when not in subject position. (106740353)
  • Fixed :has(:host) invalidation. (106768205)
  • Fixed :has(:nth-child()) invalidation and related. (106768224)
  • Fixed invalidating scope-breaking :has(:is(...)) selectors. (106768250)
  • Fixed handling dynamic updates to viewport units when used in @property initial value. (108287215)
  • Fixed baseline aligned flex items to also be aligned using their fallback alignment. (109496710)
  • Fixed: Changed to allow an empty font-family name in @font-face and @font-palette-values. (109613703)
  • Fixed the <basic-shape> implementation for offset-path. (110565070)
  • Fixed :user-invalid and :user-valid interactions with form reset and submission. (110677832)
  • Fixed <coord-box> implementation for offset-path. (110938788)
  • Fixed <position> to never serialize to a single value. (111750372)
  • Fixed NaN numeric representation to be 0 not Infinity. (111984451)
  • Fixed serializing CSS math function root nodes. (111984509)
  • Fixed min() and max() with one argument to always collapse to calc(). (111986569)
  • Fixed animation using padding-block or padding-inline not overriding the set padding style. (112023856)
  • Fixed color-mix() to respect :visited style to resolve “currentcolor”. (112419198)
  • Fixed serialization to always serialize implicit & and an implicit nested rule. (112900363)
  • Fixed <hr> width attribute set to 0 or 0px to correctly compute to 0px. (113087533)
  • Fixed mod() evaluation. (113213059)
  • Fixed round() evaluation when the number is a multiple of the step. (113233588)
  • Fixed computation of the from-font value for font-size-adjust. (113328110)
  • Fixed the serialization of percentages in color-mix(). (113399146)
  • Fixed border-image to fall back to the border property if the image is invalid. (113646392)
  • Fixed <family-name> to forbid generic families. (113746537)
  • Fixed the check for in-progress layout when setting a containing block rect for ray() used with motion-path. (113780201)
  • Fixed animating a rotate property when the scale property is also used. (113999490)
  • Fixed <resolution> to not accept negative resolutions for @property. (114235642)
  • Fixed currentcolor to correctly inherit computed :visited style. (114254856)
  • Fixed nested subgrids from contributing to the calculation of the enclosing track size. (114271839)
  • Fixed container-name to use scoped names. (114284428)
  • Fixed container unit resolution to check if the selected container is eligible. (114291153)
  • Fixed the scripting media query to never match initial-only. (114340361)
  • Fixed form submission to also affect :user-invalid and :user-valid state. (114580382)
  • Fixed the container for the ::part pseudo-element to be selected from the originating element tree. (114626579)
  • Fixed serialization of infinity and -infinity in colors. (114808320)
  • Fixed lab, lch, oklab, oklch components to be clamped to appropriate ranges. (114808444)
  • Fixed color-mix() to not serialize to legacy color syntax. (114949008)
  • Fixed resolving the size of a replaced element by using its intrinsic size as the width. (115007278)
  • Fixed basic shapes to use an offset motion path. (115068196)
  • Fixed grid to not always put first and last baseline aligned items into different alignment contexts. (115083708)
  • Fixed determining non-orthogonal grid item’s columnAxisPosition by considering fallback alignment for first/last baseline. (115136343)
  • Fixed :has(~ :is(.x ~ .y)) to consider all siblings of the :has scope when invalidating. (115205991)
  • Fixed invalidating :default pseudo-class changes on input elements. (115236525)
  • Fixed calc(clamp(1px, 1em, 1vh)) to collapse to clamp(1px, 1em, 1vh). (115240159)
  • Fixed offset path inset shapes with a border-radius. (115316728)
  • Fixed determing baseline for grid by considering the first and last baseline-aligned grid items. (115441833)
  • Fixed the serialization of the computed style of grid-area. (115521493)
  • Fixed not serializing at <position> in circle() or ellipse() if unspecified. (115866108)
  • Fixed serialization of shape-outside. (115938310)
  • Fixed serialization issues with clip-path and offset-path. (115953688)
  • Fixed getComputedStyle() to return a resolved value for font-size-adjust: from-font. (116151111)
  • Fixed non-orthogonal subgrid margin, border, and padding to be considered for self-align baseline items in the same alignment context. (116206243)
  • Fixed subgrids to have their row-start margins resolved after column sizing in the outer grid. (116369419)
  • Fixed accumulating the sum of non-orthogonal nested subgrids margin, border, and padding for first baseline alignment in the column axis. (116443747)
  • Fixed CSS grid support for last baseline alignment in the column axis for subgrid items with non-orthogonal ancestors. (116484865)
  • Fixed validating @property at parse-time. (116803886)
  • Fixed computing the definite free space of grid rows when the grid has an aspect-ratio and definite logical width. (117138268)
  • Fixed the continuity of transform animations through singular transforms. (117209302)
  • Fixed @supports selector(:popover-open) to reflect disabled state. (117226626)
  • Fixed CSS grid to synthesize the central baseline of grid items in the column axis. (117424263)
  • Fixed serialization for CSS highlight pseudo-elements. (117864974)


  • Fixed handling tasks scheduled for web pages in the back-forward cache. (116349535)
  • Removed the non-standard incremental attribute and search event. (48937114)


  • Fixed COLRv0 font rendering. (115721319)


  • Fixed <input type="number"> not returning the correct value when a decimal is entered. (107187010)
  • Fixed alert sounds in web apps being replayed when the system play/pause key is pressed. Playing short-duration <audio> sources no longer registers the page as the system’s Now Playing application. (114667001)
  • Fixed dynamic handling of <base> elements. (114756660)
  • Fixed URL encoding of <base> elements. (114861187)
  • Fixed URL encoding of SVG <image> elements. (114873373)
  • Fixed empty value attributes to not be ignored on image input types. (114887143)
  • Fixed [dir=auto] invalidation with password fields. (115887776)


  • Fixed COOP header breaking back and forward behavior when client-side redirects are involved. (104659192)


  • Fixed an edge case in the semantics of for loops. (44730906)
  • Fixed: Optimized Array#splice to skip result array creation if it is not used at all. (113367762)
  • Fixed: Updated Intl.DateTimeFormat‘s to obtain options only once, matching spec changes. (113789192)
  • Fixed: Increased minimumFractionDigits and maximumFractionDigits limit from 20 to 100. (113869343)
  • Fixed rounding to nearest in optimizing JITs. (114208146)
  • Fixed global and eval code to throw a TypeError if a function declaration attempts to shadow a non-configurable, non-writable global property. (114215396)
  • Fixed Intl.NumberFormat and Intl.PluralRules roundingIncrement to match specification changes. (114219889)


  • Fixed navigation to about scheme URLs without opaque paths. (116238322)


  • Fixed an issue where Safari would briefly change document.visibilityState to hidden when entering fullscreen. (104984915)
  • Fixed canplay event to fire for video elements where the first sample’s presentation time is slightly greater than 0. (105169372)
  • Fixed RTCRtpSender maxFramerate encoding parameter having no effect. (112397603)
  • Fixed handling NaN in audio delay curves. (114881060)
  • Fixed WebCodecs hardware encoders losing a frame. (115252749)
  • Fixed audio elements with event listeners not getting garbage collected. (116346717) (FB13224538)
  • Fixed the close algorithms for audio and video WebCodec decoders and encoders to match specification changes. (116346725)
  • Fixed picture-in-picture when the srcObject is a video stream. (116465668)
  • Fixed constraints on the maximum width or height causing blurry getDisplayMedia video. (116810370)
  • Fixed object-fit: fill to work for a video element using a canvas stream srcObject. (116832514)
  • Fixed the limit for the number of real-time audio threads. (116864442)


  • Fixed an issue causing embedded videos in iWork documents to fail. (116493190)


  • Fixed the scrollbar not updating on CSS color-scheme change. (99567600)
  • Fixed ignoring calc() values on <colgroup> elements. (106692191)
  • Fixed out-of-flow boxes not showing. (112733052) (FB12722063)
  • Fixed out-of-flow <br> to not trigger a line break. (113208050)
  • Fixed ancestor subgrids’ gutters to add to the extra layer of margin for descendant subgrids. (114271857)
  • Fixed a bug where swapping to Safari from another app (or tab) would flash black. (116530284)


  • Fixed website notifications delivered through APNS to appear with the domain name and Safari icon, matching the appearance of website notifications delivered through Web Push. (116612341)

Safari Extensions

  • Fixed an issue where dynamic declarativeNetRequest rules would not override static rules. (107044339) (FB12074742)
  • Fixed behavior of domains, requestDomains, excludedDomains, and excludedRequestDomains declarativeNetRequest values to match subdomains by default. (117592996)


  • Fixed clicking and dragging the overlay scrollbar that overlaps a composited, positioned descendant of a container with overflow: scroll. (89598421)
  • Fixed scrolling on nested pointer-events: auto inside pointer-events: none. (110954175)
  • Fixed a bug that caused some complicated websites to freeze when scrolling. (113318934)

Service Workers

  • Fixed a cache miss bug in DOMCache that triggered service worker fetch errors. (115740959) (FB13188943)


  • Fixed the motion path anchor point used for SVG when the transform-box is not the view-box. (108285569)
  • Fixed paint-order property to inherit. (114030037)
  • Fixed the SVG mask to work as a mask-resource for the CSS mask-image. (114465545)
  • Fixed repainting an SVG element with a CSS reference filter when the filter changes. (117047658)


  • Fixed font fallback to ignore generic families for Private-Use Area Unicode codepoints. (115901340) (FB13197885)

Web Animations

  • Fixed color-scheme to support discrete animation. (94615599)


  • Fixed createPattern to return null for a zero height image. (104285727)
  • Fixed: Aligned <script type language> with the HTML Standard. (109600797)
  • Fixed returning opaque origin for blob: URL containing inner non-http(s): URL. (109781193)
  • Fixed: Changed navigable target names to _blank if they have dangling markup. (110134016)
  • Fixed incorrect tab stop if the tab-size is a <length> and the distance to the next tab stop is less than 0.5ch. (112043546)
  • Fixed URL, pathname, and search setter incorrectly stripping trailing spaces. (112433299)
  • Fixed custom highlight text decoration to respect priority. (112494779)
  • Fixed handling focusability for plugin elements which have browsing context. (112821601)
  • Fixed: Converted embed hidden into a proper boolean attribute. (113051256)
  • Fixed edge cases in parsing options. (113826514)
  • Fixed <a> and <area> origin getters to return an empty string for non-parsable URLs. (114078288)
  • Fixed <a> and <area> protocol setters for non-parsable URLs. (114371380)
  • Fixed URL’s protocol setter to forbid change a special URL to a non-special URL. (114624048)
  • Fixed Worker and SharedWorker to fire an Event instead of an ErrorEvent for a parsing error. (114694487)
  • Fixed adoptedStyleSheets.length to be settable and improved ObservableArray alignment with the specification. (114822538)
  • Fixed a bug that could cause incorrect equality checks between DOM Document objects. (114857465)
  • Fixed checking for NaN when creating a DelayNode for WebAudio. (115008784)
  • Fixed element.querySelector(":has(:scope *)") to never match. (115158183)
  • Fixed mutation events for child nodes. (115527098)
  • Fixed mouse event handling such that if a drag operation is initiated from a canceled mousedown event, all subsequent mouse events are sent to the originating frame until the drag operation ends with a corresponding mouseup event. (116668701)
  • Fixed light dismiss for a popover element within a complex shadow DOM breaks light dismiss calculation. (117214343)

Web Apps

  • Fixed an issue where page zoom is reset to 100% after quit and relaunch. (110298546) (FB12233006)
  • Fixed a bug where theme-color is not applied to the title bar in web apps. (112980819)
  • Fixed an issue where sign in pages sometimes unexpectely open in Safari instead of the web app. (113520837)
  • Fixed an issue where clicking on notifications after 30 seconds from delivery fail to open the web app. (113757950)
  • Fixed an issue that repeatedly asks for camera access after relaunching a web app. (114110664)
  • Fixed remembering window size for a webpage added to the Dock. (114534506)
  • Fixed an issue where a blank window remains on screen after starting a download. (115457207)
  • Fixed an issue where some login pages unexpectedly open in Safari. (115527738) (FB13171758)
  • Fixed a bug where the scope member in the web app manifest is not respected. (116261588)
  • Fixed an issue where AutoFill settings in Safari do not take effect in web apps. (117671220)
  • Fixed an issue where option+clicking a link failed to start a download. (117809013)
  • Fixed an issue where JavaScript-based redirection to an external website causes a blank window to appear or the current window to disappear. (117809066)
  • Fixed an issue where web app usage is not reflected in Screen Time. (117809075)
  • Fixed an issue that prevents Ignore Screen Time Limits from working in web apps. (117809075)

Web Assembly

  • Fixed WebAssembly SIMD vectors that can get corrupted when using v128.any_true. (111050621)

Web Inspector

  • Fixed: Moved the details sidebar to the bottom when Web Inspector is too narrow. (63567675) (FB7711657)
  • Fixed objects logged to the console with multiple private fields that use the same name. (109215331)
  • Fixed broken search functionality. (113714342)


  • Fixed dispatched mouse events always having buttons property set to zero. (116049187)


  • Fixed a bug where multi-level textures would lose levels in WebGL. (116362216)


  • Fixed long delays switching audio input in video conferencing applications. (102724364)
  • Fixed video quality when using TransformStream with Simulcast. (110395571)
  • Fixed WebRTC UDP traffic to use interfaces already used by TCP traffic. (111000448)
  • Fixed RTCDataChannel to use BinaryType to align with specifications. (114559008)

Updating to Safari 17.2

Safari 17.2 is available for iOS 17, iPadOS 17, macOS Sonoma, macOS Ventura and macOS Monterey.

If you are running macOS Ventura or macOS Monterey, you can update Safari by itself by going to Software Update, and clicking “More info”. On macOS Ventura, that’s  > System Settings > General > Software Update > More info. To get the latest version of Safari on your iPhone or iPad, go to Settings > General > Software Update, and tap to update.


We love hearing from you. To share your thoughts on Safari 17.2, find us on Mastodon at @jensimmons@front-end.social and @jondavis@mastodon.social. Or send a reply on X to @webkit. If you run into any issues, we welcome your feedback on Safari UI, or your WebKit bug report about web technologies or Web Inspector. Filing issues really does make a difference.

Download the latest Safari Technology Preview to stay at the forefront of the web platform and to use the latest Web Inspector features.

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

December 11, 2023 06:00 PM

December 07, 2023

Release Notes for Safari Technology Preview 184

Surfin’ Safari

Safari Technology Preview Release 184 is now available for download for macOS Sonoma and macOS Ventura. 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: 270234@main…271110@main.


Resolved Issues

  • Fixed VoiceOver not announcing the button label if the button is in a shadow root. (270542@main) (118118138)


New Features

  • Added support for CSS custom properties on dialog ::backdrop (270246@main) (117949961)

Resolved Issues

  • Fixed visited color to fallback. (270270@main) (115289075)
  • Fixed block size with max-content and min-content in a table. (271054@main) (116264670)
  • Fixed the continuity of transform animations through singular transforms. (270294@main) (117209302)
  • Fixed content-visibility to not apply to tables. (270888@main) (117914336)


Resolved Issues

  • Fixed memory growth with lazy loaded images outside the viewport. (270745@main) (117683012)
  • Fixed blocking JavaScript reading nonce for <style> and <link>. (271046@main) (118676659)


New Features

  • Enabled Array group methods. (270350@main) (118037635)
  • Added support for ArrayBuffer.prototype.detached, ArrayBuffer.prototype.transfer, and ArrayBuffer.prototype.transferToFixedLength. (270349@main) (118037759)

Resolved Issues

  • Fixed Temporal API to throw TypeErrors for unexpected primitives. (270262@main) (117992134)
  • Fixed Temporal options handling to align with the specification. (270360@main) (118088676)
  • Fixed Temporal.Now.timeZone() to be updated to timeZoneId(). (271003@main) (118674314)


New Features

  • Added support for all of HTML’s character entities in WebVTT. (270240@main) (51064890)

Resolved Issues

  • Fixed the always empty video.buffered attribute. (270931@main) (118550061)
  • Fixed WebVTT to correctly parse region id settings. (270868@main) (118551267)
  • Fixed VideoEncoder produces no frames with latencyMode “realtime” when framerate/bitrate are not given. (271087@main) (118725549)
  • Fixed in-band captions wrapping unnecessarily. (271415@main) (119024855)

Web Animations

Resolved Issues

  • Fixed style invalidation for animations. (270890@main) (118500247)


New Features

  • Added support for AbortSignal.any(). (270268@main) (117985827)
  • Added support for element.checkVisibility(). (270425@main) (118157977)

Web Assembly

New Features

  • Enabled extended constant expressions. (270475@main) (118190467)

Web Inspector

New Features

  • Added support for grouping source map load errors. (270834@main) (109239646)
  • Added an editor for the CSS linear() timing function. (270855@main) (115095425)

Resolved Issues

  • Fixed the tan() function to not trigger the color picker. (271075@main) (118724061)


New Features

  • Added support for new WebGL extensions:
    • EXT_clip_control
    • EXT_depth_clamp
    • EXT_polygon_offset_clamp
    • WEBGL_polygon_mode (270382@main) (118110035)

December 07, 2023 11:22 PM