Safari Technology Preview Release 147 is now available for download for macOS Monterey 12.3 or later and macOS Ventura beta. Updates to Safari Technology Preview are no longer available for macOS Big Sur. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS Monterey, or System Settings under General → Software Update on macOS Ventura.
Note: Shared Tab Groups and syncing for Tab Groups, Website Settings, and Web Extensions are not enabled in this release.
Many of the new Safari 16 features are now available in Safari Technology Preview 147:
Live Text. Select and interact with text in videos or translate text in images on the web in macOS Ventura beta on Apple Silicon-based Macs.
Web Push. Send notifications to people who opt-in on your website or web app with Safari Technology Preview on macOS Ventura beta.
Passkeys. Preview the new type of phishing-resistant credential that makes signing in to websites safer and easier. Available through Safari’s WebAuthn platform authenticator. To learn more about passkeys, see Meet passkeys.
Improved Safari Web Extensions. Test out API improvements including the ability to open a Safari Web Extension popover programmatically.
Web Inspector Extensions. Build custom tooling or convert existing developer tools extensions to use in Web Inspector.
Flexbox Inspector. Use the new visualization overlay in Web Inspector to help you more quickly and easily understand the layout of elements with Flexbox. It marks both the free space and gaps between flex items to reveal how they affect the result.
If you see bugs or unexpected behavior with the interface of Safari Technology Preview, please file feedback with Apple’s Feedback Assistant. If you come across an implementation bug in web technology, Web Inspector, or have a request, please file a ticket on the WebKit bug tracker.
I mentioned in a previous post that Igalia organized the Web Engines Hackfest 2022 last week. As usual, fonts were one of the topic discussed. Dominik Röttsches presented COLRv1 color vector fonts in Chrome and OSS (transcript) and we also settled a breakout session on Tuesday morning. Because one issue raised was the availability of OpenType MATH fonts on operating systems, I believe it’s worth giving an update on the latest status…
There are only a few fonts with an OpenType MATH table. Such fonts can be used for math layout e.g. modern TeX engines to render LaTeX, Microsoft Office to render OMML or Web engines to render MathML. Three of such fonts are interesting to consider, so I’m providing a quick overview together with screenshots generated by XeTeX from the LaTeX formula $${\sqrt{\sum_{n=1}^\infty {\frac{10}{n^4}}}} = {\int_0^\infty \frac{2x dx}{e^x-1}} = \frac{\pi^2}{3} \in {\mathbb R}$$:
Cambria Math is a proprietary font installed by default on Microsoft Windows.
Recently, Igalia has been in touch with Myles C. Maxfield who has helped with internal discussion at Apple regarding inclusion of STIX Two Math in the list of fonts on macOS. Last week he came back to us announcing it’s now the case on all the betas of macOS 13 Ventura 🎉 ! I just tested it this morning and indeed STIX Two Math is now showing up as expected in the Font Book. Here is the rendering of the last slide of my hackfest presentation in Safari 16.0:
Obviously, this is a good news for Chromium and Firefox too. For the former, we are preparing our MathML intent-to-ship and having decent rendering on macOS by default is important. As for the latter, we could in the future finally get rid of hardcoded tables to support the deprecated STIXGeneral set.
Another interesting platform to consider for Chromium is Android. Last week, there has been new activity on the Noto fonts bug and answers seem more encouraging now… So let’s hope we can get a proper math font on Android soon!
Finally, I’m not exactly sure about the situation on Linux and it may be different for the various distributions. STIX and Latin Modern should generally be available as system packages that can be easily installed. It would be nicer if they were pre-installed by default though…
Websites have many reasons to notify their users of time-sensitive or high-priority events, even if the user does not currently have the site open. This feature is called Web Push, and is enabled by the W3C standards for Push API, Notifications API, and Service Workers, all working together. WebKit now supports the relevant parts of those standards to enable Web Push.
Apple has made changes to macOS that deeply integrate with WebKit’s support to provide a great user experience, and we’re excited to announce that Web Push is supported in Safari 16 on macOS Ventura.
Keep an eye out for Web Push on iOS and iPadOS in 2023.
As long as you’ve coded your web application to the standards you will be able to reach Safari 16 users on macOS Ventura. You don’t need to join the Apple Developer Program to send Web Push notifications.
If you exclude Safari through browser detection, now would be a great time to switch to feature detection, which lets you take advantage of new features as soon as they’re supported. Additionally, if you tightly manage push end points on your server, be sure to allow URLs from any subdomain of push.apple.com.
All of this and more is covered in Meet Web Push (15 minute video) at WWDC22.
Standards overview
Most features of the web platform are described in a single web standard. Web Push is an exception, with multiple standards describing implementation requirements.
There are many resources on the web to help web application authors get up and running with these standards. But to further cover how WebKit’s support works, it is useful to cover the web standards at a high level.
The Push API standard is the most directly relevant to start with. It describes the JavaScript interface that allows a website to register a push subscription. That subscription enables sending push messages to your user’s browser using a push service.
The ServiceWorker API is extended to support these push messages. Once a push message is received from a domain, that domain’s registered service worker script receives an event representing the push message.
The Notifications API is extended to allow service worker scripts to post a notification even without an open browser tab.
When a web application registers a push subscription, they promise that pushes will always be user visible. When the service worker handles a push message, it is required to use the Notifications API to display a user visible notification. Finally, when the user activates that notification, the service worker is sent an event representing the notification activation.
Power and privacy
Both the WebKit open source project and Apple treat privacy as a fundamental human right. As with other privileged features of the web platform, requesting a push subscription requires an explicit user gesture. It also requires you set the userVisibleOnly flag to true, and fulfill that promise by always showing a notification in response to a push message.
The Web Push API is not an invitation for silent background runtime, as that would both violate a user’s trust and impact a user’s battery life.
Violations of the userVisibleOnly promise will result in a push subscription being revoked.
A little bit about WebKit
Some of you are interested in the implementation details of Web Push in WebKit.
One goal of the WebKit open source project is to make it easy to deliver a modern browser engine that integrates well with any modern platform.
Many web-facing features are implemented entirely within WebKit, and the maintainers of a given WebKit port do not have to do any additional work to add support on their platforms.
Occasionally features require relatively deep integration with a platform. That means a WebKit port needs to write a lot of custom code inside WebKit or integrate with platform specific libraries. For example, to support the HTML <audio> and <video> elements, Apple’s port leverages Apple’s Core Media framework, whereas the GTK port uses the GStreamer project.
A feature might also require deep enough customization on a per-Application basis that WebKit can’t do the work itself.
For example web content might call window.alert(). In a general purpose web browser like Safari, the browser wants to control the presentation of the alert itself. But an e-book reader that displays web content might want to suppress alerts altogether.
From WebKit’s perspective, supporting Web Push requires deep per-platform and per-application customization.
Web Push in Apple’s WebKit port
Apple’s WebKit port includes a new daemon called webpushd. It is installed as a LaunchAgent in macOS Ventura to support Web Push. This daemon takes push subscription requests from webpages in Safari 16 and turns them into actual push subscriptions with the Apple Push Notification service.
Incoming pushes to the system are delivered to webpushd, which then wakes the appropriate application to hand off any pending push messages to a service worker.
The promise of Web Push is that you can reach your users even if they don’t have your website open in a browser tab. Because of how we integrated webpushd with built-in push support in macOS Ventura, Safari doesn’t even need to be running for a push message to be delivered.
The requirement to display user visible notifications is another platform specific point. Different browsers might implement Notifications API support in different ways. Safari has always supported local notifications by relying on the macOS Notification Center and has made additional changes to handle activating these notifications when Safari is not running.
Integrating Apple Push Notification service’s new Web Push support with webpushd and supporting notifications while Safari isn’t running are both system-level changes, making our implementation require macOS Ventura and later.
More resources
Apple has a few more resources to learn more about Web Push support in Safari 16 on macOS Ventura:
WWDC22 is here, and with it, a host of announcements of new web technology shipping in WebKit on macOS, iOS and iPadOS, including advancements in privacy and security – plus new features for Safari, Web Inspector and Safari Web Extensions. Much of the news was announced on Monday during this year’s keynote, is listed in the Safari 16 Beta Release Notes, and is described in News from WWDC: WebKit Features in Safari 16 Beta. But that’s not all.
Ten sessions at WWDC22 go into greater detail, demonstrating new technology directly relevant to web developers. New videos will be released each day this week. You can watch them on the WWDC22 website, or in the Apple Developer app for macOS, iOS, iPadOS, and tvOS.
Be part of the conversation during WWDC on the Apple Developer Forums, or share your thoughts with @WebKit on Twitter.
Explore the latest features in Safari and WebKit and learn how you can make better and more powerful websites. We’ll take you on a tour through the latest updates to HTML, CSS enhancements, Web Inspector tooling, Web APIs, and more.
Bring better notifications to your websites and web apps in Safari on macOS with Web Push. We’ll show you how you can remotely send notifications to people through the web standards-based combination of Push API, Notifications API, and Service Workers.
It’s time for a security upgrade: Learn how to add support for passkeys to create a quick and easy sign in experience for people, all while offering a radical increase to account security. Passkeys are simple and strong credentials built to eliminate phishing attacks. We’ll share how passkeys are designed with security in mind, show you how people will use them, go over how to integrate passkeys in your log in flow, and explore the platform and web APIs you need to adopt this feature.
Learn how you can use the latest improvements to Safari Web Extensions to create even better experiences for people browsing the web. We’ll show you how to upgrade to manifest version 3, adopt the latest APIs for Web Extensions, and sync extensions across devices.
Don’t be captured by CAPTCHAs! Private Access Tokens are a powerful alternative that help you identify HTTP requests from legitimate devices and people without compromising their identity or personal information. We’ll show you how your app and server can take advantage of this tool to add confidence to your online transactions and preserve privacy.
Learn how to add your own tools directly into Web Inspector using the latest Web Extensions APIs. We’ll show you how to create your own tab in Web Inspector, evaluate JavaScript in the inspected page, and use the result to help you troubleshoot and identify potential problems.
Discover techniques for building rich, accessible web apps with custom controls, SSML, and the dialog element. We’ll discuss different assistive technologies and help you learn how to use them when testing the accessibility of your web apps.
Learn how you can provide safe and fast authentication in your app using Sign in with Apple. We’ll show you how you can upgrade password-based accounts into secure, single-tap login credentials, and explore how you can seamlessly handle changes to user sessions in your app. We’ll also help you take advantage of Sign In with Apple across the web and on other platforms. To get the most out of this session, we recommend having familiarity with Sign In with Apple and REST API. We’d also recommend having a basic understanding of JavaScript.
Explore the latest updates to WKWebView, our framework for incorporating web content into your app’s interface. We’ll show you how to use the JavaScript fullscreen API, explore CSS viewport units, and learn more about find interactions. We’ll also take you through refinements to content blocking controls, embedding encrypted media, and using the Web Inspector.
Discover the latest ways to ensure that DNS — the foundation of internet addressing — is secure within your app. Learn how to authenticate DNS responses in your app with DNSSEC and enable DNS encryption automatically with Discovery of Designated Resolvers (DDR).
Safari 16 brings support for Web Inspector Extensions, so you can enhance Safari’s built-in browser developer tools. This can be especially helpful when using powerful third-party frameworks and services — perhaps your team uses React, Angular, Vue, or Ember; or maybe a popular test suite or another developer service. Now with Safari Web Inspector Extensions, you’ll be able install developer tools extensions from those frameworks and services to make your job developing with them faster and easier. Look for such extensions in the App Store this fall.
Extensions for popular third-party frameworks and services aren’t the only exciting use of Web Inspector Extensions. Often, a small enhancement to developer tools can make a huge difference in workflow. You might be the best person to imagine and create such an extension. Web extensions are made from HTML, CSS, and JS — a perfect project for web developers. To learn the basics of building a Safari Web Extension, either from a quick-start template or by converting an existing extension to work with Safari, along with how to package it for the App Store, watch the Tech Talk Build and deploy Safari Extensions.
Web Inspector Extensions join other improvements to Safari Web Extensions, including the ability to sync which extensions are enabled across iOS, iPadOS, and macOS.
Container Queries
After years of collaboration by engineers working on various browsers to figure out whether or not they would even be possible, Container Queries are finally here. Similar to Media Queries, Container Queries allow you to adjust the layout or styling of a particular item on your web page based on the size of its container rather than the size of the viewport. They’ll be an invaluable tool for creating reusable components in a design system.
Safari 16 supports size queries and container query units. “Size queries” are what web developers imagine when they talk about container queries — the opportunity to write CSS that only applies if a container is a certain size. Other ideas for style queries are also being discussed as part of Container Queries as something for the future.
Container query units are similar to viewport units, but they specify a length relative to the dimensions of a query container instead of the viewport.
unit
relative to
cqw
1% of a query container’s width
cqh
1% of a query container’s height
cqi
1% of a query container’s inline size
cqb
1% of a query container’s block size
cqmin
The smaller value of cqi or cqb
cqmax
The larger value of cqi or cqb
Web Push for macOS
Web Push is coming to Safari 16 on macOS Ventura. This lets you remotely send notifications to users of your websites and web apps — and deliver those notifications even when Safari isn’t running. It uses the same combination of web standards you may be familiar with from other browsers: Push API and Notifications API, along with Service Worker.
Users opt into notifications by first indicating interest through a user gesture — such as clicking a button. Then, they’ll be prompted to give permission for your site or app to send notifications. Users will be able to view and manage notifications in Notifications Center, and customize styles and turn notifications off per website in Notifications Settings.
If you’ve already implemented Web Push for your web app or website using industry best practices, it will automatically work in Safari. Although, if you’ve excluded Safari through browser detection, you’ll need to switch to feature detection to get it working.
Web Push in Safari uses the same Apple Push Notification service that powers native push on all Macs and iOS devices. If you tightly manage push endpoints on your server, be sure you allow URLs from any subdomain of push.apple.com. You do not need to be an Apple Developer Program member.
And look for Web Push for iOS and iPadOS in 2023.
Subgrid
CSS Grid shipped over five years ago, in March 2017, revolutionizing what’s possible in layout design on the web. Subgrid takes Grid to another level, providing an easy way to put grandchildren of a grid container on that grid. It makes it possible to line up items across complex layouts without being constrained by the HTML structure. And Safari’s Grid Inspector lets you turn on the overlays for as many grids as you want — which is especially helpful when coding subgrid.
Flexbox Inspector
Following last year’s Grid Inspector, Safari 16 adds a Flexbox Inspector. It pairs perfectly with the addition of the Alignment Editor in Safari 15.4.
Overlays for Flexbox containers make it easier to visualize the effects your CSS has on Flexbox containers. The new overlay helps you visually distinguish between free space and gaps. It also shows the bounds of items revealing how they are distributed both on the main axis and cross axis of your Flexbox containers. The toggle-able “Order Numbers” option helps show the layout order of elements in the container, which can be helpful when using the order CSS property for items. And, just like our overlays for Grid last year, you can turn on as many Flexbox overlays as you need, without impacting performance.
Accessibility Improvements
Safari 16 introduces a re-architecture of WebKit’s accessibility support on macOS that delivers improved performance and increased responsiveness. This change allows WebKit to service more accessibility requests from clients like VoiceOver in less time than before. On some complex webpages, we’ve measured twice the number of accessibility requests served in twenty-five percent less time.
This release also greatly improves accessibility support for elements with display:contents by ensuring they are properly represented in the accessibility tree.
Animation Improvements
CSS Offset Path (also known as Motion Path) provides web developers a way to animate things along a custom path of any shape. The offset-path property let’s you define a geometrical path along which to animate. The offset-anchor, offset-distance, offset-position, and offset-rotate properties give you additional abilities to refine the exact movement of the object being animated. While the offset property acts as a shorthand for combining these properties.
With Safari 16, you can now animate a CSS Grid. That means changes in the size of rows and/or columns can be animated, opening up a whole new set of possibilities for movement on a page.
Safari 16 also adds support for composite operations, resolving how an element’s animation impacts its underlying property values. And it adds support for discrete animation to thirty-nine CSS properties — see the full list in the Safari Technology Preview 143 release notes.
Overscroll Behavior
CSS Overscroll Behavior determines what happens when a user scrolls and reaches the boundary of a scrolling area. It’s useful when you want to stop scroll chaining — when a user scrolls inside a box and hits the end, you now have control over stopping or allowing scrolling on the rest of the page.
Shared Worker
Just when you thought there weren’t enough different kinds of workers, there’s a new type of worker in Safari — Shared Worker. Like Service Worker, a Shared Worker runs JavaScript in the background, but its lifetime is slightly different. Your Shared Worker runs as long as the user has any tab open to your domain, and all the tabs open to the same domain can share the same Shared Worker. So, if you want to do something like have one WebSocket connection open to a server that communicates on behalf of multiple tabs, try out Shared Worker.
And more
There’s much more, including fixes and improvements to form controls as well as support for <form>.requestSubmit() and the showPicker() method for HTML input elements. Plus support for Shadow Realms, as well as support for the worker-src Content Security Policy directive.
To learn more about what’s in Safari 16 for web developers, including a list of bug fixes, read the Safari 16 beta release notes.
Feedback
We love hearing from you. Send a tweet to @webkit, @jensimmons, or @jonathandavis to share your thoughts on this release. What technology from Safari 16 are you most excited about? What features or fixes do you want to see next? If you run into any issues, we welcome your feedback on Safari UI, or your WebKit bug report about web technology 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 use the WebKit Feature Status page to watch for new information about the web features that interest you the most.
WebKit is the purring engine of Safari, it’s true, but it has numerous ports and many contributors. These ports are used for all sorts of things, from powering Sony PlayStations to driving millions of embedded devices all over the world. Embedded use cases like smart home appliances, digital signage, and automotive displays are largely possible thanks to WPE WebKit, the official port of WebKit specifically optimized for embedded devices. That port is maintained by Igalia, an open-source consultancy headquartered in Spain. Their work on WPE WebKit goes a long way toward explaining why Igalia is the most prolific external contributor to the WebKit codebase, accounting for almost 17% of all commits in 2021.
Igalia recently celebrated the fifth birthday of WPE WebKit with a blog post on their WPE web site, covering its evolution from a fork of WebKitGTK to a Wayland-based renderer to a framework compatible with almost any rendering backend before its public launch on 21 April 2017. They also promise a series of articles to come profiling the people who work on WPE WebKit and talking about some of the technical aspects of advancing such a project. You can read more about it in their post. Happy 5th birthday, WPE!
Safari Technology Preview Release 146 is now available for download for macOS Big Sur and of macOS Monterey 12.3 or later. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Color fonts provide a way to add richness to your designs without sacrificing any of the many benefits of using plain text. Regardless of how decorative a color font is, the underlying text is always searchable, copy/paste-able, scalable, translatable, and compatible with screen readers.
WebKit now supports CSS @font-palette-values. With this at-rule, you can access predefined color palettes provided by the font designer, and you can customize them in order to make the color font a perfect match for the colors in your designs.
ONCE upon a time in the middle of winter, when the flakes of snow were falling like feathers from the clouds, a Queen sat at her palace window, which had an ebony black frame, stitching her husband’s shirts. While she was thus engaged and looking out at the snow she pricked her finger, and three drops of blood fell upon the snow. Now the red looked so well upon the white that she thought to herself, “Oh, that I had a child as white as this snow, as red as this blood, and as black as the wood of this frame!” Soon afterwards a little daughter came to her, who was as white as snow, and with cheeks as red as blood, and with hair as black as ebony, and from this she was named “Snow-White.” And at the same time her mother died.
THIS answer so angered the Queen that she became quite yellow with envy. From that hour, whenever she saw Snow-White, her heart was hardened against her, and she hated the little girl. Her envy and jealousy increased so that she had no rest day or night, and she said to a Huntsman, “Take the child away into the forest. I will never look upon her again. You must kill her, and bring me her heart and tongue for a token.” The Huntsman listened and took the maiden away, but when he drew out his knife to kill her, she began to cry, saying, “Ah, dear Huntsman, give me my life! I will run into the wild forest, and never come home again.”
You can try out @font-palette-values today in Safari 15.4 or later.
Color palettes work great in WebKit with the COLRv0 font file format, and we are investigating other formats like SVG.
Each glyph is made up of a handful of distinct layers (letterform, backdrop, ornate linework, letter outline, and border). Making the layers different-yet-coordinated colors adds depth to the design, taking it beyond what a simple foreground/background can provide. It felt like the perfect use case for a color font with multiple color palettes, and a unique opportunity for a 127-year-old font to play a small part in an emerging font technology.
Palettes in CSS
Fonts can define one or more of their own color palettes inside the CPAL table inside the font file. The palettes are ordered, and so they are identified by index. For example, a font might define color palette #3 that uses blues and greens, but another palette #5 might use reds and oranges. Colors within a palette inside the font are also identified by index – all palettes contain the same number of colors within themselves.
These color palettes can be tweaked or overridden in CSS using font-palette-values. An example looks like this:
This example means “Make a color palette named Lilac Blossom, that, when applied to Bradley Initials DJR Web, is just like the 7th palette in the font, but overrides color #0 in that palette to be white, and color #1 in the palette to be #F3B0EB.” If you don’t want to override any colors, that’s no problem – just delete the entire override-colors descriptor.
You can then apply this color palette by simply supplying it to the font-palette property like this:
font-palette: --lilac-blossom;
The Round Table
Progressive Enhancement
If you’re using an older version of Safari, or a different browser which doesn’t understand the font-palette property, it will render the default (0th) color palette in the font. Here’s what the above example would look like in such a browser:
The Round Table
If the fallback behavior is undesirable for your particular font, you can detect browsers that understand CSS color palettes in your stylesheet by using the @supports media query, like so:
Not all color palettes are clearly visible on all backgrounds. Without color fonts, the color used to render text was entirely determined by the CSS author, but now that fonts can have color palettes defined inside them, it’s up to CSS authors to pick or create a color palette that is legible on the background it’s being rendered on. This can be particularly tricky when font fallback occurs, or when the user has blocked some fonts from loading.
Fonts such as Bradley Initials DJR Web have an extra tool for helping with this, though. Fonts can indicate that certain palettes inside them are usable with light backgrounds or usable with dark backgrounds, and these palettes are hooked up to the font-palette property. You don’t even have to use @font-palette-values!
So, if you want to use a color palette on a dark background, you can simply say font-palette: dark, like this:
ABCDEFG
And the same thing for a light background: font-palette: light:
ABCDEFG
Because the font-palette property has no effect on non-color fonts, it’s safe to set it in conjunction with the prefers-color-scheme media query, like this:
Because @font-palette-values blocks are scoped to a specific font, you can make multiple of them that share a name. This is really powerful – it means you can define a single color palette name, and have it applied differently to whatever font happens to be rendered with it. Here’s an example:
This will have Bradley Initials DJR Web’s Lilac Blossom palette applied to Bradley Initials DJR Web, and Megabase’s Lilac Blossom palette applied to Megabase. And you only had to specify the font-palette property once!
Contextual color
In addition to pulling colors from palettes, some color fonts set aside special shapes that are connected to the foreground color of the current element. This makes them extra flexible, but it also means that these shapes operate independently from font-palette. In these cases, you can simply use the color property to change their color, like in this demo using Megabase.
<div style="font-family: 'Megabase Web';">
<div style="color: black;">They were just pushed into space.</div>
<div style="color: blue;">As much as I care about you.</div>
</div>
They were just pushed into space.
As much as I care about you.
Conclusion
Of course, with power comes responsibility; just because you can change colors, doesn’t mean you always should. Often the colors in a palette are coordinated to harmonize aesthetically, so it’s good to have a sense of how they are meant to relate to one another. You can look at the font’s predefined color palettes to see how the font designer assigned the roles for each color in the palette, and tweak accordingly.
It is also important to choose colors that contrast strongly against the background in order to keep your text readable and your webpage accessible. Colors in the palette that are used to form the base of the letters should typically “pop” against the background, while supporting layers like shadows, outlines, and decorative elements might contrast less in order to keep them from overpowering the letterforms.
Color fonts are a great improvement over graphic images, because they work by default with screen readers, copy/paste, and find-in-page. Also, they gracefully show fallback text if the font somehow fails to load, and they reflow if the browser window resizes. Not only that, color fonts are more flexible than graphic images, because they can incorporate the foreground color of the element using them into the design of the font.
Color fonts are not meant to take the place of single-color fonts. But used in moderation, at a big enough size in the right circumstance, they can be the perfect icing on the cake!
After the feature-packed release of Safari 15.4 two months ago, WebKit’s work for this version of Safari focused predominately on polishing existing features and fixing bugs.
Safari 15.5 does contain three new technologies for web developers — support for the inert property in HTML; support for the worker-src Content Security Policy directive; and the new minimumViewportInset and maximumViewportInset APIs for implementing new CSS Viewport Units in WKWebView-based apps.
Safari 15.5 is available for macOS Monterey 12.4, macOS Big Sur, macOS Catalina, iPadOS 15.5, and iOS 15.5. You can update to Safari 15.5 on macOS Big Sur and macOS Catalina by going to System Preferences → Software Update → More info, and choosing to update Safari.
Developer Features
Let’s look first at the HTML inert attribute. When set on an element, the attribute makes that element non-interactive by preventing mouse and keyboard focus, clicks, edits or text selection. It also hides the element from assistive technologies. For more information about inert, including a demo showing how inert can make the partially-offscreen content in a carousel visible, but inactive, read Non-interactive Elements with the inert Attribute.
Next, let’s look at support for worker-src from Content Security Policy Level 3. The worker-src directive provides web developers a way to restrict which URLs are allowed to be sources for worker scripts (Worker, SharedWorker, or ServiceWorker). This can be used to prevent already loaded scripts from loading more scripts in the form of workers, a situation that has potential to be susceptible to malicious attack through using excessive CPU for computation. We also updated Content Security Policy console logging in Web Inspector.
And last, we’ve added the minimumViewportInset and maximumViewportInset APIs to WKWebView so app developers can add support for all of the new CSS Viewport Units to their browser or other application on iOS, iPadOS and macOS. The minimumViewportInset corresponds to the large measurement, and maximumViewportInset corresponds to the small measurement. The new CSS Viewport Units, which shipped in Safari 15.4, include small (svw, svh, svi, svb, svmin, svmax), large (lvw, lvh, lvi, lvb, lvmin, lvmax), dynamic (dvw, dvh, dvi, dvb, dvmin, dvmax), and logical (vi, vb) units.
Fixes and Polish
Now, let’s get to the list of bug fixes and feature polish.
HTML
Fixed SVG tags behind modal dialogs to not be clickable
Fixed the Dialog element only animating once
Fixed rendering a USDZ loaded as the main resource
Fixed uploading “.pages” files to file inputs accepting “.pages” and “.jpeg” files
Web API
Prevented BroadcastChannel from communicating across distinct opaque origins
Fixed respecting website policies during COOP-based process swap
Fixed PointerEvent.movementX always 0
Fixed resolving a fetch promise when a page enters page cache
Fixed pointer events to perform a hit test only if there is not a pointer capture target override
Fixed computing the site for cookies when the document is created by window.open
Fixed Element.focus({preventScroll: true}) to correctly prevent scrolling on iOS
CSS
Fixed scrolling background-attachement: fixed
Fixed background-clip: text to work with display: flex
Fixed rendering for many position: sticky elements
Fixed position: sticky elements moving incorrectly while scrolling
Fixed text contents in <span> with opacity not updating when a sibling element has will-change: transform
Fixed :focus-visible matching on the wrong element when focused via script
Fixed text-shadow getting clipped
Fixed behavior of a position: sticky element within contain: paint
Fixed aspect-ratio with percentage widths
Fixed returning the default computed style for elements without the transition or animation shorthands
Authentication
Aligned WebAuthn implementation to match specifications to use the default pubKeyCredParams list if the list in makeCredential is empty
Content Security Policy
Fixed blocking image content in object elements
Fixed sending violation reports to the document for a detached element
Improved nonce hiding from the DOM
Updated Content Security Policy handling of JavaScript URLs
Media
Fixed key rotation for single key audio in modern EME paired with a native HLS player
Fixed disabled Control Center spatial control when playing a video in Safari
Fixed loading a model in QuickLook when passing extra parameters
Fixed muted video that sometimes becomes paused when taken to fullscreen
Fixed video playback on iPhone 7
Fixed video playback for HEVC content encodings that generate many b-frames with a wide sliding window
Fixed HLS stream currentTime sometimes jumping backwards
Fixed clicking on the progress bar often pausing a YouTube video
Fixed blob videos slowing to pause
Fixed audio echo after the camera us paused or unpaused
Fixed playback of HTML5 embedded audio with unbounded range requests
Fixed the video poster disappearing prematurely on play, leaving a transparent video element
WebRTC
Fixed incorrect label returned by getUserMedia regardless of language selected
Reduced perceived audio latency
Rendering
Fixed text wrapping for windows that exceed a certain width
Fixed a Korean webfont rendering issue
Fixed an issue where a transform change sometimes resulted in bad rendering
Fixed a flash of missing text content with transform-related animations
Changed to use colgroup for table sizing when it comes after thead, tbody, tfoot, or tr elements
Fixed two bopomofo tone marks to move to the correct place in vertical text with a particular bopomofo font
Apple Pay
Fixed the Apple Pay Sheet to return billingContact on iOS
WebGL
Fixed WebGL rendering when using preserveDrawingBuffer on iOS
Fixed a number of issues related to multisampling that were breaking a lot of WebGL content
Fixed handling TypedArray with AllowShared to be accepted
Fixed WEBGL_multi_draw validation
Web Inspector
Fixed large message handling from remote devices
Fixed repeated opening and closing
Compatibility
Fixed launching Microsoft Teams from Safari
SFSafariViewController
Fixed a noticeable delay in playback when rotating a full screen YouTube video
Safari Extensions
Fixed a crash clicking on Safari App Extension toolbar items
Fixed an issue where SFContentBlockerManager.getStateOfContentBlocker() could return an incorrect value on iOS
Added support for optional_host_permissions for Safari Web Extensions
Feedback
We love hearing from you. Send a tweet to @webkit, @jensimmons or @jonathandavis to share your thoughts on this release. If you run into any issues, we welcome your feedback on the Safari UI or your WebKit bug report about web technology. 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 use the WebKit Feature Status page to watch for new information about the web features that interest you the most.
Safari Technology Preview Release 145 is now available for download for macOS Big Sur and of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
This release covers WebKit revisions 291957-293023. This release of Safari Technology Preview does not support versions of macOS Monterey prior to 12.3. Please update to macOS Monterey 12.3 or later to continue using Safari Technology Preview.
Note: Tab Groups do not sync in this release.
Web Inspector
Sources tab
Allowed Response Local Overrides to map to a file on disk (r292084, r292120)
:has() pseudo-class
Added invalidation support for the pseudo-classes :autofill (r292531); :placeholder-shown (r292523); :indeterminate, :read-only, :read-write, :required and :optional (r292466, r292582)
Safari Technology Preview Release 144 is now available for download for macOS Big Sur and of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
This release covers WebKit revisions 291506-291957. This release of Safari Technology Preview does not support versions of macOS Monterey prior to 12.3. Please update to macOS Monterey 12.3 or later to continue using Safari Technology Preview.
Note: Tab Groups do not sync in this release.
Web Inspector
Fixed page reloading and showing an empty inspector on pages with container queries (r291824)
Elements Tab
Fixed $0 not being displayed for the selected node after switching to another tab (r291729)
Fixed unwanted extra dash when autocompleting CSS variable names in the Styles panel (r291740)
Fixed inline swatch popovers not being hidden when the inline swatch is removed (r291628)
Console Tab
Fixed console.screenshot to no longer have extra transparent pixels at the bottom of viewport screenshots (r291519)
CSS
Added Typed OM support for container units (r291524)
Since the switch to GStreamer mono repository, gst-buildhas been deprecated. The mechanism in WebKit were added, basically, to allow GStreamer upstream, so keeping gst-build directory just polluted the conceptual framework.
By using gst-build one could override almost any other package in WebKit SDK. For example, for developing gamepad handling in WPE I added libmanette as a GStreamer subproject, to link a modified version of the library rather than the one in flatpak. But that approach added an unneeded conceptual depth in tree.
In order to simplify these operations, by taking advantage of Meson’s subproject support directly, gst-build handling were removed and new mechanism was set in place: Local Dependencies. With local dependencies, you can add or override almost any dependency, while flatting the tree layout, by placing at the same level GStreamer and any other library. Of course, in order add dependencies, they must be built with meson.
For example, to override libsoup and GStreamer, just clone both repositories below of Tools/flatpak/local-projects/subprojects, and declare them in WEBKIT_LOCAL_DEPS environment variable:
Elements are interactive in multiple ways; they can be focused, clicked, edited or selected. Assistive technologies such as screen readers also interact with them. What if you wanted to disable all these interactions for a section of your webpage? The inert attribute covers this.
Pre-existing Solutions
The most basic way to disable all interactions is the disabled HTML attribute, it prevents focus, clicks, edition or selection. However, it only works on form controls, and does not necessarily hide the control from assistive technologies. What if you wanted to do this on elements other than form controls?
To prevent focus, tabindex="-1" is a popular option, but it isn’t ideal because setting the attribute on a container element will not prevent its children from being focused, and it also does not prevent click-focus with a mouse. If you want to prevent keyboard focus on a whole section, every single focusable element in the section needs the attribute set.
To prevent clicks, the pointer-events: none CSS property may be an option although, unless you’ve put tabindex="-1" on the element as well, tabbing to the element and interacting with it is still possible.
To prevent selection, the user-select: none CSS property is the most common option.
To prevent editing, if you’re using contenteditable, you would need to manually remove the contenteditable attribute. If it’s a form control, you need to manually set the disabled attribute.
All of this is heavy and also does not allow you to hide the section from assistive tools, which may be appropriate when making a whole section non-interactive. It is currently possible to do this by setting aria-hidden="true" on the relevant section.
This is why the inert attribute was added to HTML, it is an efficient way to disable all of the interactions above and hide elements from assistive technology.
What is the inert attribute?
The inert attribute was originally specified in the context of the dialog element about 10 years ago, where the main use-case was making the content behind a modal dialog non-interactive. It was later removed due to a lack of adoption. Recent developer requests lead it to be re-proposed a few years later. However, the main issue with that definition was complexity in interaction with modal dialogs and performance. This is why changes have been made to the standard to use a CSS-based approach to define inert nodes.
Hit-testing must act as if the ‘pointer-events’ CSS property were set to ‘none’.
Text selection functionality must act as if the ‘user-select’ CSS property were set to ‘none’.
If it is editable, the node behaves as if it were non-editable.
In addition to the above:
It is not focusable.
It behaves similarly to aria-hidden="true" for assistive technologies
Example Usage: A Carousel
Let’s build a carousel with multiple pages:
You will notice that you can still interact with links/inputs/etc. from inactive pages. This is undesirable behavior that can be prevented with the inert attribute. In the switchToIndex method which changes the active page, we can add 2 lines of code that fix this.
this.items being an array of HTML elements representing each slide, the first line makes every slide inert, and the second line makes the active one non-inert.
Today is a special day for Igalia, especially for those colleagues that work on WebKit:
Five years ago, on the 21st of April 2017, the WPE port was announced by our colleague
Žan Doberšek on the WebKit mailing list.
Let’s take some time to celebrate and recap how WPE evolved from the early prototyping days to the product empowering hundreds of millions of devices
worldwide today.
WPE is … what exactly?
To get everyone on the same page, let’s start by reiterating what WPE is: a WebKit port optimized for embedded devices.
It allows you to embed a full-fledged Web browser engine that supports a large set of modern Web technologies into your product.
WPE itself is not a Web browser such as Safari, Chrome or Firefox but contains the underlying building blocks to load, parse and
render websites. To learn more about the distinction between a Web browser and a Web browser engine read our explainer.
You might ask yourself, what does “optimized for embedded devices” mean in practice? Unlike most other WebKit ports, WPE does not
rely on a specific user-interface toolkit, such as Qt, GTK, Cocoa,
etc., nor does it offer any integration with these kinds of toolkits. WPE WebKit is light-weight, integrates well with a
variety of hardware configurations, and only requires a minimum set of APIs on your side:
EGL and OpenGL ES 2.
The early days 2014 - 2017
The idea for a new WebKit port was born in 2014, as part of a collaboration between Metrological
and Igalia. The goal of this collaboration was to have a WebKit port running efficiently on their set-top boxes,
utilizing a modern Wayland based Linux graphics architecture. Back then, QtWebKit was popular
among embedders; however, it was unmaintained and its future was unclear since Qt wanted to transition
from using WebKit to Blink.
In September 2014 a group of Igalians forked the WebKitGtk port, removed all GTK toolkit dependencies, and prototyped what was necessary
to achieve the goal: rendering websites without involving any of the traditional toolkits and instead utilizing a Wayland-based rendering approach.
During development it became apparent that this WebKit port is generally useful for all our customers and the community as a whole.
Therefore Igalia decided to aim for an even more flexible design, where Wayland is only one of the possible backends.
Our fellow Igalian Miguel Gomez reported in his late 2016 blog post
about this change, and the renaming of the port: WPE appears for the first time in public.
The project’s removal of the Wayland dependency and the subsequent reorganization lead to the architecture we have today,
consisting of not only the WPE port itself but a whole ecosystem of projects such as libwpe,
WPEBackend-fdo, WPEBackend-rdk, etc.,
that together form the WPE project.
2017 - today
After months of focused engineering efforts, the downstream work was finished and Igalia was ready to announce WPE to the
public on the 17th of April 2017, with the promise that Igalia
will maintain the port alongside the existing WebKitGtk port. That is not a cheap bill: maintaining an upstream port is a recurring
multi-million dollar investment. Just in order to keep the port itself healthy, as updates are made all around it, requires infrastructure,
bots and a team of fully dedicated engineers to deal with maintenance, testing, triaging, tickets, etc. To implement new Web standards, fix
related bugs or design and contribute features requires an even more considerable amount of resources.
Since then, Igalia ramped up the WPE investments and steadily advanced the port while helping customers to integrate WPE into their
environments. Today WPE is healthy, runs on many platforms, and offers the most flexible browser architecture at present. Also, thanks in great
part to this work, Igalia was responsible for nearly 16.5% of all commits in WebKit itself last year, helping make the larger project
and ecosystem around it healthier too.
However, none of this would be possible without the commitment of many Igalians pushing the project forward every day for the past 8 years.
A new People Behind WPE series will be launched soon: over the following months, the Igalians involved with WPE will introduce themselves, their area of
expertise, and talk about a specific WPE related technical topic. You’ll get to know the people behind the product and a first-class technical overview
of individual parts of the WPE architecture! We plan to release a new article every 3-4 weeks, so be sure to visit wpewebkit.org/blog
again soon and enjoy the upcoming People Behind WPE series.
Feel free to spread the word and make noise about WPE. Stay healthy, stay tuned!
Once again Igalia is organizing the Web Engines Hackfest. This year the event is going to be hybrid. Though most things will happen on-site, online participation in some part of the event is going to be possible too.
Regarding dates, the hackfest will take place on June 13 & 14 in A Coruña. If you’re interested in participating, you can find more the information and the registration form at the event website: https://webengineshackfest.org/2022/.
What’s the Web Engines Hackfest?
This event started a long way back. The first edition happened in 2009 when 12 folks visited the Igalia offices in A Coruña and spent there a whole week working on WebKitGTK port. At that time, it was kind of early stages on the project and lots of work was needed, so those joint weeks were very productive to move things forward, discuss plans and implement features.
As the event grew and more people got interested, in 2014 it was renamed to Web Engines Hackfest and started to welcome people working on different web engines. This brought the opportunity for engineers of the different browsers to come together for a few days and discuss different features.
The hackfest has continued to grow and these days we welcome anyone that is somehow involved on the web platform. In this year’s event there will be people from different parts of the web platform community, from implementors and spec editors, to people interested in some particular feature.
This event has an unconference format. People attending are the ones defining the topics, and work together in breakout sessions to discuss them. They could be issues on a particular browser, generic purpose features, new ideas, even sometimes tooling demos. In addition, we always arrange a few talks as part of the hackfest. But the most important part of the event is being together with very different folks and having the chance to discuss a variety of topics with them. There are not lots of places where people from different companies and browsers join together to discuss topics. The idea of the hackfest is to provide a venue for that to happen.
2022 edition
This year we’re hosting the event in a new place, as Igalia’s office is no longer big enough to host all the people that will be attending the event. The venue is called Palexco and it’s close to the city center and just by the seaside (with views of the port). It’s a great place with lots of spaces and big rooms, so we’ll be very comfortable there. Note that we’ll have childcare service for the ones that might need it.
The event is going to be 2 days this time, 13th and 14 June. Hopefully the weather will be great at that time of the year, and the folks visiting A Coruña should be able to really enjoy the trip. There are going to be lots of light hours too, sunrise is going to be around 7am and sunset past 10pm.
The registration form is still open. So far we’ve got a good amount of people registered from different companies like: Arm, Deno Land, Fission, Google, Igalia, KaiOS, Mozilla, Protocol Labs, Red Hat and Salesforce.
Apart from that there are going to be some talks that will be live streamed during the event. We have a Call For Papers with a deadline by the end of this month. Talks can be on-site or remote, so if you’re interested on giving one, please fill the form.
We know we’re in complex times and not everyone can attend onsite this year. We’re sorry about that, and we hope you all can make it in future editions.
Welcome to the fourth feature update on Private Click Measurement, our proposed web standard for measuring advertising in a privacy-preserving way. More precisely three major and two minor updates to PCM, all available in iOS/iPadOS 15.4 and macOS Monterey 12.3.
Major updates:
Conversion fraud prevention. This enables merchant websites to sign unlinkable tokens and get proof in the attribution report that it was triggered by a trustworthy conversion event.
Replacement for third-party tracking pixels. Merchant websites can now trigger a conversion event through a same-site pixel which removes the need to call any third-parties in PCM.
Measurement of clicks in cross-site iframes. Many publishers want to isolate ads on their site in cross-site iframes. Now clicks in such iframes can be measured too.
What is Private Click Measurement?
Private Click Measurement, or PCM, is a proposed web standard for measuring the effectiveness of click-through advertising in a privacy-preserving way. It allows for 8 bits of data on the click source site to be combined with 4 bits of data on the click destination site to measure which clicks are driving conversions. The combined 8+4 bits of data is sent to both the click source and destination in an attribution report that doesn’t carry any user or device identifiers. The net result is a report that says “Someone who clicked ad X on website A later converted with value Y on website B.”
PCM was made available as a beta in May 2019, and then shipped 2021 in iOS/iPadOS 14.5 and in Safari 14.1 on macOS. Its privacy-preserving nature means it can be used without getting the user’s permission to track according to AppTrackingTransparency.
Conversion Fraud Prevention
In July 2021 we presented our beta feature for click fraud prevention in PCM. It allows the click source website to sign an unlinkable token at the time of the click navigation, which results in a signed token being included in the resulting attribution report. Today we present the same capability for the click destination website.
PCM’s triggering event is a redirect to the well-known path .well-known/private-click-measurement/trigger-attribution/. Now that redirect supports a query string parameter called attributionDestinationNonce. If a nonce is included in the redirect, it triggers WebKit to ask the click destination server to sign an unlinkable token.
Step 1: Generate an RSA Key Pair
Unlinkable tokens require a public key for token generation and validation, and a corresponding private key for signing. PCM supports three different RSA key sizes: 2048, 3072 and 4096 bits. The expected encoding of the public key is a Base64URL encoded (using RFC4648 section 5) SPKI with the RSA-PSS OID and the parameters corresponding to the RSABSSA IETF draft. Examples of such key encodings are included in an earlier blog post.
Step 2: Add the Query Parameter attributionDestinationNonce
The triggering event should look like this to opt in to conversion fraud prevention:
https://site.example/some/sub/resource/
… redirects to … https://site.example/.well-known/private-click-measurement/trigger-attribution/11?attributionDestinationNonce=ABCDEFabcdef0123456789
The attributionDestinationNonce is only in place to help the click destination server know the context for which it’s signing an unlinkable token. When the click destination server is asked to sign an unlinkable token, it’ll get the attributionDestinationNonce and can make a decision as to whether the triggering event was trustworthy or not.
The attributionDestinationNonce needs to be a Base64URL encoded 128-bit/16-byte value. Any smaller or larger value will cancel the issuance flow of the fraud-prevention signature. Any non Base64URL encoded value will also cancel the token transaction. Web Inspector will log a warning if the attributionDestinationNonce is malformed.
Step 3: Respond to a Request for Your Public Key
The browser and any validating party needs to be able to fetch your public key at any point in time from this well-known location: https://clicksource.example/.well-known/private-click-measurement/get-token-public-key/.
Replacement For Third-Party Tracking Pixels
Previous versions of PCM required HTTP requests on the click destination site to go to the click source site. This was designed to enable reuse of existing cross-site tracking pixels that no longer carry cookies under tracking prevention.
For new adoption of PCM, for instance onboarding a new publisher site where ads are shown, there’s no need to add such legacy pixels. Longer term, we want to remove support for triggering events through cross-site tracking pixels since even though they don’t carry cookies, they do ping third-party domains that may be categorized as trackers. Many websites want to be completely tracker-free and we’re happy to be able to support that with PCM while still allowing for click measurement across websites.
Click destination sites can now signal a triggering event through a same-site pixel. It looks like below.
Same-site subresource request to: https://clickDestination.example/triggeringEventRedirect/21
… redirects to same-site well-known location: https://clickDestination.example/.well-known/private-click-measurement/trigger-attribution/21?attributionSource=https://clickSource.example
The above redirect tells PCM to schedule an attribution report if there is a pending attribution from clickSource.example.
Why not a JavaScript API to trigger attribution without cross-site pixels, you may ask? There are two reasons for that:
The standards conversation in W3C Privacy CG told us that some click destination sites have a policy against placing third-party scripts on their websites because of the risky dependencies they introduce. We think that’s a very valid concern, not in the least for user privacy. However, those same click destination sites do not have the same strict policies against pixels. We can given them the API functionality they need by introducing same-site pixels. They can be triggered through actual image elements or through a JavaScript Fetch.
We continue to consider it very important that websites should not be able to tell at page load time whether or not the user has PCM (or other ad measurement) features enabled. This is to protect the user’s right to a choice. Pixel APIs don’t reveal if the triggering event is accepted or not. A JavaScript API could be made to not reveal that info either but pixel APIs just do that.
Measurement of Clicks in Cross-Site Iframes
One of the first PCM change requests from the web community was to allow for measurement of clicks that happen in cross-site iframes. This would allow for measurement of advertising isolated in iframes. We’re happy to announce that we now support that. Note that the resulting attribution report is still sent to the two first-party websites, often referred to as the publisher and the merchant.
WebIDL Attributes Now In Camelcase
JavaScript access to PCM’s anchor tag attributes now requires camel-casing. This is the result of interoperability work in web standards. Here’s how it looks:
When sending your public key to the browser, it needs to be a 2048, 3072 or 4096 bit RSA public key wrapped with an RSAPSS OID in ASN1 format. See “Step 1: Generate an RSA Key Pair above.” Those bytes then need to be base64 encoded with the URL and filename safe alphabet. Examples of such key encodings are included in an earlier blog post.
Safari Technology Preview Release 143 is now available for download for macOS Big Sur and of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
This release covers WebKit revisions 290223-291506. This is the last release of Safari Technology Preview that will support versions of macOS Monterey prior to 12.3. Please update to macOS Monterey 12.3 or later to continue using Safari Technology Preview.
Note: Tab Groups do not sync in this release.
Web Inspector
Elements Tab
Added option in the Layout panel of the Details Sidebar for Flexbox overlays to show each item’s CSS order and/or DOM index in the parent flex container (r290613)
Service workers are no longer terminated while they are being inspected. (r291467)
CSS Container Queries
Added support for nested container queries (r290257)
This is the final report about the work Igalia has been doing to add support for :focus-visible in WebKit. As you probably already know this work is part of the Open Prioritization campaign by Igalia that has been funded by different people and organizations. Big thanks to all of you!
If you’re curious and want to know all the details you can find the previous reports on this blog.
Let’s start from the beginning, my colleague Brian Kardell had an idea to find more diverse ways to sponsor the development of the web platform, after some internal discussion that idea materialized into what we call Open Prioritization. In summer 2020 Igalia announced Open Prioritization that intially had six different features on the list:
Last year at TPAC, Eric Meyer gave an awesome talk called Adventures in Collective Implementation, explaining the Open Prioritization effort and the ideas behind it. This presentation also explains why there’s room for external investments (like this one) in the web platform, and that all open source projects (in particular the web browser engines) always have to make decisions regarding priorities. Investing on them will help to influence those priorities and speed up the development of features you’re interested in.
It’s been quite a while since we started all this, but now :focus-visible is supported in WebKit/Safari, so we can consider that the first Open Prioritization experiment has been successful.
When :focus-visible was first enabled by default in Safari Technology Preview early this year, there were lots of misunderstandings about how the development of this feature was funded. Happily Eric wrote a great blog post on the matter, explaining all the details and going over some of the ideas from his TPAC talk.
:focus-visble is shipping in in WebKit, how that happened?
In November last year, I gave a talk at CSS Conf Armenia about the status of things regarding :focus-visible implementation in WebKit. In that presentation I explained some of the open issues and why :focus-visible was not enabled by default yet in WebKit.
The main issue was that Apple was not convinced about not showing a focus indicator (focus ring) when clicking on a focusable element (like a <div tabindex="0">). However this is one of the main goals of :focus-visible itself, avoiding to get a focus indicator in such situations. As Chromium and Firefox were already doing it, and aiming to have a better interoperability between the different implementations, Apple finally accepted this behavioral change on WebKit.
Then Antti Koivisto reviewed the implementation, suggesting a few changes and spotting some issues (thanks about that). Those things were fixed and the feature was enabled by default in the codebase last December. As usual once a feature is enabled some more issues appear and they were fixed too. Including even a generic issue regarding accesskey on focusable elements, which required to add support to test accesskey on WebKit Web Platform Tests (WPT).
As part of all this work since my previous blog post we landed 9 more patches on WebKit, making a total of 36 patches for the whole feature, together with a few new WPT tests.
Buttons and :focus-visible on Safari
This topic has been mentioned in my previous posts and also in my talk. Buttons (and other form controls) are not mouse focusable in Safari (both in macOS and iOS), this means that when you click a button on Safari, the button is not focused. This behavior has the goal to match Apple platform conventions, where the focus doesn’t move when you click a button. However Safari implementation differs from the platform one, as the focus gets actually lost when you click on such elements. There are some very old issues in WebKit bugtracker about the topic (see #22261 from 2008 or #112968 from 2013 for example).
There’s a kind of coincidence related to this. Before :focus-visible existed, buttons were never showing a focus indicator in Safari after mouse click, as they are not mouse focusable. This was different in other browsers where a focus ring was showed when clicking on buttons. So while :focus-visible fixed this issue for other browsers, it didn’t change the default behavior for buttons in Safari.
However with :focus-visible implementation we introduced a problem somehow related to this. Imagine a page that has an element and when you click it, the page moves the focus via script (using HTMLElement.focus()) to a different element. Should the new focused element show a focus indicator? Or in other words, should it match :focus-visible?
ol > li::marker {
content: counter(list-item) ") ";
}
The answer varies depending on whether the element clicked is or not mouse focusable:
If you click on a focusable element and the focus gets moved via script to a different element, the newly focused element does NOT show a focus indicator and thus it does NOT match :focus-visible.
If you click on a NON focusable element and the focus gets moved via script to a different element, the newly focused element shows a focus indicator and thus it matches :focus-visible.
All implementations agree on this, and Chromium and Firefox have been shipping this behavior for more than a year without known issues so far. But a problem appeared on Safari, because unlike the rest of browsers, buttons are not mouse focusable there. So when you click a button in Safari, you go to point 2) above, and end up showing a focus indicator in the newly focused element. Web authors don’t want to show a focus indicator on that situations, and that’s something that :focus-visible is fixing through point 1) in the rest of browsers, but not in Safari (see bug #236782 for details).
We landed a workaround to fix this problem in Safari, that somehow adds an exception for buttons to follow point 1) even if they are not mouse focusable. Anyway this doesn’t look like the solution for the long term, and looking into making buttons mouse focusable on Safari might be the way to go in the future. That will also help to solve other interop issues.
And now what?
The feature is complete and shipped, but as usual there are some other things that could be done as next steps:
The :focus-visible specification is kind of vague and has no normative text related to when or not show a focus indicator. This was done on purpose to advance on this area and have flexibility to adapt to user needs. Anyway now that all 3 major web engines agree on the implementation, maybe there could be the chance to define this in some spec. We tried to write a PR for HTML spec when we started the work on this feature, at that time it was closed, probably it was not the right time anyway. But maybe something like that could be retaken at some point in the future.
WebKit Web Inspector (Dev Tools) don’t allow you to force :focus-visible yet. We sent a patch for forcing :focus-within first but some UI refactoring is needed, once that’s done adding support for :focus-visible too should be straight forward.
Coming back to the topic on buttons not being mouse focusable in Safari. The web platform provides a way to make elements not keyboard focusable via tabindex="-1". Why not providing a way to mark an element as not mouse focusable? Maybe there could be a proposal for a new HTML attribute that allows making elements not mouse focusable, that way websites could mimic Apple platform conventions. There are nice use cases for this, for example when you’re editing an input and then you click on some button to show some contextual information, with something like this you could avoid losing the focus from the input to carry on with your editing.
Wrap-up
So yeah after more than a year since Igalia started working on :focus-visible in WebKit, we can now consider that this work has been complete. We can call the first Open Prioritization experiment a success, and we can celebrate together with all the people that have supported us during this achievement. 🎉
Thank you very much to all the people that sponsored this work. And also to all the people that helped reviewing patches, reporting bugs, discussing things, etc. during all this time. Without all your support we won’t be able to have made this happen. 🙏
Last but not least, we’d like to highlight how this work has helped the web platform as a whole. Now the major web browser engines have shipped :focus-visible and are using it in the default UA stylesheet. This makes tweaking the focus indicator on websites easier than ever.
Safari Technology Preview Release 142 is now available for download for macOS Big Sur and macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Fixed incorrect absolute position layout when toggling contain (r289527)
Updated conversion to a color space with a smaller gamut to perform gamut mapping (r289396)
Updated an element with both -webkit-user-select: all and -webkit-user-drag: element to get a snapshot as when using only -webkit-user-drag: element (r289544)
Web Animations
Enabled the KeyframeEffect.composite property (r290067)
Added composite accumulation support for transform properties (r289599)
Added support for logical properties in JS-originated animations (r289216)
Aligned animations with different, but compatible, frameRate values (r290121)
Allowed setting frameRate as an option passed to Element.animate() (r290123)
Allowed setting frameRate as an option passed to document.timeline.animate() (r290125)
Changed to properly handle interpolation of non-invertible matrices (r289862)
Changed to use the animation frameRate during animation resolution and scheduling (r290003)
Changed Animation.commitStyles() to use the non-animated style (r289453)
Fixed additive and accumulation interpolation to work correctly with implicit 0% and 100% keyframes (r289454)
Fixed animating from scale() to scale() translate() (r289732)
Fixed additive animations to prevent other animations from running accelerated (r289605)
Fixed recomputing keyframes when changing direction or writing-mode (r289426)
Fixed clearing computed keyframes when changing direction or writing-mode (r289226)
Fixed animations associated with a custom effect to appear in document.getAnimations() result (r290122)
Implemented parsing and animation support for offset shorthand (r289876)
Rendering
Fixed tab characters and ch units to obey synthetic bold width adjustments correctly (r289609)
Forms
Changed input elements to return an empty string for an invalid floating-point number that ends with “.” (r290124)
Fixed selection method return values to match the spec (r289813)
Fixed light appearance text fields rendering invisible in Increased Contrast mode (r290054)
Fixed clicking an <input type="image"> submitting the form with a null submitter (r289615)
Made standalone model documents interactive (r289666)
Web Extensions
Added support for the browser.action.openPopup() and browser.browserAction.openPopup() API to open the extension popup for a specific window (this is a WECG proposal)
Added support for the optional_host_permissions manifest key in manifest_version 3 extensions (this is a WECG proposal)
Added support for browser.runtime.getFrameId() so it easier to get frame identifiers from content scripts (this is a WECG proposal)
Added support for the frameId option that can be passed to browser.tabs.sendMessage()
Fixed the number returned by parentFrameId for webNavigation and webRequest events to be -1 when it is the main frame
Fixed devtools.panels.onShown so the window object of the panel is sent to the listeners
Fixed devtools.panels.onShown to prevent it from firing multiple times per active panel change
Made the devtools API namespace only exposed to the devtools background pages
With over 70 additions to WebKit, Safari 15.4 is packed with new web technologies, updates, and fixes. We’ve assembled a huge release as part of our commitment to web developers, and the people who use the web. This is the first big WebKit release of 2022, and we’re just getting started.
Safari 15.4 is available for macOS Monterey 12.3, macOS Big Sur, macOS Catalina, iPadOS 15.4, and iOS 15.4. You can update to Safari 15.4 on macOS Big Sur and macOS Catalina by going to System Preferences → Software Update → More info, and choosing to update Safari.
HTML
Let’s start with HTML. WebKit added support for lazy-loading images with the loading attribute on the <img> element, providing web developers with an easy way to instruct the browser to defer loading certain images until the user scrolls near them.
After years of standardization debates over accessibility considerations and with a solution finally at hand, WebKit added support for the <dialog> element and ::backdrop pseudo-element. The <dialog> element provides a robust and powerful way to create overlays and modals.
<dialogid="confirmation-dialog"><h1>Do you want to delete everything?</h1><p>You will lose all your data.</p><buttonid="cancel-delete">Cancel</button><buttonid="confirm-delete">Delete!</button></dialog>
The ::backdrop pseudo-element makes it possible to style the background underneath the modal.
WebKit also added support for the global autofocus attribute allowing developers to indicate which element should be the one in focus when the page loads or when a <dialog> is displayed.
CSS
Features for CSS Architecture
Several additions to CSS in 2022 offer revolutionary new ways for web developers to architect their code, making it easier to reuse code, create design systems, and integrate with complex applications.
Landing in Safari first, WebKit added support for the :has() pseudo-class. This selector fulfills a long-expressed desire for a “parent selector” — a way to apply CSS rules conditionally based on the contents of an element — and goes even further with the possibilities it enables. It was long thought such a selector was not possible, but our team figured out a way to highly-optimize performance and deliver a flexible solution that does not slow the page.
WebKit added support for Cascade Layers — a powerful way to organize styles into layers where specificity is calculated independently inside each layer.
A web developer could create a “framework” layer and a “custom” layer — assigning all the CSS from a 3rd-party framework to the “framework” layer, and writing their own code in the “custom” layer. They could designate that everything in the custom layer should beat everything in the framework layer, no matter the specificity of the selectors used in each layer. Cascade Layers is arriving in all major browsers at about the same time and is included in Interop 2022, ensuring this is a tool web developers can begin to seriously consider for the future.
WebKit also added support for CSS Containment — all four types: size, layout, style, and paint — with the contain property.
Solving Pain Points
Several more additions to CSS in WebKit introduce solutions to long-standing pain points.
Web developers often ask for a tool that would work similar to existing viewport units, but work better on mobile devices where the dimensions of the browser’s viewport change as a user scrolls the page. The new Viewport Units are that solution. 100svh refers to 100% of the height of the smallest possible viewport. 100lvh refers to 100% of the height of the largest possible viewport. And 100dvh refers to 100% of the dynamic viewport height — meaning the value will change as the user scrolls.
There are other new viewport units as well — svw, lvw, and dvw serve the same purpose for width. To cover the small, large, and dynamic versions of vmin and vmax, the svmin, svmax, lvmin, lvmax, dvmin, and dvmax units were implemented. To support logical dimensions, the new vi and vb are similar to existing Viewport Units, in the viewport inline and viewport block dimensions. And svi, svb, lvi, lvb, dvi, and dvb provide logical dimension units for the small, large, and dynamic versions of the inline and block dimensions. WebKit is happy to lead the pack, shipping these new units first and encouraging other browsers to do so through Interop 2022.
To make native form controls more customizable, the accent-color property provides a way for web developers to alter the color of particular parts of the form control UI. Accent color is supported for <input type="checkbox">, <input type="radio">, <progress>, <select>, and text-input types with a <datalist> on macOS, iPadOS, and iOS. Additionally, on iPadOS and iOS, accent color is supported for <input type="range">, <button>, and <input type="button">.
WebKit fixed a bug with the interpolation between colors with alpha transparency — improving gradient support.
WebKit added support for calc() math functions including sin, cos, tan, e, pi, exp, log, atan, acos, asin, and atan2.
Typography
Several new WebKit features in Safari 15.4 enrich what’s possible in typography on the web.
WebKit added support for the font-palette CSS property and @font-palette-values rule. The font-palette property provides a way for web developers to select one of several different pre-defined color palettes contained inside a color font — for example, to declare that a font’s dark color palette be used for the site’s dark mode design. The @font-palette-values rule provides a way for web developers to define their own custom color palette for recoloring color fonts.
The color font used for the enlarged caps is Bradley Initials DJR Web, shown here with its default palette, a customized palette created by the web developer, an alternative palette that’s included with the font, and with color removed by the user’s preference.
WebKit added support for text-decoration-skip-ink to control how underlines and overlines are rendered when they pass over glyph ascenders and descenders. WebKit previously supported this typography feature through text-decoration-skip, but since no other browsers yet support this short-hand, WebKit’s support of the long-hand will make it easier to turn off ink skipping for underlines and overlines.
WebKit added support the ic unit, useful when typesetting CJK scripts. Much like how the ch unit is equivalent to the width (or height, whichever is the inline direction) of the 0 glyph in a font, the ic unit is equivalent to inline direction length (width or height) of the “水” glyph in the element’s current font.
Retiring WebKit prefixes
In an ongoing effort to reduce dependency on prefixes, WebKit newly supports several CSS properties and values that were only previous available in an earlier form. The prefixed versions will still work, now aliased to the unprefixed versions. Safari 15.4 added support for:
appearance, including appearance: auto
mask, along with the long-hand forms mask-image, mask-size, mask-repeat-x, mask-repeat-y, mask-origin
backface-visibility
text-combine-upright
print-color-adjust
match-parent CSS value for the text-align property
WebKit also removed the non-standard CSS properties -webkit-border-fit, -webkit-margin-collapse, -webkit-margin-top-collapse, -webkit-margin-bottom-collapse, -webkit-margin-before-collapse, -webkit-margin-after-collapse, and -webkit-background-composite.
Web APIs
This release includes many upgrades to Web APIs in WebKit to help web developers deliver better user experiences.
New support for BroadcastChannel allows tabs, windows, iframes, and Workers from an origin to send messages to each other. This enables experiences like syncing login state for a site across multiple tabs.
Another new mechanism supported in WebKit is the Web Locks API to manage access to a resource as an asynchronous locking control from an origin in tabs, windows, iframes, and Workers.
Developers can also control scroll behavior for an element with either the CSS scroll-behavior property or the behavior option in window.scroll(), window.scrollTo(), and window.scrollBy() methods in JavaScript. This new support gives developers the ability to choose between instantly jumping to a position in the viewport or smoothly animating the scroll operation.
The ResizeObserver API has updated support for the ResizeObserverSize interface used by ResizeObserverEntry to help developers observe changes to an element’s box-sizing properties.
The addition of structuredClone(value) provides a utility that uses the structured clone algorithm to synchronously perform a deep copy to clone and transfer objects from the input value.
WebKit support of the File System Access API with Origin Private File System first shipped in Safari 15.2. This release introduces the getFile() method in FileSystemFileHandle making it more convenient to read a file from the file system. Plus, WebKit updated WriteableStream to work with the File System Access API. For more information, read File System Access API with Origin Private File System.
JavaScript
New features and updates to JavaScript bring added convenience for developers. Handy new Array features make it nicer to search starting from the end of an array using the findLast() and findLastIndex() methods. These methods help developers avoid the typical approach requiring mutating the array with reverse() first.
There’s also support for the at() method to access an entry at a specified integer index, which notably includes support for using negative integers to start at the end of the array.
letlist= ['banana','cherry','orange','apple','kiwi'];
// Instead of this:
console.log(list[list.length-2]);
// It's as easy as:
console.log(list.at(-2));
The new language utility Object.hasOwn() simplifies detecting when the object has a property itself, one that is not inherited or doesn’t exist.
Internationalization
As the standards process defines more Internationalization features, WebKit continues to add regular updates to its Intl implementation. This release includes identifying the supported values of local time zones, collations, calendars, numbering systems, and currency with the Intl Enumeration API.
Intl.Locale, updated to V2, exposes new information that includes calendar-week data such as the first day of the week, text information like writing direction, and other region-dependent defaults such as calendars, 12- or 24-hour cycles, and numbering systems.
WebKit also updated Intl.DisplayNames to V2, adding support for the calendar and dateTimeField names, and the languageDisplay option.
The selectRange() method added to Intl.PluralRules provides locale-correct pluralization for ranges (e.g. 0-1 items). The Intl.NumberFormat V3 update adds the formatRange() and formatRangeToParts() methods for formatting a number range using locale-aware conventions along with new useGrouping, roundingPriority, roundingIncrement, trailingZeroDisplay, and signDisplay options.
Finally, Intl.DateTimeFormat includes support for four new timeZoneName options: shortOffset, longOffset, shortGeneric, and longGeneric.
Web Apps
Web App Manifest and ServiceWorker received updates that improve the user experience for both websites in Safari and web apps saved to the home screen on iOS and iPadOS.
Web App Manifest improvements include ensuring the browser always fetches the manifest file during page load instead of when the user chooses to “Add to Home Screen” from the Share menu. This approach improves reliability, and also allows a manifest file to define the characteristics of a webpage in Safari.
In addition, declaring icons in a web app manifest file is now supported. Safari and iOS use manifest-declared icons when there is no apple-touch-icon defined in the HTML head, and when the manifest file code for declaring the icons either omits the "purpose" key or includes "purpose": "any". Defining icons by using apple-touch-icon takes precedence over manifest-declared icons in order to provide consistent behavior for web apps that use this technique to define specific icons for iOS, distinct from other mobile platforms.
Developers can now enable Navigation Preload in ServiceWorker to improve load performance and avoid ServiceWorker startup delays that block network requests. There’s also new support for allowing users to download files generated by a ServiceWorker. WebKit also improved the reliability of using Fetch using FormData with a file going through ServiceWorker.
Media
The WebRTC negotiation API is now fully aligned with the WebRTC 1.0 specification to support WebRTC perfect negotiation. It is an approach that solves potential synchronization issues that can happen during negotiation between two remote peers.
WebKit added support for in-band chapter tracks for audio and video. In-band text tracks provide captions or chapter marker information inside the container for the media itself, instead of being declared in HTML or injected with JavaScript. In-band caption tracks like CEA-608 were already supported. Now, in-band chapter tracks are also supported, where the “cue” represents the start time and title of a chapter.
WebKit added support for requestVideoFrameCallback() on <video>, which allows the caller to be notified when there’s a new video frame available for display, and also provides metadata about that frame.
Privacy
Continuing our dedication to privacy, and to further our proposed web standard for measuring advertising in a privacy-preserving way, Safari 15.4 provides three updates to Private Click Measurement:
Added conversion fraud prevention via unlinkable tokens for triggering events on merchant websites.
Added support for same-site conversion pixels on merchant websites, to remove dependency on cross-site pixels.
Allowed measurement of links in nested, cross-site iframes on publisher websites.
Security
WebKit in Safari 15.4 improves support for Content Security Policy Level 3, providing enhanced security control over the loading of content, and helping web developers to mitigate risks of cross-site scripting and other vulnerabilities. Blocked resource violation reporting for inline script, inline style and eval execution is updated to match web standards. New support for 'strict-dynamic', 'unsafe-hashes', and 'report-sample' source expressions give developers more flexibility. Developers can also safely include external JavaScript in their pages using new support for hash source expressions.
The release also removes support for the XSS Auditor, which has been superseded by modern cross-origin defenses like CSP and COEP.
WKWebView
Developers that use WKWebView, including third-party browsers on iOS and iPadOS, can make use of new WKPreferences for additional user experience control. Apps on iOS, iPadOS, and macOS can now control allowing or preventing web content from using the Fullscreen API. Another new preference allows enabling or disabling site-specific quirks, a set of site-specific behaviors designed to improve web compatibility.
On iPadOS, web content that uses Media Source Extensions now works in WKWebView.
Safari Web Extensions
In our ongoing commitment to a cross-browser interoperable model for extensions, Safari 15.4 includes additional support for Web Extensions, including support for manifest_version 3 and related API changes. New capabilities include:
service_worker background scripts as an alternative to non-persistent background pages.
Script and style injection via the browser.scripting APIs.
Dynamic and session rules via the browser.declarativeNetRequest APIs.
Webpage-to-extension messaging using externally_connectable:matches.
And several issues were resolved, including:
Limits are now enforced on the size and number of items in extension sync storage.
More directives are now allowed to be included in the content_security_policy of an extension’s manifest, such as the sandbox directive.
Special matching characters (*, |, ||, and ^) in urlFilter of declarativeNetRequest rules are now handled, instead of being treated as regex patterns.
Promise returns from runtime.onMessage listeners are now allowed for the message reply.
Web Inspector
Updates to Web Inspector provide helpful new tools for working with CSS in the Styles panel, including intuitive support for Cascade Layers and the new @layer rulesets, making it easy to identify in which layer a rule is defined.
There are also new CSS Alignment controls when using align-content, align-items, align-self, justify-content, justify-items, or justify-self for Flexbox and Grid to visually identify and select an ideal value.
While adding new properties or values in the Styles panel, Web Inspector offers convenient auto-completion options. This release upgrades auto-completion to use fuzzy matching, making finding the right option even easier.
When working with CSS custom properties, or CSS variables as they’re more widely known, a common practice is to add them to a selector rule for :root or html. Unfortunately, this leads to a long list of inherited CSS variables for every element on the page. Web Inspector helps you handle this in a few ways. First, it hides unused inherited CSS variables automatically. Then, a button is available to reveal them all when you need to see them. You can also use the filter tools to search for the right CSS variable. Or, you can see all applicable CSS variables grouped by value type in the Computed panel, allowing you to collapse the groups that aren’t relevant to your task.
The list of applicable CSS variables can be grouped by value type into collapsible subsections: colors, dimensions and other types.
We love hearing from you. Send a tweet to @webkit, @jensimmons or @jonathandavis to share your thoughts on this release. What technology from Safari 15.4 are you most excited about? What features or fixes do you want to see next? If you run into any issues, we welcome your feedback on the Safari UI or your WebKit bug report about web technology. 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 use the WebKit Feature Status page to watch for new information about the web features that interest you the most.
And More
To see the full list of what’s in WebKit for Safari 15.4, including additional bug fixes, read the Safari 15.4 release notes.
Safari Technology Preview Release 141 is now available for download for macOS Big Sur and of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Enabled automatic collapsing of blackboxed call frames (r288580)
Network Tab
Collapsed resource type filter scope bar into a single button to save space (r288469)
Changed the Ignore Caches icon to be a button with a label so it’s more visible and immediately understandable (r288533)
Moved the Group Media Requests and Preserve Log checkboxes into a single filter icon that shows a contextmenu with those options when clicked to save space (r288470)
Graphics Tab
Added the display of more pseudo-elements than ::before and ::after (r288623)
Console Tab
Moved the Preserve Log checkbox into a single filter icon that shows a contextmenu with those options when clicked to save space (r288702)
From the very beginning, the web was always intended to work in any browser, on any computer. This is possible through interoperability — when each underlying web technology is implemented in the same way in every browser. To reach interoperability, it takes a commitment from all browser engineers to implement web technology according to web standards — the incredibly detailed specifications where new technology is defined.
In 2022, Apple, Bocoup, Google, Igalia, Microsoft, and Mozilla have come together to commit to improve interoperability in 15 key areas that will have the most impact on web developer experience, in a project called Interop 2022.
At its root, Interop 2022 is an evolving metric generated from a set of automated tests that aims to evaluate support for certain web standards that are most important for web developers. The Interop 2022 dashboard will constantly update throughout the year, showing progress as browser engineers fix bugs, implement new features, and improve the tests.
The current overall score on the Interop 2022 dashboard on March 3, 2022.
The group planning Interop 2022 chose ten new focus areas to add to the five areas from Compat 2021. We also committed to several investigation projects, which will begin this spring.
The scoring breakdown on the Interop 2022 dashboard on March 3, 2022.
Focus areas
Chosen from a list of proposals, with an eye to what web designers & developers want and need most, the ten new focus areas for 2022 are:
Cascade Layers
Color Spaces and Functions
Containment
Dialog Element
Form Fixes
Scrolling
Subgrid
Typography and Encodings
Viewport Units
Web Compat
Let’s take a quick tour of each.
Cascade Layers
Designed to soothe the frustrations of web developers wrestling with CSS on large projects, Cascade Layers provides a powerful way to organize styles into layers, where specificity is calculated independently inside each layer.
A website could create a “framework” layer and a “custom” layer — assigning all the CSS from a 3rd-party framework to framework layer, writing their own code in the custom layer. They could designate that everything in the custom layer should beat everything in the framework layer, no matter the specificity of the selectors being used.
Color Spaces and Functions
In the early days of the web, most sites restricted their use of color to a specific palette of 216 colors. Then for a long time, web developers used anything in the sRGB color space, and typically expressed those colors in hexadecimal, rgb(), rgba(), or hsl(). Meanwhile, camera and monitor technology have greatly evolved to capture and display a wider and brighter range of colors. Today’s Apple displays support the Display P3 color space, which is about 50% wider than sRGB.
New color functions and support for new color spaces bring this vibrancy to the web. Interop 2022 includes testing for support of three expanded color spaces (LAB, LCH, P3), and two ways to write color in CSS through functional notation: color-mix and color-contrast.
For several years now, web developers’ number one most requested addition to the web has been Container Queries. It will be a powerful tool in CSS for identifying and measuring the size of a specific container, and then conditionally applying styles based on that size. It’s like media queries, but instead of measuring the size of the viewport, you measure the size of a box holding the content.
Containment is foundational to making Container Queries work. In fact, Container Queries is defined in level 3 of the Containment specification. The group driving Interop 2022 didn’t come to consensus to include Container Queries this year. But we did agree to focus on the interoperability of layout, size, and paint containment through the containment property, setting the stage for prioritizing the interoperability of the rest of Containment and Container Queries in the future.
Dialog Element
Another long-requested feature for the web, the dialog element provides a robust and powerful way to create overlays and modals. The ::backdrop pseudo-element makes it possible to style the background underneath the modal. You can learn more about how to use <dialog> and ::backdrop in Introducing the Dialog Element.
Form Fixes
Forms are another area where web designers and developers find interoperability challenges — ones that the Open UI community group and appropriate standards bodies are working to solve. Interop 2022 is contributing to this work by focusing on improving the pass rates for existing tests of existing specs. This includes the appearance property, <form>, events on disabled form controls, and bugs with input elements, form submission, and form validation.
Scrolling
Today’s websites and web apps care more deeply about how scrolling works than ever before. Scroll snap provides the tools for designers and developers to control how interfaces scroll and how content appears. The scroll-behavior property in CSS sets the behavior for a scrolling box when scrolling is triggered by the navigation or CSSOM scrolling APIs. The overscroll-behavior CSS property determines what a browser does when reaching the boundary of a scrolling area.
Subgrid
CSS Grid shipped five years ago in March 2017, revolutionizing what’s possible in layout design on the web. Subgrid is defined in CSS Grid level 2, and provides an easy way to put grandchildren of a grid container on that grid. It will make it possible to line up items across complex layouts, without any regard for the DOM structure. The vision of a working layout system on the web will be more fully realized with Grid and Subgrid together.
Typography and Encodings
Typography and encodings encompasses a collection of tests that impact typography on the web. Font Features are powerful properties for refining typography, but incomplete support has been making them harder to use then they’re supposed to be. The vast majority of the encoding tests pass in every browser, but a handful do not, so they’ve been included. And the ic unit is included.
Viewport Units
Web developers often ask for a tool that would work similar to viewport units, but work better on mobile devices where the dimensions of the browser’s viewport change as a user scrolls the page. The new Viewport Units are that solution. 100svh refers to 100% of the height of the smallest possible viewport. 100lvh refers to 100% of the height of the largest possible viewport. 100dvh refers to 100% of the dynamic viewport height — meaning the value will change as the user scrolls.
There are other new viewport units as well — svw, lvw, and dvw serve the same purpose, for width. And there are new units to refer to the inline or block dimensions of the viewport.
Web Compatibility
There are many scenarios that could impact web compatibility. For example, specific bugs in browsers could disproportionately cause some websites to not work as intended, or perhaps one browser may vary from the web standard, causing an inconsistent and buggy experience for website or web app users. Interop 2022 aims to capture and address these issues through the web compatibility measurement.
Investigation Projects
There were several other items that the group knows are important to web development, but which cannot yet be easily evaluated by automated testing. We’ve committed to embark on several investigations in these areas — to manually test browsers, to determine if and how automated testing could be more helpful, to improve the infrastructure of WPT itself, to discover where there might still be a lack of interop, and to make suggestions to the appropriate standards groups. The three areas of investigation are:
Editing, contenteditable, and execCommand
Pointer and Mouse Events
Viewport Measurement
Each browser will always have the same result for “2022 Investigation”, based on how much work has been accomplished by the group as a whole.
Our Commitment to Interoperability
All of these technologies are important to Apple and to everyone working on WebKit. We care deeply about the health of the web, and interoperable implementations of web standards. We welcome collaboration with our colleagues in the many web standards organizations, and in Interop 2022 to make the web as interoperable as it can be. Because that’s how websites and web apps will work best for the people who matter most — everyday people using the web to live their lives.
CSS Custom Properties, better known as CSS variables, have been widely adopted by web designers to build reusable and configurable design systems. One common approach is to define most CSS variables in a CSS rule with a selector for a root element such as html or :root.
While this has the benefit of putting them all in one easy to find place, it has a side effect: because CSS variables are inheritable, all descendant elements effectively inherit all variables from ancestors. This behavior is what enables you to use a CSS variable defined at a higher level on the styles of an element that is deeply nested within the document.
Descendant elements inherit all CSS variables from their ancestor elements. This can result in very long lists of inherited properties in the Styles panel.
When an element inherits a large number of CSS variables, inspecting its styles can become overwhelming. Likewise, identifying a particular CSS variable to reuse becomes more difficult because you have to search through a large list of them.
Over the past few releases, Web Inspector in Safari Technology Preview has introduced some features to help you when working on projects that use large numbers of CSS variables.
Hiding Unused CSS Variables
Of all inherited CSS variables, only a few are actually used on the styles of any one element. To reduce visual clutter in the Styles panel, unused inherited CSS variables are automatically hidden. They’re replaced with a button that reveals them on click.
This helps focus your attention on just the styles that took effect on the inspected element.
Unused inherited CSS variables are automatically hidden behind a button that reveals them on click.
Searching for CSS Variables
The Computed panel in Web Inspector has a section that lists all CSS variables applicable to the inspected element. This list of properties can help you when searching for a CSS variable to reuse.
Use the filter input field to narrow down the list if you know roughly what you’re looking for, either part of the CSS variable name or part of the value.
Find all CSS variables applicable to the selected element in the Variables section of the Computed panel. Filter the list using the filter input field at the bottom of the panel.
Grouping CSS Variables
Safari Technology Preview 138 introduced the ability to view this list grouped by value type. This creates separate subsections for CSS variables with values such as colors, numbers, dimensions (numbers followed by CSS units), and so forth. Reduce clutter by collapsing the groups you’re not interested in.
The list of applicable CSS variables can be grouped by value type into collapsible subsections: colors, dimensions and other types.
Grouping this way can help you find a CSS variable when you know the type of value you’re looking for, a particular color, for example. Color swatches shown next to variable values together with the ability to group all variables with color values into one distinct section make it easier to visually scan for the desired value.
Jump to CSS Variable Definition
Here’s a tip: place the mouse cursor over any CSS variable in the Computed panel to reveal a go-to arrow. Click this to highlight the place in the Styles panel where the variable is defined. If the target CSS variable is hidden because it is unused, it will be automatically shown. This allows you to quickly jump in context to the place where a CSS variable is defined and edit it.
Use the go-to arrow next to CSS variables in the Computed panel to highlight where the variable is defined in the Styles panel.
You can also use the filter input field at the bottom of the Styles panel (as described above) and type the name of the variable, but using the go-to arrow to quickly jump to it is much more convenient.
Fuzzy Autocompletion of CSS Variable Names
Since its inception, Web Inspector has provided autocompletion for CSS properties and values in the Styles panel. More recently, it introduced the ability to provide completion suggestions for CSS variable names when typing within var() function values, as in var(--link-color).
Safari Technology Preview 138 made this even better with the introduction of fuzzy matching for CSS autocompletion. This is particularly useful for CSS variables when you might not remember the full name. With fuzzy matching, you can get results that match the query at any position, not just at the beginning.
For example, if you know that the CSS variable name you’re looking for, say --link-color, includes the substring “color”, you can type just var(color|) (the | character represents the position of the typing caret). You don’t even need to type the double dash prefix. If the CSS variable --link-color is defined on or inherited by the inspected element, it will be shown in the list of completion suggestions even if the query match occurs at the end of the variable name.
Enjoy the flexibility of fuzzy matching for CSS autocompletion to quickly find a variable by typing any part of its name. For example, type just “color” to match --link-color.
Conclusion
CSS variables enable a growing number of uses, such as building configurable design systems, practical theming for light and dark modes, customizable styles for web components, among many others. But the proliferation of variables in large numbers within a project can also become a burden during development and debugging.
Web Inspector has introduced features to help keep you focused and productive. Hiding unused inherited CSS variables in the Styles panel reduces clutter. Collecting and grouping CSS variables in the Computed panel focuses your attention. Fuzzy matching in autocompletion of variable names and filter input fields help you quickly find and reuse variables.
We hope these improvements make your work easier when dealing with CSS variables.
As always, if you encounter any issues, please file a report at webkit.org/new-inspector-bug.
If you want to share feedback or ideas, please send them to us on Twitter: @webkit.
It is very common for an application to interact with local files. For example, a general workflow is opening a file, making some changes, and saving the file. For web apps, this might be hard to implement. It is possible to simulate the file operations using IndexedDB API, an HTML input element with the file type, an HTML anchor element with the download attribute, etc, but that would require a good understanding of these standards and careful design for a good user experience. Also, the performance may not be satisfactory for frequent operations and large files.
The File System Access API makes it possible for web apps to have easy and efficient file access. It provides a way to create, open, read, and write files directly. It also allows apps to create directories and enumerate their contents.
Origin Private File System
WebKit has added support for the File System Access API with the origin private file system — a private storage endpoint to some origin. Conceptually, every origin owns an independent directory, and a page can only access files or directories in its origin’s directory. For example, https://webkit.org cannot read files created by https://apple.com.
Based on the implementation of different browsers, one entry in the origin private file system does not necessarily map to an entry in the user’s local filesystem — it can be an object stored in some database. That means a file or directory created via the File System Access API may not be easily retrieved from outside of the browser.
Persistence
The API is currently unavailable for Safari windows in Private Browsing mode. For where is it available, its storage lifetime is the same as other persistent storage types like IndexedDB and LocalStorage. The storage policy will conform to the Storage Standard. Safari users can view and delete file system storage for a site via Preferences on macOS or Settings on iOS.
Browser Support
The File System Access API with origin private file system is enabled in WebKit from r284131. It is available in Safari on:
macOS 12.2 and above
iOS 15.2 and above
In Safari on macOS 12.4 and iOS 15.4, we introduced the getFile() method of FileSystemFileHandle.
The API
WebKit currently supports four interfaces of the File System Access API:
FileSystemHandle, which represents an entry in the file system. It is available in Worker and
FileSystemFileHandle, which inherits from FileSystemHandle and represents a file entry.
FileSystemDirectoryHandle, which inherits from FileSystemHandle and represents a directory entry.
FileSystemSyncAccessHandle, which provides an exclusive duplex stream for synchronous read and write on an entry. Unlike the interfaces above, which exist in both Window and Worker contexts, FileSystemSyncAccessHandle is only available in Worker.
With these basic interfaces in mind, let’s look at how to use them by diving into some examples.
Examples
Accessing the Origin Private File System
In the origin private file system, a FileSystemHandle represents either the root directory of the origin’s space, or a descendant of the root directory. Therefore, the first step is to get the root FileSystemDirectoryHandle. It is done via StorageManager interface.
constroot=awaitnavigator.storage.getDirectory();
Creating a directory or a file
With a FileSystemDirectoryHandle object like root, you can get access to its child with some specific name using getDirectoryHandle() and getFileHandle() methods.
// Create a file named Untiled.txt under root directory.
constuntitledFile=awaitroot.getFileHandle("Untitled.txt", { "create":true });
// Get access to existing Untitled.txt file.
// untitledFile and existingUntitledFile point to the same entry.
constexistingUntitledFile=awaitroot.getFileHandle("Untitled.txt");
// Create a directory named Diary Folder.
constdiaryDirectory=awaitroot.getDirectoryHandle("Diary Folder", { "create":true });
Moving or Renaming a Directory or a File
To move around the file or directory a FileSystemHandle represents, you can use the move() method. The first parameter is a FileSystemDirectoryHandle representing the target parent directory, and the second parameter is a USVString representing the target file name. The string must be a valid file name.
// Move Untitled.txt from /root/ to /root/Diary Folder/.
awaituntitledFile.move(diaryDirectory, untitledFile.name);
// Rename Untitled.txt to Feb_01.txt
awaituntitledFile.move(diaryDirectory, "Feb_01.txt");
// The two steps above can be combined as:
// await untitledFile.move(diaryDirectory, "Feb_01.txt");
Resolving the Path from a Directory Entry to its Descendant
To find out if a FileSystemHandle is a descendant of an existing FileSystemDirectoryHandle, and to get their relative path, you can use the resolve() method. The result is an array of component names that forms the path.
// Get access to Feb_01.txt in Diary Folder.
constdiaryFile=awaitdiaryDirectory.getFileHandle("Feb_01.txt");
// Resolve path between Feb_01.txt and root.
constrelativePath=awaitroot.resolve(diaryFile);
// relativePath is ["Diary Folder", "Feb_01.txt"].
Enumerating Contents in a Directory
The methods introduced above require you to know the name of target, but if you don’t know the name, you can still get it by enumerating the contents of an existing directory with async iterators returned by the keys(), values(), and entries() methods.
// Create a directory named Trash under the root directory.
consttrashDirectory=awaitroot.getDirectoryHandle("Trash", { "create":true });
// Find directories under root/ and print their names.
constdirectoryNames= [];
forawait (consthandleofroot.values()) {
if (handle.kind=="directory") {
directoryNames.push(handle.name);
}
}
// directoryNames is ["Trash", "Diary Folder"].
Deleting a Directory or a File
With a FileSystemDirectoryHandle object, you can delete its child entries by name with the removeEntry() method.
// Delete Feb_01.txt in Diary Folder.
awaitdiaryDirectory.removeEntry(diaryFile.name);
// Delete Trash and all its descendants.
awaitroot.removeEntry(trashDirectory.name, { "recursive":true });
Reading a File
Once you have the FileSystemFileHandle representing the target file, you can read its properties and content by converting it to a File object using the getFile() method. You can get file information and content using interfaces of File.
Another way to read a file is to use the read() method of the FileSystemSyncAccessHandle interface. You can create a FileSystemSyncAccessHandle from a FileSystemFileHandle object using the createSyncAccessHandle() method. Since FileSystemSyncAccessHandle is only available in Worker contexts, you will need to create a dedicated Worker first.
Unlike getFile() that returns a Promise, read() is synchronous, and thus provides better performance. If you’re aiming for the most efficient file access, FileSystemSyncAccessHandle is the way to go.
To write a file, you can use the synchronous write() method of FileSystemSyncAccessHandle. In the current implementation, this is the only way to write a file in WebKit.
To implement synchronous read and write operations, a FileSystemSyncAccessHandle must have exclusive access to a file entry. Therefore, the attempt to create a second FileSystemSyncAccessHandle on an entry will fail, if the previous FileSystemSyncAccessHandle is not closed properly.
// Get access to the existing Draft.txt file.
constroot=awaitnavigator.storage.getDirectory();
constdraftFile=awaitroot.getFileHandle("Draft.txt");
// Create FileSystemSyncAccessHandle on the file.
constaccessHandle=awaitdraftFile.createSyncAccessHandle();
// Get size of the file.
constfileSize=awaitaccessHandle.getSize();
// Read file content to a buffer.
constreadBuffer=newArrayBuffer(fileSize);
constreadSize=accessHandle.read(readBuffer, { "at":0 });
// Write a sentence to the end of the file.
constencoder=newTextEncoder();
constwriteBuffer=encoder.encode("Thank you for reading this.");
constwriteSize=accessHandle.write(writeBuffer, { "at":readSize });
// Truncate file to 1 byte.
awaitaccessHandle.truncate(1);
// Persist changes to disk.
awaitaccessHandle.flush();
// Always close FileSystemSyncAccessHandle if done.
awaitaccessHandle.close();
Summary
If your web app needs to interact with files, you should try the new File System Access API. It provides interfaces that are similar to the native file system API, with optimized performance.
As the standard evolves and development goes on, we will keep adding or updating interfaces and methods according to the File System Access API spec. If you encounter any issue when using this API, please file a bug on bugs.webkit.org under the “Website Storage” component. You may also create a new bug report for feature requests, describing your use case and why the feature is important. If you have any question or suggestion about the API itself, you can file a spec issue in the WICG repo. Your feedback is very important to us.
Safari Technology Preview Release 140 is now available for download for macOS Big Sur and of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Although the alert, confirm and prompt JavaScript methods are convenient, they aren’t recommended due to their script-blocking behavior. That’s why we worked with other browser vendors to drive improvements to the <dialog> specification over the last few years. The most important conversations involved accessibility.
You can find more complex use-cases, like payment dialogs, on the web. They are currently addressed by custom solutions from frameworks like Bootstrap. Unfortunately, they aren’t convenient to use and aren’t always accessible. We believe the web deserves a simple and bug-free solution for these use-cases. Safari Technology Preview 134 and Safari 15.4 beta introduces the <dialog> element for this reason!
How Do I Use <dialog>?
Let’s create a simple confirmation dialog:
<dialogid="confirmation-dialog"><h1>Do you want to delete everything?</h1><p>You will lose all your data.</p><buttonid="cancel-delete">Cancel</button><buttonid="confirm-delete">Delete!</button></dialog>
Dialogs are hidden by default. We can use the showModal() method to show the dialog. When it’s shown, the dialog can be closed with the close() method.
Here is an example:
<buttonid="delete">Delete everything</button><pid="result"></p><script>
let dialog = document.getElementById("confirmation-dialog");
let result = document.getElementById("result");
// Show the dialog when clicking "Delete everything"
document.getElementById("delete").addEventListener("click", function() {
dialog.showModal();
});
document.getElementById("cancel-delete").addEventListener("click", function() {
dialog.close();
result.textContent = "Canceled!";
});
document.getElementById("confirm-delete").addEventListener("click", function() {
dialog.close();
result.textContent = "Deleted!";
});
</script>
Note that the dialog will get an open attribute once opened, which may be useful for styling purposes. However, it’s not recommended to toggle this attribute manually to show or hide the dialog, since the browser may lose track of the dialog state, and will not perform proper focus adjustments for accessibility.
Example confirmation dialog
Modal and Non-modal Dialogs
In the last example, the showModal() method was used to create a modal dialog. User interaction is locked inside modal dialogs and outside content cannot be clicked, focused, selected, edited, or seen by accessibility tools. Another feature of modal dialogs is their ability to appear on top of everything else in the web page, regardless of the z-index of other elements.
Non-modal dialogs also exist and can be invoked using show() method. Unlike modal dialogs, they still allow interaction with the surrounding content. An example use-case may be a find-in-page dialog for a document editor, where you still want to allow the user to interact with the rest of the document.
Using Forms with <dialog>
Forms within dialogs can be used to request information from the user, such as when a shipping address or payment details are needed.
Unlike a traditional <form>, where method="get" or "post" indicates that the form data is sent to a server, using <form method="dialog"> causes form submission instead to close the dialog and set the returnValue property to the submit button’s value. This can save you from writing custom code, while providing the correct semantics to your web page.
We can simplify the initial example using this new feature:
<dialogid="confirmation-dialog"><formmethod="dialog"><h1>Do you want to delete everything?</h1><p>You will lose all your data.</p><buttontype="submit"value="Canceled!">Cancel</button><buttontype="submit"value="Deleted!">Delete!</button></form></dialog><buttonid="delete">Delete everything</button><pid="result"></p><script>
let dialog = document.getElementById("confirmation-dialog");
document.getElementById("delete").addEventListener("click", function() {
dialog.showModal();
});
dialog.addEventListener("close", function() {
document.getElementById("result").textContent = dialog.returnValue;
});
</script>
Note the use of the close event here, which is special to <dialog>.
Example confirmation dialog with a form
Styling
The semi-transparent box behind the dialog that you may have noticed from previous examples is the ::backdrop pseudo-element. By default, it is styled so it covers the whole viewport. Like the dialog itself, you can style the backdrop using CSS. Animations can also be used if you would like to add a fade-in effect for instance.
Note that the backdrop is only shown for modal dialogs.
Here is an example:
<dialog><h1>This is a pretty dialog</h1><p>The backdrop animates!</p></dialog><buttononclick="document.querySelector('dialog').showModal()">Show the dialog</button><style>
dialog {
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
border: none;
border-radius: 10px;
}
dialog::backdrop {
background: linear-gradient(rgba(0,0,0,0.1), rgba(0,0,0,0.4));
animation: fade-in 1s;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>
This is a pretty dialog
The backdrop animates!
Pretty dialog example styled and animated
Accessibility
For accessibility tools, the <dialog> element is equivalent to role="dialog". In addition to that, a modal dialog will behave similarly to an element with aria-modal="true".
Users can dismiss modal dialogs using the “Escape” key on desktop browsers. That will trigger a cancel event which you can intercept. If multiple modal dialogs are opened, the one shown last will be dismissed.
It is also possible to specify an element to initially focus on when opening dialogs by adding the autofocus attribute to the relevant element.
In this post, we’ve covered the basics of <dialog> and the features around it. Here are the next steps around this element:
We are working on getting the element interoperable with other browser vendors as part of the Interop 2022 effort. One of the main discussions is around initial focus behaviour, to agree on which elements should be focused by default when there is no element with the autofocus attribute.
As part of implementing <dialog> we’ve also made advances on the inert attribute to get interoperable behavior across browsers. It is currently disabled by default and not yet standardized, but you can enable the “inert attribute” in the Experimental Features menu from the Develop menu in Safari Technology Preview to test it.
Feel free to reach out to @therealntim on Twitter for any questions. To report any issues, please file a bug blocking bug 84635.
The “focus indicator”, as its name suggests, visually indicates (often with a kind of an outline) that an element has focus. That sounds simple enough, and it perfectly describes what the old :focus selector makes possible. However, for decades now, whether a browser will actually display a focus indicator natively has been a considerably more complex affair.
Focus indicator on different elements in Safari
Based on lots of feedback and study, browsers have long employed heuristics about both the type of element, and how it came to gain focus, in order to determine whether the focus indicator should be displayed. If the user is navigating the page with the keyboard, it should. In other situations, it depends. A text input, for example, will display an indicator regardless of how it received focus. This is important because all users need to know where their input data will be placed. Interfaces that do not employ those heuristics feel unnatural.
The goal of the old :focus selector was to allow authors to better style the focus indicator to be in tune with their overall design choices. The trouble is that using it meant losing the heuristics. The net result, unfortunately, has been that the most common use of the :focus selector has been to remove indicators altogether. This avoids the “false positive” focus styles that cause complaints from many users. The problem is that removing focus styling breaks website accessibility, causing trouble for people navigating the page using the keyboard.
Fortunately, a new CSS selector comes to the rescue, avoiding this kind of accessibility issue while providing the behavior web developers were looking for. The :focus-visible pseudo-class matches elements based on the browsers heuristics. It allows web authors to style the focus indicator only if it would be drawn natively.
Despite being a new feature that has recently landed on the web platform, it’s already being used by almost 1% of web pages (according to the Web Almanac by HTTP Archive).
Implementation
As of Safari Technology Preview 138, the :focus-visible selector has been added to WebKit, paying special attention to interoperability with other implementations. As part of the WebKit implementation, the Web Platform Tests test suite has been improved and expanded quite a lot, adding coverage to new cases and ensuring a better interop between the different implementations.
This work led to changes and improvements all around. Thanks to tests and discussions, all browsers now follow a common set of heuristics and have the same behavior in most situations. In WebKit, for example, clicking on a <div tabindex="0"> will no longer show a focus indicator by default (matching other browser engines).
In addition, the default User Agent style sheet in all browsers now uses the :focus-visible pseudo-class. This is a nice thing to have because it avoids the type of issues that happened with :focus in the first place, and circumvents the need of using some weird workarounds, like :focus:not(:focus-visible), to style the default focus indicator painted by the browser.
If you’re curious about the implementation details, you can read a series of blog posts written along the path to development at blogs.igalia.com/mrego, or watch this talk.
Examples
The good news after all the changes that have happened around :focus-visible lately, is that now you’d just need to use :focus-visible selector to style the focus indicator drawn by the browser engines.
On top of that, browsers are no longer showing a focus indicator when the user isn’t expecting it, like focusing a regular element via mouse click.
Web authors won’t need any kind of workaround to achieve the goal of styling the focus indicator shown by the web engine.
For example, if you want your focus indicator to have a thick magenta outline with a small offset, you just need to use the following CSS on your website:
It’s worth mentioning that this work was collectively chosen from among many, in numerous engines and partially funded by the public, through Igalia’s Open Prioritization campaign. This effort works to democratize the development of web platform features, giving groups of people and smaller organizations the chance to have a direct impact on the web ecosystem. If you want to learn more about this, Eric Meyer gave a talk explaining it at the W3C’s TPAC.
Thanks to many contributions, this experiment yielded not only :focus-visible development in WebKit, but a lot of interoperability work and alignment in other implementations and a sense that there was real demand for the feature. Thank you to everyone involved.
Feedback
:focus-visible pseudo-class has been enabled by default in Safari Technology Preview 138, please try it out and report any issues you might found on bugs.webkit.org. You can also send a tweet to @regocas or @webkit to share your thoughts about this feature.
Safari Technology Preview Release 139 is now available for download for macOS Big Sur and macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Fixed length of Intl.NumberFormat.formatRange and Intl.PluralRules.selectRange (r287543)
Content Security Policy
Changed to always use UTF-8 encoded content when checking hashes (r287270)
Implemented CSP strict-dynamic for module scripts (r287756)
Media
Added MediaRecorder support for the bitsPerSecond option (r287613)
Changed to prevent packing audio samples with discontinuity together (r287249)
Service Workers
Added full support for Service Worker interception of fetch requests with FormData body (r287612)
Fixed FetchRequest.clone to not need to be called with the current context (r287532)
Web Extensions
Added support for the redirect rule type in declarativeNetRequest, which requires host permissions to be granted for the host of the URL being redirected
Added support for declarativeNetRequest.getMatchedRules, which requires host permissions to be granted to view the URLs of blocked resources
Safari Technology Preview Release 138 is now available for download for macOS Big Sur and macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Some weeks ago I wrote a twitter thread explaining the process of fixing a Chromium bug related to wavy text decorations. At first it was just a patch in Chromium, but we also fixed the same issue in WebKit, which unveiled a mistake on the initial fix in Chromium (a win-win situation).
This blog post is somehow a story around web platform features implementation, which highlights the importance of interoperability and how investigating and fixing bugs on different web engines usually leads to gains for the whole ecosystem.
My colleague Delan Azabani has been leading this effort. If you want more details about this work you can read her twoblog posts. Also don’t miss the chance to enjoy her amazing talk from last BlinkOn 15, this talk gives lots of details about how highlight pseudos work, and includes some cool animations that help to understand this complex topic.
Lately I’ve been also helping with some related tasks here and there. Next I’m going to talk about one of them.
Spelling and grammar error markers
As you probably know spelling and grammar error markers use wavy underlines in some platforms like Linux or Windows, though not in all of them as they use dotted underlines on Mac. In Chromium they’re painted on a separated codepath, totally independent of how CSS text decorations are painted. You can easily spot the difference between a “native” spelling errors (left) and elements with text-decoration: wavy red underline (right) in the next picture.
Spelling errors Chromium Linux (left) vs wavy red underlines (right)
As part of our work around ::spelling|grammar-error highlight pseudos, we plan to merge both codepaths and use the CSS one for painting the default spelling and grammar error markers in the future. This doesn’t mean that they’ll look the same, actually they will still have a different rendering so the user can differentiate between a spelling marker and a wavy text decoration (like it happens now). But they’ll share the same code, so any improvement we do will apply to both of them.
There have been some bugs on each of them in the past, related to invalidation and overflow issues, and they had to be fixed in two places instead of just one. That’s why we’re looking into sharing the code, as its main job is to produce very similar things.
The issue we’re describing in the next section doesn’t happen on native spelling error markers, but as we plan to follow the CSS codepath, we had to fix it as a preliminary task getting things ready to move the spelling markers to use that codepath.
The issue
One problem with wavy text decorations in Chromium was that they sometimes don’t cover the full length of the text. This is because it only paints whole cycles and thus fall short in some situations.
A simple example is a wavy underline (text-decoration: wavy green underline) on a “m” letter using big fonts (see the picture below and how it doesn’t cover the full length of the letter).
<divstyle="font-size: 5em; text-decoration: wavy green underline;">m</div>
Green wavy underline doesn’t cover the full length of the letter “m” (Chromium)
Fixing the problem
This section goes into some implementation details about how this works on Chromium and how we fixed it.
To draw wavy text decorations Chromium defines a vector path for a Bezier curve, that path is generated at TextDecorationInfo::PrepareWavyStrokePath(). The comment in that method is quite self explanatory:
Comment from TextDecorationInfo::PrepareWavyStrokePath() explaining how the Bezier curve is defined
This method generates the path for wavy text decorations using the next loop:
As you can see, it only uses whole cycles of the wave (2 * step), and it never splits that in smaller chunks. If we’re going to end up further away than the text size, we don’t add that wave to the path (x + 2 * step <= x2). Which leads to the wrong behavior we saw in the example of the “m” letter above, where the text decoration falls short.
To prevent this problem, the code was using the method AdjustStepToDecorationLength(), that was expected to adjust the length of a whole wave. If that method was working properly, we would always cover the full text width adjusting the size of the waves. However there were two different problems on that method:
On one side, that method adjusted the step, but we were always generating whole waves (2 * step), so we might need to adjust the whole length of the wave instead.
On the other side, the method had some bug, as it was changing the step when it was not actually needed. For example if you pass a total length of 40px and a step of 10px, this method was adjusting the step to 10.75px, which makes no sense.
Digging a little bit on the repository’s history, we found out that this method has been around since 2013 thus it was present in both Blink and WebKit.
As it has a bunch of issues and our proposed fix was cutting the waves at any point, we decided it was not needed to try to adjust their size anymore, so we get rid of this method.
The solution we used to the length issue requires two main changes:
First we generate two extra waves before and after the text width:
// We paint the wave before and after the text line (to cover the whole length// of the line) and then we clip it at// AppliedDecorationPainter::StrokeWavyTextDecoration().// Offset the start point, so the beizer curve starts before the current line,// that way we can clip it exactly the same way in both ends.FloatPointp1(start_point+FloatPoint(-2*step,wave_offset));// Increase the width including the previous offset, plus an extra wave to be// painted after the line.FloatPointp2(start_point+FloatPoint(width_+4*step,wave_offset));
Then clip the path so it’s no longer than the text width. For which GraphicsContextStateSaver was really useful to just clip things related to the line that is currently being painted (for example in cases where you have both underline and overline text decorations).
Video showing the solution described above
The reviewers liked the idea and the patch landed in Chromium 97.0.4692 with some internal tests (not using WPT tests as how wavy lines are painted is not defined per spec and varies between implementations).
To finish this section, below there is a screenshot of the “m” with wavy green underline after this patch.
Green wavy underline covering the full length of the letter “m” (Chromium)
WebKit & WPT test
While looking into the history of AdjustStepToDecorationLength() method we ended up looking into some old WebKit patches, we realized that the code for the wavy text decoration in WebKit is still very similar to Chromium, and that this very same issue was also present in WebKit. For that reason we decided to fix this problem also in WebKit too, with the same approach than the patch in Chromium.
Green wavy underline doesn’t cover the full length of the letter “m” (WebKit)
Just an aside quick explanation, reference tests (reftests) usually compare a screenshot of the test with a screenshot of the reference file, to see if the rendered output matches exactly or not between both files. But sometimes browsers do a different thing that is called mismatch reftests, which compares a test with a reference and checks that they’re actually different.
The idea here was to do a test that has a wavy text decoration but we hide most of the content with some element on top of that, and just show the bottom right corner of the decoration. We mismatch against a blank page, because there should be something painted there, if the wavy text decoration cover the whole line.
So we wrote WPT tests, that we can share between implementations, to check that this was working as expected. And while working on that test we discovered an issue on the initial Chromium fix, as the wavy underline was kind of misplaced to the left. More about that later.
Again let’s finish the section with a screenshot of the “m” with wavy green underline after the WebKit patch.
Green wavy underline covering the full length of the letter “m” (WebKit)
Round trip
As mentioned in the previous section, thanks to porting the patch to WebKit and working on a WPT test we found out a mistake on the first Chromium fix.
So we’re back in Chromium where we were clipping the wavy text decoration with the wrong offset, so it looks like it was a little bit shifted to the left (specially when using big fonts). I’m repeating here the image for the initial Chromium fix, adding a grey background so it’s easier to notice the problem and compare with the final fix. There you can see that the wavy underline starts more on the left than expected, and ends earlier than the “m” letter.
Green wavy underline covering the full length of the letter “m” (Chromium). Initial fix
The patch to fix that was pretty simple, so we landed it in Chromium 98.0.4697. And this is the final output with the text decoration positioned in the proper place.
Green wavy underline covering the full length of the letter “m” (Chromium). Final fix
Other issues
In addition to an improved rendering on static wavy text decorations, these fixes have some nice effects when animations are involved. See the following video showing an example with animations (stealing a letter-spacing example from Delan’s latest blog post), on the left you can see Chromium (buggy version on top, fixed one on bottom) and on the right WebKit (again buggy on top and fixed on bottom).
Video showing the fix (top left: Chromium buggy, bottom left: Chromium fixed, top right: WebKit buggy, bottom right: WebKit fixed)
But as usual there’s still something else, in this case very related to this topic. There’s the concept of decorating box in CSS Text Decoration spec, and that hasn’t been implemented in Chromium and WebKit yet (though Firefox seems to be doing that right in most cases, except for dotted text decorations).
This issue is quite noticeable when you have different elements in the same line (like a <strong> element), or different font sizes in the same line. See the next video that shows this problem in Chromium (on the left) and Firefox (on the right, where only dotted text decorations have problems).
element. The video shows the behavior in Chromium (buggy) and Firefox (working fine except for dotted)."> element. The video shows the behavior in Chromium (buggy) and Firefox (working fine except for dotted)." class="center-block" style="width: 100%;" controls="" loop="">
Video showing the problem related to decorating box (left: Chromium, right: Firefox)
This has been a problem in Chromium and WebKit forever, even native spelling and grammar error markers have the same issue (thought it’s less noticeable as they’re painted always in a small size). Though even when this isn’t a strictly a blocker for all this work, this is something we’re looking forward to get fixed too.
Conclusion
The main takeaway from this blog post is how browser interoperability plays a key important role in the implementation of web platform features. Which is something we’re very concerned of at Igalia.
The fact that we fixed this issue in Chromium and WebKit at the same time helped to get more eyes looking into the same code, which is usually very beneficial.
We ended up not just fixing the issue in both implementations (Chromium and WebKit), but also adding new WPT tests that would be useful for any other implementation and to prevent regressions in the future. Even as a positive side effect, WebKit added support for mismatch reference tests as part of this work.
Finally, thanks to all the people that helped to make this happen by providing ideas, feedback and reviewing the patches; particularly Delan Azabani (Igalia), Myles Maxfield (Apple) and Stephen Chenney (Google).
Safari Technology Preview Release 137 is now available for download for macOS Big Sur and macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Most colors used on the Web today are sRGB colors. These are the colors that you specify with the familiar #rrggbb and rgb(r, g, b) CSS syntax, and whose individual color components are given as values in the range [0, 255]. For example, rgb(255, 0, 0) is the most saturated, pure red in the sRGB color space. But the range of colors in sRGB — its color gamut — does not encompass all colors that can be perceived by the human visual system, and there are displays that can produce a broader range of colors.
sRGB is based on the color capabilities of computer monitors that existed at the time of its standardization, in the late 1990s. Since then, other, wider gamut color spaces have been defined for use in digital content, and which cover more of the colors that humans can perceive. One such color space is Display P3, which contains colors with significantly higher saturation than sRGB.
This browser reports that the display does not support Display P3 colors; figures in this post may not appear as intended.
Conic gradients showing fully saturated sRGB (left) and Display P3 (right) colors. Viewed in a browser and on a display supporting Display P3, the colors in the circle on the right will show as more intense than those on the left. (View as standalone page.)
For a more in depth introduction to color spaces, see Dean Jackson’s earlier post, Improving Color on the Web.
Today, there are many computer and mobile devices on the market with displays that can reproduce all the colors of the Display P3 gamut, and the Web platform has been evolving over the last few years to allow authors to make best use of these displays. WebKit has supported wide color images and video since 2016, and last year became the first browser engine to implement the new color syntax defined in CSS Color Module Level 4 where colors can be specified in a given color space (like color(display-p3 1 0 0), a fully saturated Display P3 red).
One notable omission in wide gamut color support, until now, has been in the HTML canvas element. The 2D canvas API was introduced before wide gamut displays were common, and until now has only handled drawing and manipulating sRGB pixel values. Earlier this year, a proposal for creating canvas contexts using other color spaces was added to the HTML standard, and we’ve recently added support for this to WebKit.
Drawing on a wide gamut canvas rendering context
The getContext method on a canvas element, which is used to create a rendering context object with 2D drawing APIs, accepts a new option to set the canvas backing store’s color space.
<canvasid="canvas"width="400"height="300"></canvas><script>
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d", { colorSpace: "display-p3" });
// ... draw on context ...
</script>
The default color space remains sRGB, rather than having the browser automatically use the wider color space, to avoid the performance overhead of color space conversions with existing content. The two explicit color spaces that can be requested are "srgb" and "display-p3".
Fill and stroke styles can be specified using any supported CSS color syntax.
letposition=0;
for (letgreenof [1, 0]) {
for (letblueof [1, 0]) {
for (letredof [1, 0]) {
context.fillStyle= `color(display-p3 ${red} ${green} ${blue})`;
context.fillRect(position, position, 40, 40);
position+=20;
}
}
}
Display P3 colors used as fill styles on an sRGB (left) and Display P3 (right) canvas. Colors on the left are clamped to remain within the sRGB gamut. (View as standalone page.)
Any drawing that uses a color outside the color space of the canvas will be clamped so that it is in gamut. For example, filling a rectangle with color(display-p3 1 0 0) on an sRGB canvas will end up using a fully saturated sRGB red. Similarly, drawing on a Display P3 canvas with color(rec2020 0.9 0 0.9), an almost full magenta in the Rec.2020 color space, will result in pixels of approximately color(display-p3 1.0 0 0.923) being used, since that is the closest in the Display P3 color gamut.
constCOLORS= ["#0f0", "color(display-p3 0 1 0)"];
for (lety=20; y<180; y+=20) {
context.fillStyle=COLORS[(y/20) %2];
context.fillRect(20, y, 160, 20);
}
Stripes of interleaved Display P3 and sRGB colors on an sRGB (left) and Display P3 (right) canvas. Because colors are clamped to remain within the gamut of the canvas, the two shades of green are indistinguishable on the sRGB canvas. (View as standalone page.)
On macOS, you can use the ColorSync Utility to convert color values between sRGB, Display P3, Rec.2020, and some other predefined color spaces.
Wide gamut colors are usable in all canvas drawing primitives:
as the fill and stroke of rectangles, paths, and text
in gradient stops
as a shadow color
Pixel manipulation in sRGB and Display P3
getImageData and putImageData can be used to get and set pixel values on a wide gamut canvas. By default, getImageData will return an ImageData object with pixel values in the color space of the canvas, but it is possible to specify an explicit color space that does not match the canvas, and a conversion will be performed.
letcontext=canvas.getContext("2d", { colorSpace:"display-p3" });
context.fillStyle="color(display-p3 0.5 0 0)";
context.fillRect(0, 0, 100, 100);
letimageData;
// Get ImageData in the canvas color space (Display P3).
imageData=context.getImageData(0, 0, 1, 1);
console.log(imageData.colorSpace); // "display-p3"
console.log([...imageData.data]); // [128, 0, 0, 255]
// Get ImageData in Display P3 explicitly.
imageData=context.getImageData(0, 0, 1, 1, { colorSpace:"display-p3" });
console.log(imageData.colorSpace); // "display-p3"
console.log([...imageData.data]); // [128, 0, 0, 255]
// Get ImageData converted to sRGB.
imageData=context.getImageData(0, 0, 1, 1, { colorSpace:"srgb" });
console.log(imageData.colorSpace); // "srgb"
console.log([...imageData.data]); // [141, 0, 0, 255]
The ImageData constructor similarly takes an optional options object with a colorSpace key.
letcontext=canvas.getContext("2d", { colorSpace:"display-p3" });
// Create and fill an ImageData with full Display P3 yellow.
letimageData=newImageData(10, 10, { colorSpace:"display-p3" });
for (leti=0; i<10*10*4; ++i)
imageData.data[i] = [255, 255, 0, 255][i%4];
context.putImageData(imageData, 0, 0);
As when drawing shapes using colors of a different color space, any mismatch between the ImageData and the target canvas color space will cause putImageData to perform a conversion and potentially clamp the resulting pixels.
Serializing canvas content
The toDataURL and toBlob methods on a canvas DOM element produce a raster image with the canvas contents. In WebKit, these methods now embed an appropriate color profile in the generated PNG or JPEG when called on a Display P3 canvas, ensuring that the full range of color is preserved.
Drawing wide gamut images
Like putImageData, the drawImage method will perform any color space conversion needed when drawing an image whose color space differs from that of the canvas. Any color profile used by a raster image referenced by an img, and any color space information in a video referenced by a video (be it a video file or a WebRTC stream), will be honored when drawn to a canvas. This ensures that when drawing into a canvas whose color space matches the display’s (be that Display P3 or sRGB), the source image/video and the canvas pixels will look the same.
Here is an interactive demonstration of using canvas to make a sliding tile puzzle. The tiles are drawn by applying a clip path and calling drawImage pointing to the img element on the left, which references a wide gamut JPEG. Toggling the checkbox shows how the colors are muted when an sRGB canvas is used.
Sliding tile puzzle. Toggling the checkbox will change whether an sRGB or a Display P3 canvas is used. (View as standalone page.)
Web Inspector support
Web Inspector also now shows color space information for canvases to help ensure your canvases’ backing stores are in the expected color space.
In the Graphics tab, the Canvases Overview will display the color space for each canvas next to the context type (e.g. 2D) on each canvas overview tile.
After clicking on a Canvas overview tile to inspect it, the color space is shown in the Details Sidebar in the Attributes section.
Browser support
Wide gamut canvas is supported in the macOS and iOS ports of WebKit as of r283541, and is available in Safari on:
macOS Monterey 12.1 and above
iOS 15.1 and above
Safari is the first browser to support drawing shapes, text, gradients, and shadows with wide gamut CSS colors on Display P3 canvases. All other features, including getImageData, putImageData, and drawImage on Display P3 canvases, are supported in Safari and in Chrome 94 and above.
Feature detection
There are a few techniques you can use to detect whether wide gamut display and canvas support is available.
Display support: To check whether the display supports Display P3 colors, use the color-gamut media query.
Canvas color space support: To check whether the browser supports wide gamut canvases, try creating one and checking the resulting color space.
functioncanvasSupportsDisplayP3() {
letcanvas=document.createElement("canvas");
try {
// Safari throws a TypeError if the colorSpace option is supported, but
// the system requirements (minimum macOS or iOS version) for Display P3
// support are not met.
letcontext=canvas.getContext("2d", { colorSpace:"display-p3" });
returncontext.getContextAttributes().colorSpace=="display-p3";
} catch {
}
returnfalse;
}
CSS Color Module Level 4 syntax support: To check whether the browser supports specifying wide gamut colors on canvas, try setting one and checking it wasn’t ignored.
There are a few areas where wide gamut canvas support could be improved.
2D canvas still exposes image data as 8 bit RGBA values through ImageData objects. It may be useful to support other pixel formats for a greater color depth, such as 16 bit integers, or single precision or half precision floating point values, especially when wider color gamuts are used, since increased precision can help avoid banding artifacts. This has been proposed in an HTML Standard issue.
The two predefined color spaces that are supported are sRGB and Display P3, but as High Dynamic Range videos and displays that support HDR become more common, it’s worth consdering allowing 2D canvas to use these and other color spaces too. See this presentation at the W3C Workshop on Wide Color Gamut and High Dynamic Range for the Web from earlier this year, which talks about proposed new color space and HDR support.
WebKit now has support for creating 2D canvas contexts using the Display P3 color space, allowing authors to make best use of the displays that are becoming increasingly common. This feature is enabled in Safari on macOS Monterey 12.1 and iOS 15.1.
If you have any comments or questions about the feature, please feel free to send me a message at @heycam, and more general comments can be sent to the @webkit Twitter account.
The internet has always been about communication and collaboration. It started with asynchronous messages made of text. As it matured, the internet became real-time. Then the web came along, adding images, and later, video. Websites provided a means to publish, to broadcast, to run stores, to gather communities and create worlds.
Now, the web is maturing to the point where web apps make rich collaboration experiences possible — including digital creation. Recent updates to WebKit bring a number of improvements to Safari 15.2 that focus on supporting creative applications and leveraging the incredible power of today’s hardware.
WebAssembly Enhancements
Web Assembly (Wasm) is a low-level assembly language that allows a multitude of programming languages like C/C++, C#, Objective-C, Swift, Python, Java or even Cobol to be compiled to run on the web at near native speed — without the user needing to install anything special. It’s designed to work alongside of JavaScript, allowing sites to use both together. Wasm provides the tools needed to bring powerful software applications to the web.
In Safari 15.2, the addressable memory for Wasm has been expanded to 4GB, opening up possibilities for bigger and more powerful applications. The addition of zero-cost exception handling also provides potential performance gains.
COOP/COEP HTTP Headers
Shared memory provides powerful functionality for native applications, but on the web, such power must be balanced with strong security protections. SharedArrayBuffer was supported in WebKit for Safari 10.1–11, but was disabled along with other browsers due to the risk of using it for speculative execution attacks like Spectre.
Safari 15.2 adds support for Cross-Origin-Opener-Policy (COOP) and Cross-Origin-Embedder-Policy (COEP) HTTP response headers. Sites can adopt these headers to opt into process isolation and be better protected. If sites serve both Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp, they are now able to use SharedArrayBuffer and Wasm threading again.
Wide gamut support for Canvas
These days modern creative tools depend on amazing camera and gorgeous displays. Yet, most colors on the Web today are sRGB colors, which match the limited color capabilities of computer monitors from the late 1990s. The human visual system can perceive a much broader range of colors. Today’s modern displays reproduce the colors of the Display P3 gamut, with significantly higher saturation than sRGB.
Since 2016, WebKit has supported wide color images and video, and last year became the first browser engine to implement the new color syntax defined in CSS Color Module Level 4. One notable omission in wide gamut color support was in the HTML canvas element. Earlier this year, a proposal for support was added to the HTML standard, and now, in Safari 15.2, WebKit adds wide gamut support — including Display P3 — for use in canvas.
Safari 15.2 is available on macOS Monterey, macOS Big Sur and macOS Catalina. To update on macOS, go to Apple menu > System Preferences, and click Software Update.
Safari 15.2 is available on iOS and iPadOS 15.2. To update, go to Settings > General, then tap Software Update.
Feedback
If you run into issues, we welcome your bug reports for Safari, or WebKit bugs for web content issues. Send us a tweet @webkit to share your thoughts on this release.
Safari Technology Preview Release 136 is now available for download for macOS Big Sur and macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Private Click Measurement (PCM) can now be used for in-app direct response advertising using SFSafariViewController. Try it out in our iOS 15.2 beta.
What is PCM?
PCM is a proposed web standard for measuring the effectiveness of click-through advertising in a privacy-preserving way. It allows for 8 bits of data on the click source site to be combined with 4 bits of data on the click destination site to measure which advertising is driving sales. The combined 8+4 bits of data is sent to both the click source and destination in an attribution report that doesn’t carry any user or device identifiers. The net result is a report that says “Someone who clicked ad X on website A later converted with value Y on website B.”
PCM shipped in iOS/iPadOS 14.5 and in Safari 14.1 on macOS. Its privacy-preserving nature means it can be used without getting the user’s permission to track according to AppTrackingTransparency.
What is SFSafariViewController?
SFSafariViewController is a ready-built in-app web browser on iOS/iPadOS with full-fledged Safari features such as Reader, AutoFill, Fraudulent Website Detection, content blocking, and bookmarks. User activity and interaction with SFSafariViewController are not visible to your app which means users can safely browse the web in it and you do not need to secure data between your app and SFSafariViewController.
On Direct Response Advertising
Our introductory blog post on PCM featured two important FAQ entries on app-to-web advertising – on the subject of taking the user to the device’s browser and on the subject of an in-app experience.
When to Take the User To the Device’s Browser
PCM app-to-web in iOS 14.5 had to take the user to the device’s browser. This was designed to support re-engagement. Stored clicks in PCM are valid for 7 days and customers who are ready to take action only after a few hours or days, will most likely go find the merchant website in their browser. They’ll either look up the tab where they left off, use a bookmark they might have saved, use their search provider to find the right webpage, or enter the website’s address directly in the URL bar.
For the stored click data to be readily available when the user re-engages in this fashion, the initial click needs to take the user to their browser. This is still true going forward.
When to Handle the Tap In-App
Another form of click-through advertising is called direct response. In such cases, the user is not expected to think about converting for an extended period of time, but rather take action directly on the webpage they land on. It could be a limited offer or a product priced so that the user doesn’t feel like they have to think it over.
Developers and advertisers have told us they want to be able to provide more of an in-app experience for direct response advertising, rather than take the user to the device’s browser. They want a seamless in-app experience for users who tap on an ad, check out the product page, decide to buy or not, and then want to go back to the hosting app.
Our intro blog post covered this request in the FAQ section, and we said “We are interested in this but don’t have a solution yet.” Today we have a solution. PCM is now capable of supporting in-app advertising with new API for SFSafariViewController.
PCM App-to-Web with SFSafariViewController
SFSafariViewController provides a great in-app browsing experience where the user can store Safari bookmarks that can sync across devices, has access to autofill of credentials and payment card info, and Apple Pay. It is simply a great place to take the user as part of direct response advertising.
Ephemeral Clicks to Prevent Click Fraud
PCM will only store click data and schedule an attribution report if the user triggers a conversion in the SFSafariViewController that they opened through the click. A tap which navigates to a website in SFSafariViewController without a matching triggering event will not be stored. As a result, an instance of SFSafariViewController can only hold one non-converted click at a time, whereas all converted clicks will be stored and result in reports.
This ensures that a hosting app cannot speculatively store clicks in its SFSafariViewController for fraudulent reasons. It also ensures that this use of PCM really is geared toward direct response advertising.
Attributions are Per Hosting App
Taps in different apps navigating the user to the same advertised website do not affect each other. Every app gets its own attribution. Again, this feature is for direct response advertising so a customer who buys a product twice based on ad clicks in two different apps will generate two attribution reports.
Attribution Reports Don’t Require Your App to Run
One particular challenge for the kind of delayed attribution reporting PCM uses is what to do if the user doesn’t use the hosting app frequently or doesn’t use it around the time when the report is supposed to be sent out. We have made sure that pending attribution reports from PCM app-to-web with SFSafariViewController are sent independent of if the hosting app where the click happened is running or not.
The API
As shown in our introductory blog post on PCM, apps can already use Private Click Measurement with Safari by putting a UIEventAttribution on a UISceneOpenExternalURLOptions and using it with UIScene’s openURL:options:completionHandler:.
In iOS 15.2 beta, a new attribute of type UIEventAttribution is added to the existing class SFSafariViewControllerConfiguration:
@available(iOS 15.2, *)
@NSCopying var eventAttribution: UIEventAttribution?
You can optionally use it when opening a URL in SFSafariViewController, like this:
func didTapOnAdWithIdentifier(advertisementIdentifier: UInt8, url: URL) {
let attribution = UIEventAttribution(
sourceIdentifier: advertisementIdentifier,
destinationURL: url,
sourceDescription: "Ad for toy XYZ.",
purchaser: "Toy Example Company")
let configuration = SFSafariViewController.Configuration()
configuration.eventAttribution = attribution
present(SFSafariViewController(url: url, configuration: configuration), animated: false)
}
When an UIEventAttribution is part of the configuration, SafariViewService checks that a tap on an UIEventAttributionView preceded the opening of SFSafariViewController to guarantee click-through attribution. Then the information from the UIEventAttribution object is checked before it’s given to WebKit for processing.
Private Click Measurement works as previously from that point.
Make sure to restart your app after enabling PCM Debug Mode with SFSafariViewController. If you are having trouble getting PCM Debug Mode to turn on or off, try restarting the device.
Please Provide Feedback
We really appreciate all the developer and ad tech feedback we’ve received so far on Private Click Measurement. Prioritizing PCM for in-app measurement was the result of such feedback. There are three ways for you to continue to tell us what you think:
The standards proposal repository in the W3C Privacy Community Group for anything related to the specified web parts of PCM, i.e. feedback on the proposed standard as it would work in any web engine.
Safari Technology Preview Release 135 is now available for download for macOS Big Sur and macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Fixed anchor.relList.supports("opener") to return true (r284745)
Fixed changing the src attribute of the <img> element inside an ImageDocument to trigger a load (r284901)
Fixed document.open() and friends to use the correct document as a source for reset document’s URL (r284758)
Fixed form navigations with target="_blank" to not have an opener (r284821)
Fixed form submission to be cancelled if the form gets detached from inside the formdata event handler (r284660)
Fixed JavaScript URL result to be treated as UTF-8 bytes (r284934)
Fixed Origin of opaque blob: URLs to be null instead of an empty string (r284478)
Fixed selection extend() with no ranges to trigger an exception (r285084)
Fixed the intrinsic size of a picture image inside a template (r284667)
Updated appearance of <datalist> indicator (r284626)
Scrolling
Fixed misplaced position: fixed content with async-scrollable iframes when switching tabs (r284738)
Rendering
Ensured CanvasRenderingContext2D.drawImage(video) uses the right color space (r284439)
WebAuthn
Changed to obtain consent to create a new credential when the platform authenticator is in excludedCredentials. This improves compliance with the WebAuthn spec (Step 3.1 of makeCredential). (r284413)
Media
Added support for requestVideoFrameCallback API for MediaStreamTrack-based backends (r284528)
Fixed video appearing blank with only audio playing if video element isn’t appended to the DOM tree (r284741)
Updated WebM with invalid size to fail to load with error (r284434)
WebRTC
Decreased WebRTC latency by pulling data more often (r284860)
Changed to fallback to SW decoder in case of VP9-SVC (r284523)
Changed to always set the color space for incoming H.264/265 streams (r284433)
Ensured synchronized rendering of incoming audio tracks (r285027)
Fixed latent audio over peer connections when changing the output (r284674)
WebGL
Fixed an issue where high-performance WebGL wasn’t getting the correct GPU (r284669)
App Extensions
Fixed an issue where App Extension toolbar items would not remember their position or stay removed from the toolbar
Web Extensions
Fixed an issue where browser.storage.sync entries were saved into the browser.storage.local storage area. If unable to locate sync storage entries, check in the local storage area and do a one-time migration to the sync storage area
Safari Technology Preview Release 134 is now available for download for macOS Big Sur and macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
With the release of Safari 15 for macOS Monterey, iPadOS 15, iOS 15, and watchOS, as well as macOS Big Sur and macOS Catalina, WebKit brings significant advancements in privacy and security, improved interoperability, and a host of new features for web developers. Take a look.
Web Extensions
This release brings Safari Web Extensions to iOS and iPadOS. Web Extensions use HTML, CSS, and JavaScript to offer powerful browser customizations. Now developers can create them for every device that supports Safari, using APIs, functionality, and permissions that are increasingly standardized across all browsers. Learn how to build Safari Web Extensions and discover how to convert an existing extension by watching Meet Safari Web Extensions on iOS at WWDC21.
This year’s release also adds support for the Declarative Net Request WebExtensions API to block content on the web. Learn all about the latest WebExtension APIs by watching Explore Safari Web Extension Improvements at WWDC21.
HTML
WebKit now provides support for theme-color in HTML meta tags, and in Web Manifest. By specifying a theme-color, web developers can change the color of the status bar and overscroll area in Safari on iOS 15. Theme-color also changes the Tab Bar and overscroll area background colors in Compact Tab layout for Safari 15 on macOS Monterey and Big Sur and iPadOS 15.
In the HTML meta tag, developers can specify separate colors for Dark Mode and light appearance with the media attribute.
Watch “Design for Safari 15” at WWDC21 to learn more about the Compact Tab bar and how to use theme-color.
CSS
WebKit now supports CSS aspect-ratio. This property can be used to set a preferred aspect ratio on any element, including boxes like divs, iframes for embedded video, or graphic design elements on a page.
WebKit provides support for the new lab(), lch(), hwb() color syntaxes from Color level 4, providing web developers with ways to express a richer range of colors in Lab, Lch, and Hue-Whiteness-Blackness. WebKit also supports predefined color spaces using the color() function syntax: srgb, display-p3, a98-rgb, prophoto-rgb, rec2020, xyz.
WebKit supports 12 new values for list-style-type: disclosure-closed, disclosure-open, ethiopic-numeric, japanese-formal, japanese-informal, korean-hangul-formal, korean-hanja-formal, korean-hanja-informal, simp-chinese-formal, simp-chinese-informal, trad-chinese-formal, and trad-chinese-informal.
Apollo
Hubble
Chandra
Cassini-Huygens
Spitzer
Disclosure closed
Apollo
Hubble
Chandra
Cassini-Huygens
Spitzer
Ethiopic Numeric
Apollo
Hubble
Chandra
Cassini-Huygens
Spitzer
Simplified Chinese informal
There’s also improved implementation of existing values for list-style-type: armenian, cjk-ideographic, hebrew, lower-armenian, lower-roman, upper-armenian, and upper-roman. See a demo of all of these options at MDN. We also updated WebKit’s implementation of list-style-position:inside to match the updated CSS specification, creating interoperability after a 22 year old debate.
This release of WebKit adds support for ES6 Modules in Workers and ServiceWorkers. ES6 Modules provides a powerful way for developers to organize large applications using purpose-specific libraries. Workers/Service Workers provides a way to offload work from the main thread, and are often used for complex applications. Now, developers can use them together — moving work off the main thread, improving performance, while retaining the organizational benefits of modules.
Additional new capabilities to the JavaScript engine, include:
support for top-level await
Error.cause
private class methods and accessors
BigInt64Array and BigUint64Array
Improvements to WebAssembly include streaming compilation, bulk memory operations, reference types, and non-trapping conversions from float to int.
You can learn more about the latest JavaScript and WebAssembly updates to WebKit and Safari 15 by watching “Develop Advanced Web Content” at WWDC21.
Web APIs
WebKit now supports WebGL2 (demos). In addition, the WebGL implementation now runs on top of Metal for better performance.
Web Share level 2 enhancements to Web Share enable sharing files from a web page to an app. See Web Share API for more information.
User gestures now propagate through requestAnimationFrame with a one-second time limit.
And now, with Safari 15.1, performance.timeOrigin is available in Web Workers.
Safari 15 includes several media improvements for users and developers. For example, built-in media controls now have Playback Speed and Chapters menus. Plus, the language/subtitle tracks menu is now available on iOS and iPadOS.
There’s also new support for the Opus audio codec in WebM containers. And on on all iPads that support iPadOS 15, VP9 and WebM in Media Source Extensions (MSE) are now hardware-accelerated.
Continuing our dedication to privacy and security, Safari on iOS 15 and macOS Monterey supports automatic HTTPS upgrades and hides your IP address from known trackers. Automatic HTTPS upgrades are also supported in Safari 15 on older macOS versions.
Earlier this year, Safari was the first browser to ship a proposed web standard for measuring advertising in a privacy-preserving way – Private Click Measurement, or PCM. Safari 15 provides three major updates to PCM:
Attribution reports also sent to click destination.
WebKit now includes support for on-device verification codes in your app or website for a more secure sign-in experience with iCloud Keychain Password Manager. To use verification codes with Safari and Autofill:
Use autocomplete=one-time-code to make an <input> eligible for AutoFill.
Use a standard otpauth URL and replace the scheme with apple-otpauth to link directly to the password manager for setup.
Use a raster image to enable contextual menus on otpauth QR codes that offer to set up a verification code generator.
Despite their prevalence, passwords inherently come with challenges that make them poorly suited to securing someone’s online accounts. Passkeys are WebAuth credentials intended to replace passwords for websites and apps with device sync and backup. The technology is now available in WebKit as a preview. To enable in Safari, choose Develop > Enable Syncing Platform Authenticator. Learn more watching “Move beyond passwords” at WWDC21.
Payments
Apple Pay enhancements allow developers using the Payment Request API to indicate an estimated arrival date for shipping methods, support a coupon code, and mark the shipping method as in-store pickup.
Availability
These improvements are available to users running Safari on iPadOS 15, iOS 15, or Safari 15 on macOS Monterey, macOS Big Sur, or macOS Catalina. These features were also available to web developers in Safari Technology Preview releases. Changes in this release of Safari were included in the following Safari Technology Preview releases: 123, 124, 125, 126, 127, 128, 129.
If you run into any issues, we welcome your bug reports for Safari or WebKit bugs for web content issues. Send us a tweet @webkit to share your thoughts on this release.
Safari Technology Preview Release 133 is now available for download for macOS Big Sur and betas of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
This release covers WebKit revisions 281797-282317. Note: The changes for these release notes were updated after publishing to account for an incorrect end revision number.
Note: Tab Groups do not sync in this release.
CSS
Added support for self-start, self-end, start, end, left, and right values in positional alignment (r282267, r282078, r281840)
Added support for percentages in the scale() transform functions, and the scale property (r282144)
Added support for sin(), cos(), tan(), e, and pi in calc() (r282162)
Fixed incorrect stacking order with an absolutely positioned and negative z-index<div> with a canvas child (r281913)
Fixed absolute positioning with orthogonal writing modes (r281995)
Fixed right-relative and bottom-relative values in background-position-x and background-position-y (r282234)
Fixed incorrect vertical position in table layout when the inline level box has 0px height (r282256)
Fixed changing the border size on rows with border-collapse not redrawing (r282266)
Fixed position: sticky used within table cells (r282201)
Fixed incorrectly calculated position: sticky constraints when the scrolling container has padding and borders (r282138)
Fixed an interoperability issue in margin collapsing with overflow: hidden elements (r282085)
CSS Cascade Layers
Added initial support for CSS Cascade Layers in Experimental Features:
Added support for computing the order correctly for late added sublayers (r281798)
Supported layer argument in @import rules (r281928)
CSS Font Loading API
Updated the implementation of the CSS Font Loading API to be closer to the spec and other browsers:
Fixed CSSFontFaceSet.clear() to not clear CSS-connected members (r281842)
Safari Technology Preview Release 132 is now available for download for macOS Big Sur and betas of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Safari Technology Preview Release 131 is now available for download for macOS Big Sur and betas of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Safari Technology Preview Release 130 is now available for download for macOS Big Sur and betas of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Note: Tab Groups do not sync in this release. On macOS Big Sur, this release requires enabling GPU Process: Media option from Experimental Features under the Develop menu to address issues with streaming services.
Web Inspector
Elements
Fixed selecting a sibling node using navigation breadcrumbs (r280037)
CSS
Changed to use the correct block-size to resolve min-content (r280023)
Changed to pass the full target point when selecting a snap offset (r280171)
Changed images as grid items to use the overridingLogicalWidth when defined to compute the logical height (r280024, r280078)
Ignored the aspect-ratio of a replaced element if stretch alignments are applied to both axes (r280022)
JavaScript
Implemented Array.prototype.findLast and Array.prototype.findLastIndex behind a runtime flag (--useArrayFindLastMethod) (r279937)
Added webm/opus container support for Web Audio (r280416)
Fixed hanging when entering PiP from element fullscreen (r280358)
Fixed SourceBuffer.abort() doesn’t go back to state WAITING_FOR_SEGMENT properly (r279904)
Fixed video pausing after scrubbing with the Touch Bar (r280330)
Web API
Fixed document.referrer value missing a trailing slash (r280342)
Fixed FetchResponse.formData() to not reject the promise if the body is null and the MIME type is "application/x-www-form-urlencoded" (r280046, r279969)
Fixed getBoundingClientRect() to return the correct rectangle on elements inside a multi-column container (r280017)
Fixed HTMLImageElement.decoding to reflect the decoding content attribute, limited to only known values (r280047)
Fixed Sync XHR “load” event always having total/loaded=0 (r279967)
Hold up, a blog post before a year’s up? I’d best slow down, don’t want to over-strain myself So, a year ago, OffscreenCanvas was starting to become usable but was missing some key features, such as asynchronous updates and text-related functions. I’m pleased to say that, at least for Linux, it’s been complete for quite a while now! It’s still going to be a while, I think, before this is a truly usable feature in every browser. Gecko support is still forthcoming, support for non-Linux WebKit is still off by default and I find it can be a little unstable in Chrome… But the potential is huge, and there are now double the number of independent, mostly-complete implementations that prove it’s a workable concept.
Something I find I’m guilty of, and I think that a lot of systems programmers tend to be guilty of, is working on a feature but not using that feature. With that in mind, I’ve been spending some time in the last couple of weeks to try and bring together demos and information on the various features that the WebKit team at Igalia has been working on. With that in mind, I’ve written a little OffscreenCanvas demo. It should work in any browser, but is a bit pointless if you don’t have OffscreenCanvas, so maybe spin up Chrome or a canary build of Epiphany.
OffscreenCanvas fractal renderer demo, running in GNOME Web Canary
Those of us old-skool computer types probably remember running fractal renderers back on their old home computers, whatever they may have been (PC for me, but I’ve seen similar demos on Amigas, C64s, Amstrad CPCs, etc.) They would take minutes to render a whole screen. Of course, with today’s computing power, they are much faster to render, but they still aren’t cheap by any stretch of the imagination. We’re talking 100s of millions of operations to render a full-HD frame. Running on the CPU on a single thread, this is still something that isn’t really real-time, at least implemented naively in JavaScript. This makes it a nice demonstration of what OffscreenCanvas, and really, Worker threads allow you to do without too much fuss.
The demo, for which you can look at my awful code, splits that rendering into 64 tiles and gives each tile to the first available Worker in a pool of rendering threads (different parts of the fractal are much more expensive to render than others, so it makes sense to use a work queue, rather than just shoot them all off distributed evenly amongst however many Workers you’re using). Toggle one of the animation options (palette cycling looks nice) and you’ll get a frame-rate counter in the top-right, where you can see the impact on performance that adding Workers can have. In Chrome, I can hit 60fps on this 40-core Xeon machine, rendering at 1080p. Just using a single worker, I barely reach 1fps (my frame-rates aren’t quite as good in WebKit, I expect because of some extra copying – there are some low-hanging fruit around OffscreenCanvas/ImageBitmap and serialisation when it comes to optimisation). If you don’t have an OffscreenCanvas-capable browser (or a monster PC), I’ve recorded a little demonstration too.
The important thing in this demo is not so much that we can render fractals fast (this is probably much, much faster to do using WebGL and shaders), but how easy it is to massively speed up a naive implementation with relatively little thought. Google Maps is great, but even on this machine I can get it to occasionally chug and hitch – OffscreenCanvas would allow this to be entirely fluid with no hitches. This becomes even more important on less powerful machines. It’s a neat technology and one I’m pleased to have had the opportunity to work on. I look forward to seeing it used in the wild in the future.
Safari Technology Preview Release 129 is now available for download for macOS Big Sur and betas of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Note: On macOS Big Sur, this release requires enabling GPU Process: Media option from Experimental Features under the Develop menu to address issues with streaming services.
Web Inspector
Added contextual documentation for CSS properties (r279510)
Changed the sidebar panel and navigation bar to layout asynchronously during resize (r279790)
Elements Tab
Fixed Details sidebar navigation items wrapping to a second line (r279613)
Added support for function value completions in the Styles sidebar (r279422)
Autocomplete var() and attr() values in the Styles sidebar (r279502)
Sources Tab
Fixed the Scope Chain sidebar panel to not strip repeating whitespace from strings (r279294)
CSS
Changed to not clamp flex base size with min-height, max-height, min-width, and max-width (r279271)
Changed :link and :visited pseudo-class selectors to not match <link> elements (r279818)
Changed to match the CSS specification which prohibits numbers with a trailing decimal point (e.g. “1.px”) (r279429)
Fixed the CSS parser “consume declaration” algorithm to handle whitespace correctly (r279358)
Fixed CSS style sheets loaded by HTMLLinkElement to fall back correctly when the charset is an invalid encoding name (r279383)
Improved computation of intrinsic sizes of flex items with aspect ratio (r279286)
Included container’s writing mode to get grid item’s margin (r279278)
Today I am happy to unveil GNOME Web Canary which aims to provide bleeding edge,
most likely very unstable builds of Epiphany, depending on daily builds of the
WebKitGTK development version. Read on to know more about this.
Until recently the GNOME Web browser was available for end-users in two …
Earlier this year, Safari was the first browser to ship a proposed web standard for measuring advertising in a privacy-preserving way – Private Click Measurement, or PCM. Today we’re happy to announce three major updates to PCM, available in our iOS/iPadOS 15 and macOS Monterey betas:
Attribution reports also sent to click destination.
Click fraud prevention with unlinkable tokens.
IP address protection for attribution reports.
All of it is covered in Kate’s WWDC session. There are also two minor updates to naming and data types so look out for those below.
Let’s jump in.
Quick PCM Refresher
PCM allows for measurement of clicks which navigate the user from one website to another or from an iOS app to a website. The website or app where the click happens is called the click source, and the website the user is navigated to is called the click destination.
Attribution Reports Also Sent to Click Destination
We’ve designed PCM’s attribution reports to ensure they make sense to both the click source and the click destination, i.e. no opaque IDs that can only be understood or decoded by one party. PCM now sends the resulting attribution report to both click source and destination to help advertisers validate data and be in control of their ad measurement. The two reports are sent independently with a 24-48 hour delay (each report gets its own delay within those 24-48 hours). Since PCM reports already contain information about both click source and destination, there is no new privacy risk involved in sending it to both places (they could have already shared this information).
PCM’s Debug Mode (see “Testing and Debugging” in our previous blog post) sends them independently too but with the shorter 10 second delay, i.e. one after 10 seconds and one after 10+10=20 seconds.
Click Fraud Prevention With Unlinkable Tokens
We mentioned in our introductory blog post on PCM that fraud prevention is a top request for the feature and we’re super excited to announce that optional unlinkable tokens for click fraud prevention are now supported. The cryptographic operations required for blinding and unblinding the tokens are only available on iOS/iPadOS 15 and macOS Monterey so no such tokens will be provided by clients on older operating systems. However, PCM will still work and send attribution reports if you opt-in to tokens in a Safari version that doesn’t support them. Also, unlinkable tokens for click fraud prevention are only available in PCM web-to-web for now.
An unlinkable token provides a privacy-preserving proof, generated by the click-source and the client, of a unique click event on the attributed link. Unlinkable tokens are implemented using RSA blind signatures, a well-studied cryptographic construction that guarantees unlinkability between the moment the token is getting signed and the verification of the resulting signature, through blinding and unblinding operations that are performed on the client side. In contrast with some other cryptographic token constructions, blind signatures provide public verifiability, allowing both the click source and click destination to verify the authenticity of the click event on the click’s source website.
This is how you as a developer of the click source website opt in to click fraud prevention with unlinkable tokens:
Step 1: Generate an RSA Key Pair
Unlinkable tokens require a public key for token generation and validation, and a corresponding private key for signing. PCM supports three different RSA key sizes: 2048, 3072 and 4096 bits. The expected encoding of the public key is a Base64-encoded SPKI with the RSA-PSS OID and the parameters corresponding to the RSABSSA IETF draft.
Step 2: Add the New Link Attribute attributionsourcenonce
The attributionsourcenonce is only in place to help the click source server know the context for which it’s signing an unlinkable token. The click source is intended to uniquely and ephemerally identify a specific click event in the eyes of the server. When the server is asked to sign an unlinkable token it’ll get the attributionsourcenonce and can make a decision as to whether the click event was trustworthy or not.
The attributionsourcenonce needs to be a Base64URL encoded 128-bit/16-byte value. Any smaller or larger value will cancel the issuance flow of the fraud-prevention signature. Any non Base64URL encoded value will also cancel the token transaction. Web Inspector will log a warning if the attributionsourcenonce is malformed.
Step 3: Respond to a Request for Your Public Key
The browser and any validating party needs to be able to fetch your public key at any point in time from this well-known location: https://clicksource.example/.well-known/private-click-measurement/get-token-public-key/.
Your server’s response should look like this, with its key Base64URL-encoded:
Step 4: Receive Token Signing Request, Sign, and Respond
After the browser has requested your public key, it will send your server a signing request with an unlinkable token to this well-known location: https://clicksource.example/.well-known/private-click-measurement/sign-unlinkable-token/.
The source_nonce is the attributionsourcenonce that was provided in the link and the source_unlinkable_token is the Base64URL-encoded token to sign.
If you deem this is a valid request, your server performs the BlindSign operation of the RSABSSA draft, which is a RSA private key operation on the browser-provided source_unlinkable_token and returns the blind signature as a Base64-encoded output labeled “unlinkable_token" in a response like this:
Step 5: Respond to Another Request for Your Public Key
When it’s time for the browser to send the attribution report, it will again fetch the public key from your server and make sure that it’s the same public key. This is how PCM makes sure that the server doesn’t use user-specific keys. Note that this second request for your public key may happen multiple days after the click you are measuring and that the browser will discard the attribution data if it receives a mismatching key.
There is currently no support for managed key renewal. We intend to support responses with two public keys – one current and one decommissioned. That way, the browser can compare with both and allow attribution with tokens that were signed with the private key matching the decommissioned public key.
Step 6: Receive and Validate the Attribution Report
The resulting attribution report will contain a signed secret token (source_secret_token) that your server has never seen before and that your server cannot link to its corresponding unlinkable token (source_unlinkable_token). However, the signature you created with your private key will be valid for the secret token, effectively proving to you that you deemed this a trustworthy click some time back. Make sure to validate the signature.
Since the click destination receives attribution reports too, they should validate the token as well.
The report will look like this, with the source_secret_token and source_secret_token_signature Base64URL-encoded:
Safari 15 already hides the devices IP address from known trackers so if PCM is communicating with a known tracker, that protection is there. On top of that, PCM now protects all of the requests it makes that don’t happen very close in time to a linkable event in a first-party context, i.e. requests that don’t have a high likelihood of being matched based on time with very recent non-protected requests. The device IP address is hidden by sending requests through a two-hop relay which makes sure that Apple doesn’t get to see things like the attribution data. Learn more about this technology in the WWDC session “Get ready for iCloud Private Relay.”
PCM Debug Mode skips IP address protection so that you can test locally.
Attribute Name and Data Type Change
Our introductory blog post on PCM also mentioned that there will likely be changes to naming due to the ongoing efforts to harmonize as much as possible with Google’s alternative proposal Click Through Attribution Reporting API.
The PCM naming and data type changes in Safari 15 are:
The click destination is now stated in the anchor element’s attributiondestination attribute (used to be attributeon).
attributionsourceid is now an unsigned long, i.e. a non-negative integer (used to be a string).
Feature Enabled and Disabled Should Be Indistinguishable
Our continued work in this space has taught us that it’s important to design ad measurement features so that websites cannot tell if the user has the feature enabled or not, at least not at page render time. The reason why is to offer users a real choice. If a webpage can tell that the user has chosen to disable ad measurement, it may pressure the user to enable it. We encourage other browser vendors in this space to think about this important aspect of user agency, especially in light of the multi year effort to make the web work well without third-party cookies (as a user setting or as the default).
Support for legacy tracking pixels. With a redirect, nothing needs to change on the click destination site.
Server-side control. By requiring a server-side redirect, we enable the domain owner to be in control of when a triggering event is fired. If it was just a HTTP request, any JavaScript on the destination site could fire triggering events. The redirect is not a fool proof system but it offers at least one checkpoint of control.
Q2: I’m not receiving attribution reports in my testing. How should I debug?
A2: Attribution reports are sent over HTTPS to the registrable domain of the click source and click destination. There needs to be a valid TLS certificate for each of those domains and the server needs to accept a request to the well-known location.
Q3: Will Apple add support for unlinkable tokens for triggering events on the destination site?
A3: Yes, that is our intention.
Q4: Will these enhancements be available in Safari 15 on older versions of macOS?
A4: Attribution reports also sent to click destination will be available in Safari 15 across the board. However, click fraud prevention with unlinkable tokens relies on cryptographic functionality that is only available starting with iOS/iPadOS 15 and macOS Monterey.
After three years working on JavaScriptCore (JSC), I recently had the opportunity to work on optimizing one of our standard library functions for the first time. I thought it’d be interesting to share what I learned about how they work in JSC and how we make them faster.
How are standard library functions implemented in JSC?
The JavaScript standard library functions include all the prototype functions for Array, Object, etc, and in this post we’ll look at Function.prototype.toString.
In JSC, standard library functions can either be implemented in native code (C++) or in JavaScript. When using JavaScript, we can use a special syntax for operations that are not allowed for regular JavaScript programs. Standard library functions written in JavaScript can be called like any other JavaScript function, and will also run through the same optimizations in each of our tiers, including inlining. Meanwhile, standard library functions written in C++ require the VM to make calls to native code, which is more expensive, and while the implementation will be optimized by the C++ compiler, its internals are completely opaque to the caller JS function, so there can be no inlining. The reason I mention inlining is that it allows the caller to “see” inside the function body, which unlocks many further optimizations to be done beyond the function boundary, some of which we’ll see in this example.
Making standard library functions faster
Let’s start with a reduced test case to see how can we optimize Function.prototype.toString:
functionf() { /* ... some code here ... */ }
functiong() {
returnf.toString();
}
When we call g, JSC will start by executing it in the interpreter (called LLInt), and if we continue calling it enough times it will eventually be promoted through the 3 compilers in JSC: the non-optimizing Baseline compiler and our optimizing DFG and FTL compilers. You can read more about our execution tiers on this post from back when the FTL was first introduced, but since then the FTL has gotten a new backend.
The first step before we run any code in any tier is to convert it from source code to bytecode (again, you can read a lot more about our bytecode here). After that, our DFG and FTL compilers also have their own representation of the code, that we call an Intermediate Representation or IR for short. For this post I’ll take the liberty of using some pseudocode that is somewhere in between our initial bytecode and DFG IR. This way we can see some of the details that are only available in DFG but without exposing too much low level complexity that is not relevant for our example. Here’s what our pretend IR could look like for g:
function g():
f = Lookup("f") // look for the variable "f" in the lexical scope
toString = Get(f, "toString") // access the "toString" property of `f`,
// respecting the semantics of JavaScript property
// lookup, including looking up the prototype, etc.
result = Call(toString, f) // call the toString function with `f` as `this`
Return(result) // return our result to the caller
Caching
The first optimization implemented wasn’t specific to standard library functions at all: the result of calling toString on a function never changes, so we can cache it.
Our implementation of Function.prototype.toString is written in C++ and has to handle a few special cases. One such case is when we are calling toString on a native function implemented in C++, but for the common case where it’s a regular JavaScript function, we have to look at the source code for the function. Since the source can’t be changed while it’s being executed, and the name of the function also cannot be changed, this result can be cached. This is completely transparent to our IR, which means we don’t have to change anything in the compiler and we already get a great speedup.
Speculation
When we start trying to optimize any JavaScript program we quickly face several challenges, since pretty much everything in JavaScript can be mutated at runtime, including our standard library functions. One way that modern JavaScript VMs get around these challenges is by speculating that certain facts about the program will not change during its execution, but since we can’t ever be sure of that, we need a way to fall back if our assumptions become invalid. We have an amazing post that goes deep into how we speculate in JSC, but for our current purposes all we need to know is that we can compile a function and say the code we generated is only valid so long as some assumptions are valid. This also isn’t specific to standard library functions, but it’ll be important later to see how all these optimizations interact with each other yielding more than the sum of their parts.
In this case we can speculate that f will never change, for our example let’s assume that it’s always called with an object allocated at address 0x123456. This is done through a mechanism called watchpoints, which is beyond the scope of this post, but the TL;DR is that this code will immediately become invalid if anyone assigns to f, which would produce a different result. Using other watchpoints we can also speculate that f.toString will always resolve to Function.prototype.toString which already leads to much better code:
function g():
f = 0x123456 // speculated value of Lookup("f")
toString = Function.prototype.toString // speculated value of Get(f, "toString")
result = Call(toString, f)
Return(result)
Intrinsics
Next, JSC has the concept of intrinsics. Intrinsics in JSC are when the compiler utilizes the knowledge that it’s calling a well known standard library function to emit optimized code inline. This is even more powerful than regular function inlining, since it can emit only the fast path inline and it works even if the standard library function was written in C++. In this case, the intrinsic tells us we are calling Function.prototype.toString and we can emit a new instruction, FunctionToString, instead of the generic function call. Our new pseudo instruction will try to load the cached value we computed in our very first optimization, but for the slow case, where we don’t have cached value yet, it will still call the C++ implementation. Computing the toString for the first time requires some really complex code that we don’t want to emit inline for every call to toString. Our code with our new instruction might look like this:
function g():
f = 0x123456
result = FunctionToString(f)
Return(result)
And diving a little deeper, the implementation of our new FunctionToString instruction might look like the following:
function g():
f = 0x123456
result = Load(f, "cachedToString") // Load a specific property of the object,
// much faster since it's just a memory
// access, not a JS property access like Get
if (!result) {
// For the slow case where we don't have a cached value we just fallback
// to calling the C++ code
toString = Function.prototype.toString
result = Call(toString, f)
}
Return(result)
Abstract Interpretation
The next step in speeding up our example is using our Abstract Interpreter (AI). The idea behind the AI is that it understands what our IR instructions will do to its operands at runtime, and if all of its operands are known (i.e. we proved they will always have the same value), we can try to compute the result of our instruction at compile time. In this case, since we already speculated that f will be 0x123456, when we’re compiling our FunctionToString instruction we can try to load the cachedToString property from the object at 0x123456. If that succeeds our generated code will look more like this:
function g():
result = "function f() { /* … some code here .. */ }"
Return(result)
That’s much better!
Recap
We can speed up the f.toString() call in our example by:
Caching the result
Speculating f will always be the same object
Speculating that f.toString will always resolve to Function.prototype.toString
Adding an Intrinsic and a new instruction, FunctionToString, that loads the cached value directly whenever it’s available
Teaching our abstract interpreter that if we already know which function we are stringifying, and its toString value has already been computed, we can just use the cached value as a constant.
If you want to dive deeper into the actual implementation you can find the commit here and if you have any questions feel free to reach out to me on Twitter.
Safari Technology Preview Release 128 is now available for download for macOS Big Sur and betas of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Safari Technology Preview Release 127 is now available for download for macOS Big Sur and betas of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
In case you’re not familiar with WebKit‘s multiprocess model, allow me to explain some basics: In WebKitGTK and WPE we currently have three main processes which collaborate in order to ultimately render the web pages (this is subject to change, probably with the addition of new processes):
The UI process: this is the process of the application that instantiates the web view. For example, Cog when using WPE or Epiphany when using WebKitGTK. This process is usually in charge of interacting with the user/application and send events and requests to the other processes.
The network process: the goal of this process is to perform network requests for the different web processes and make the results available to them.
The web process: this is the process that really performs the heavy lifting. It’s in charge of requesting the resources required by the page (through the network process), create the DOM tree, execute the JS code, render the content, respond to user events, etc.
In a simple case, we would have a UI process, a web process and a network process working together to render a page. There are situations where (due to security reasons, performance, build options, etc.) the same UI process can be using more than a single web process at the same time, while these share the network process. I won’t go into details about all the possibilities here because that’s beyond the goal of this post (but it’s a good idea for a future post!). If you want more information related to this you can search for topics like process prewarm, process swap on navigation or service workers running on a dedicated processes. In any case, at any given moment, one of those web processes is always the main one performing all the tasks mentioned before, and the others are helpers for concrete tasks, so the situation is equivalent to the simpler case of a single UI process, a web process and an network process.
Developers know that processes have the bad habit of getting hung sometimes, and that can happen here as well to any of the processes. Unfortunately, in many cases that’s probably due to some kind of bug in the code executed by the processes and we can’t do much more about that than fixing the bug. But there’s a special kind of block that affects the web process only, and it’s related to the execution of JS code. Consider this simple JS script for example:
setTimeout(function() {
while(true) { }
}, 1000);
This will create an infinite loop during the JS execution after one second, blocking the main thread of the web process, and taking 100% of the execution time. The main thread, besides executing the JS code, is also in charge of processing incoming events or messages from the UI process, perform the layout, initiate redraws of the page, etc. But as it’s locked in the JS loop, it won’t be able to perform any of those tasks, causing the web process to become unresponsive to user input or API calls that come through the UI process. There are other situations that could make the process unresponsive, but the execution of faulty JS is probably the most common one.
WebKit has some internal tools to detect these situations, that mainly consist on timeouts that mark the process as unresponsive when some expected reply is not received in time from the web process. But on WebKitGTK and WPE there wasn’t a way to notify the application that’s using the web view that the web process had become unresponsive, or a way to recover from this situation (other than closing the application or the tab). This is fixed in trunk for both ports by adding two components to the WebKitWebView class:
A new property named is-web-process-responsive: this property will signal responsiveness changes of the web process. The application using the web view can connect to the notifications of the object property as with any other GObject class, and can also get the its value using the new webkit_web_view_get_is_web_process_responsive() API method.
A new API method webkit_web_view_terminate_web_process() that can be used kill the web process when it becomes unresponsive (but will also work with responsive ones). Any load performed after this call will spawn a new and shiny web process. When this method is used to kill a web process, the web view will emit the signal web-process-terminated with WEBKIT_WEB_PROCESS_TERMINATED_BY_API as the termination reason.
With these changes, the browser can now connect to the is-web-process-responsive signal changes. If the web process becomes unresponsive, the browser can launch a dialog informing the user of the problem, and ask whether to wait or kill the process. If the user chooses to kill the process, the browser can use webkit_web_view_terminate_web_process() to kill the problematic process and then reload (or load a new page) to go back to functional state.
These changes, among other wonderful features we’re developing at Igalia, will be available on the upcoming 2.34 release of WebKitGTK and WPE. I hope they are useful for you
Safari Technology Preview Release 126 is now available for download for macOS Big Sur and betas of macOS Monterey. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Many of the new Safari 15 features are now available in Safari Technology Preview 126:
Streamlined tab bar. Use Tab Groups to save and organize your tabs. Experience the new design. Test your site. Experiment with theme-color.
Live Text. Select and interact with text in images on the web in macOS Monterey betas on M1 Macs.
Improved Safari Web Extensions. Try out the support for declarativeNetRequest, which expanded to 150K content blocking rules and non-persistent background pages for improved performance.
Quick Notes. Add links and Safari highlights to remember important information or ideas on the web in macOS Monterey betas.
WebGL 2. Try out the improved 3D graphics performance of WebGL running on top of Metal via ANGLE.
If you see bugs or unexpected behavior with the interface of Safari Technology Preview, please file Feedback in Apple’s Feedback Assistant. If you come across an implementation bug in web technology, or have a request, please file a ticket at bugs.webkit.org.
UPDATE: If you downloaded an earlier version of Safari Technology Preview 126, you may need to reboot.
There’s a lot of news coming out of WWDC21 about WebKit and the web technology that’s shipping in Safari 15 on Apple’s platforms. Many of the new features were announced on Monday, at this year’s WWDC21 Keynote, and listed in the Safari 15 Beta Release Notes. But that’s not all, and we’re excited to share it with you.
Ten sessions at WWDC21 will go into greater detail and announce even more. New videos are released each day this week. You can watch them on the WWDC21 website, or in the Apple Developer app for macOS, iOS, iPadOS, and tvOS.
You can be part of the conversations around WWDC on the Apple Developer Forums, share your thoughts with @WebKit team on Twitter, or use other ways of staying in touch with folks involved in the WebKit project.
Safari Web Extensions use HTML, CSS, and JavaScript to offer people powerful browser customizations — and you can now create them for every device that supports Safari. Learn how to build a Safari Web Extension that works for all devices, and discover how you can convert an existing extension to Safari through Xcode and the Safari Web Extension Converter.
Learn how you can extend Safari’s functionality with Safari Web Extensions. We’ll introduce you to the latest WebExtension APIs, explore non-persistent background page support — a particularly relevant topic if you’re developing for iOS — and discover how you can use the Declarative Net Request WebExtensions API to block content on the web. Lastly, we’ll show you how to customize tabs in Safari 15.
Explore the latest updates to WKWebView. We’ll show you how to use APIs to manipulate web content without JavaScript, explore delegates that can help with WebRTC and Downloads, and share how you can easily create a richer web experience within your app.
Despite their prevalence, passwords inherently come with challenges that make them poorly suited to securing someone’s online accounts. Learn more about the challenges passwords pose to modern security and how to move beyond them. Explore the next frontier in account security with secure-by-design, public-key-based credentials that use the Web Authentication standard. Discover in this technology preview how Apple is approaching this standard in iOS 15 and macOS Monterey.
Learn how you can support on-device verification codes in your app or website for a more secure sign-in experience. We’ll explore the latest updates to the iCloud Keychain password manager and discover how verification codes, AutoFill, iCloud Keychain sync, and two-tap setup simplify the multi-step sign-in process. We’ll show you how to support this process in your apps and websites.
Develop in JavaScript, WebGL, or WebAssembly? Learn how the latest updates to Safari and WebKit — including language changes to class syntax — can help simplify your development process, enhance performance, and improve security. We’ll explore several web APIs that can help provide better interoperability and bring new capabilities to your web content.
Web Inspector provides the tools for you to understand and debug your web pages on macOS, iOS, and iPadOS. We’ll take you through the latest features and improvements to Web Inspector, including a new overlay for inspecting CSS Grid containers on your pages, even more configurable breakpoints to make debugging simpler, and the ability to create and edit Audits.
Discover how you can measure your ad campaigns in apps and on the web without compromising privacy. We’ll introduce you to Private Click Measurement and explore SKAdNetwork, which provides you with a more secure, private, and useful way to measure your app installs.
Meet Safari 15: redesigned and ready to help people explore the web. Discover how you can approach designing websites and apps for Safari, and learn how to incorporate the tab bar in your designs. We’ll also take you through features like Live Text and accessibility best practices, explore the latest updates to CSS and Form Controls, and learn how to use the aspect-ratio property in CSS to create incredible websites.
Create SharePlay experiences that people can enjoy on the web and in your companion app. Learn how you can use the GroupActivities framework in combination with a companion website to bring SharePlay to Safari, letting people connect with each other for enjoyable group interactions — even if they haven’t yet downloaded your app from the App Store.
Today we are announcing an update to the MotionMark benchmark. This is a relatively small update, aimed at increasing test reliability and reproducibility. The largest change is the removal of the Focus subtest, which was causing significant variance in test results, and wasn’t measuring what it was intending to measure.
Benchmark Harness
Most of the changes in MotionMark 1.2 aim to reduce variance between multiple runs of the benchmark. We increased the warm-up time between each test by 1900ms and required at least 30 frames to be rendered in between tests to reduce interference between adjacent tests. Different tests stress different parts of the graphics pipeline, and their processing can overlap if they aren’t more strongly segmented.
We also implemented a few different strategies to decrease the benchmark’s sensitivity to individual frame times. The first is to make sure that the benchmark never makes any ramping decisions based on the time of a single frame, but instead requires at least 9 frames before adjusting complexity. In addition, the benchmark now discards outlier frame times.
Focus Subtest
Modern browsers use a compositing architecture, where part of the graphics work is responsible for drawing individual elements into layers, and other graphics work is responsible for compositing layers together into a final image. The interface between these two parts behaves differently in different browsers, and may indeed be entirely asynchronous, possibly providing almost no backpressure if the compositor is running more slowly than element painting.
In browser engines which run the compositor asynchronously (like WebKit), the Focus subtest measured how fast descriptions of work can be delivered to the compositor, rather than how fast the compositor can actually execute that work, which is what the subtest was trying to measure. In addition, the backpressure in the Focus subtest is indirect, passes through the scheduler, and is therefore noisy. It was causing a huge variance in subtest score on machines which have relatively high element painting performance compared to their compositor performance.
Conclusion
MotionMark 1.2 produces significantly less score variance than previous versions of MotionMark on a wide variety of machines of varying relative performance. Because of the removal of the Focus subtest, MotionMark 1.2 is also more reflective of real-world graphics performance across browsers.
ChangeLog
Here is a list of all the changes that have gone into MotionMark 1.2:
As you might already know this work is part of the Open Prioriziatation campaign by Igalia that has been funded by a lot of people. Thank you all for your support!
The high level summary is that the implementation in WebKit can be considered to be complete and all the :focus-visible patches have been included on the last Safari Technology Preview 125 as an experimental feature. Moreover, Igalia has been in conversations with Apple trying to find a way to enable the feature by default at some point.
Implementation details
As I’ve just mentioned, the implementation finished by the end of April, and no more patches have landed since then. It passes most of the WPT tests, there are still some minor differences here and there (like some input types matching or not :focus-visible) but those issues have been considered to be fine as they depend on the different browsers specific behavior.
You can test this feature in Safari Technology Preview (since release 125) by enabling the runtime flag in the menu (Develop > Experimental Features > :focus-visible pseudo-class). Please play with it and report any issue you might find.
Debate time
During the last patch reviews more Apple engineers got interested on the feature, and there were a bunch of discussions about whether it would (or should) change the default behavior in WebKit, and how.
So let’s start from the beginning, what is focus-visible? A broad description of :focus-visible is that it will match based on when the browser would natively show a focus ring. The typical example for this are buttons, in general when people click a button they don’t expect to see a focus ring, for that reason most browsers haven’t been showing it for years. When an element is focused browsers use some internal heuristics to decide when to show or not a focus ring.
However buttons in Safari are different to other browsers, and that’s because Safari follows the Mac platform conventions. Buttons are not click focusable in Safari (though you can still focus them via keyboard with Option + Tab), as they don’t receive focus on click they don’t even match :focus, so they never show a focus ring on mouse interactions. This behavior tries to mimic what happens on the Mac platform, but there are still some differences. The Mac platform standard, for example, allows that you can be editing an input, click on a button and keep editing the input as the focus is still there. However that’s not exactly what happens in Safari either, when you click the button, even if it doesn’t get the focus, the focus is gone from the input, so you cannot just continue editing it like in the platform. On top of that, an invisible navigation caret moves to that button on click, and further keyboard navigations start from there. So it’s kind of similar to the platform, but with some nuances.
This is only part of the problem, the web is full of things that are focusable, like <div tabindex="0"> elements. These elements have always matched (and still match) :focus by default, and have usually showed a focus ring when focused via mouse click. Web authors generally want to hide the focus ring when clicking on <div tabindex="0"> elements, and that’s why the current :focus-visible implementations don’t match in this case. Chrome and Firefox are using :focus-visible in the User Agent (UA) style sheet, so they don’t show a focus ring when clicking on such elements. However, Apple has expressed some concerns here that it might change the default focus indicator behavior in a way that might differ from their platform philosophy, and thus needs more review.
During these conversations an idea showed up as potential solution. What if we show a focus ring when users click on a generic <div tabindex="0">, but we don’t if that element has some specific role, e.g. <div tabindex="0" role="button">. This would give web authors the possibility to get their desired behavior by just adding a role to those elements.
This would make <div tabindex="0" role="button"> work similar to regular buttons on Mac, but there’s still one difference, those elements will still get the focus so some use cases might get broken. James Craig came out with a scenario in which an user is scrolling the page with the spacebar, then they click on a <div tabindex="0" role="button">, and if they enter spacebar again, that wouldn’t keep scrolling the page anymore. And the user won’t know exactly why, as they haven’t seen any focus ring after click (note that with the current :focus-visible implementation, they user will start to see a focus ring on that <div tabindex="0" role="button"> after entering the spacebar).
On that discussion James has shared an idea to add a new CSS property (or it could be a HTML attribute) that marks an element so it cannot receive focus via mouse click. That would make possible to make buttons work like in Safari in other browsers, or make a <div tabindex="0"> to work like a Mac button too. However this would be something new that would need to get implemented in all browsers, not just WebKit and that would need to get discussed and agreed with the web community.
On the same issue Brian Kardell is proposing some alternatives, for example having some special parameter like :focus-visible(platform) (syntax to be defined) that could behave differently in Safari than other browsers, so Safari can use it in the UA style sheet, while :focus-visible alone would work the same in all browsers.
As you see there’s not a clear solution to all this discussion yet, but we’re following it closely and providing our feedback to try to reach some final proposal that makes everyone happy.
Some numbers
Let’s do a final review of the total numbers (as nothing has changed in May):
26 PRs merged in WPT.
27 patches landed in WebKit.
9 patches landed in Chromium.
2 PRs merged in CSS spcs.
1 PR merged in HTML spec.
Wrapping up
:focus-visible has been added to WebKit thanks to the support from many individual people and organizations that make it happen through the Open Prioritization experiment by Igalia. Once more big thanks to you all! 🙏
In addition, the WPT test suite has been improved counting now ~40 tests for this feature. Also in January neither Firefox or Chrome were using :focus-visible on the UA style sheet, however they both use it there nowadays. Thus, doing the implementation on WebKit has helped to move forward this feature on different places.
There is still the ongoing discussion about when or how this could be enabled by default in WebKit and eventually shipped in Safari. That conversation is moving and we hope there’ll be some kind of positive resolution so this feature can be enjoyed by web authors in all the browser engines. Igalia will keep being on top of the topic and pushing things forward to make it happen.
Finally, thanks to everyone who has helped in the different conversations, reviews, etc. during these months.
Safari Technology Preview Release 125 is now available for download for macOS Big Sur and macOS Catalina. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
This is the last post of the series showing interesting debugging tools, I hope you have found it useful. Don’t miss the custom scripts at the bottom to process GStreamer logs, help you highlight the interesting parts and find the root cause of difficult bugs. Here are also the previous posts of the series:
This is useful to know why a particular package isn’t found and what are the default values for PKG_CONFIG_PATH when it’s not defined. For example:
Adding directory '/usr/local/lib/x86_64-linux-gnu/pkgconfig' from PKG_CONFIG_PATH
Adding directory '/usr/local/lib/pkgconfig' from PKG_CONFIG_PATH
Adding directory '/usr/local/share/pkgconfig' from PKG_CONFIG_PATH
Adding directory '/usr/lib/x86_64-linux-gnu/pkgconfig' from PKG_CONFIG_PATH
Adding directory '/usr/lib/pkgconfig' from PKG_CONFIG_PATH
Adding directory '/usr/share/pkgconfig' from PKG_CONFIG_PATH
If we have tuned PKG_CONFIG_PATH, maybe we also want to add the default paths. For example:
SYSROOT=~/sysroot-x86-64
export PKG_CONFIG_PATH=${SYSROOT}/usr/local/lib/pkgconfig:${SYSROOT}/usr/lib/pkgconfig
# Add also the standard pkg-config paths to find libraries in the system
export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/usr/local/lib/x86_64-linux-gnu/pkgconfig:\
/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig:\
/usr/lib/pkgconfig:/usr/share/pkgconfig
# This tells pkg-config where the "system" pkg-config dir is. This is useful when cross-compiling for other
# architecture, to avoid pkg-config using the system .pc files and mixing host and target libraries
export PKG_CONFIG_LIBDIR=${SYSROOT}/usr/lib
# This could have been used for cross compiling:
#export PKG_CONFIG_SYSROOT_DIR=${SYSROOT}
Man in the middle proxy for WebKit
Sometimes it’s useful to use our own modified/unminified files with a 3rd party service we don’t control. Mitmproxy can be used as a man-in-the-middle proxy, but I haven’t tried it personally yet. What I have tried (with WPE) is this:
Add an /etc/hosts entry to point the host serving the files we want to change to an IP address controlled by us.
Configure a web server to provide the files in the expected path.
:bulb: Pro tip: If you have to debug minified/obfuscated JavaScript code and don’t have a deobfuscated version to use in a man-in-the-middle fashion, use http://www.jsnice.org/ to deobfuscate it and get meaningful variable names.
Bandwidth control for a dependent device
If your computer has a “shared internet connection” enabled in Network Manager and provides access to a dependent device , you can control the bandwidth offered to that device. This is useful to trigger quality changes on adaptive streaming videos from services out of your control.
This can be done using tc, the Traffic Control tool from the Linux kernel. You can use this script to automate the process (edit it to suit to your needs).
Useful scripts to process GStreamer logs
I use these scripts in my daily job to look for strange patterns in GStreamer logs that help me to find the cause of the bugs I’m debugging:
h: Highlights each expression in the command line in a different color.
mgrep: Greps (only) for the lines with the expressions in the command line and highlights each expression in a different color.
filter-time: Gets a subset of the log lines between a start and (optionally) an end GStreamer log timestamp.
highlight-threads: Highlights each thread in a GStreamer log with a different color. That way it’s easier to follow a thread with the naked eye.
remove-ansi-colors: Removes the color codes from a colored GStreamer log.
aha: ANSI-HTML-Adapter converts plain text with color codes to HTML, so you can share your GStreamer logs from a web server (eg: for bug discussion). Available in most distros.
gstbuffer-leak-analyzer: Analyzes a GStreamer log and shows unbalances in the creation/destruction of GstBuffer and GstMemory objects.
In this new post series, I’ll show you how both existing and ad-hoc tools can be helpful to find the root cause of some problems. Here are also the older posts of this series in case you find them useful:
Use strace to know which config/library files are used by a program
If you’re becoming crazy supposing that the program should use some config and it seems to ignore it, just use strace to check what config files, libraries or other kind of files is the program actually using. Use the grep rules you need to refine the search:
First, try to strace -e trace=signal -p 1234 the killed process.
If that doesn’t work (eg: because it’s being killed with the uncatchable SIGKILL signal), then you can resort to modifying the kernel source code (signal.c) to log the calls to kill():
If you ever find yourself with little time in front of a stubborn build system and, no matter what you try, you can’t get the right flags to the compiler, think about putting something (a wrapper) between the build system and the compiler. Example for g++:
#!/bin/bash
main() {
# Build up arg[] array with all options to be passed
# to subcommand.
i=0
for opt in "$@"; do
case "$opt" in
-O2) ;; # Removes this option
*)
arg[i]="$opt" # Keeps the others
i=$((i+1))
;;
esac
done
EXTRA_FLAGS="-O0" # Adds extra option
echo "g++ ${EXTRA_FLAGS} ${arg[@]}" # >> /tmp/build.log # Logs the command
/usr/bin/ccache g++ ${EXTRA_FLAGS} "${arg[@]}" # Runs the command
}
main "$@"
Make sure that the wrappers appear earlier than the real commands in your PATH.
The make wrapper can also call remake instead. Remake is fully compatible with make but has features to help debugging compilation and makefile errors.
The source code shown below must be placed in the .h where the class to be debugged is defined. It’s written in a way that doesn’t need to rebuild RefCounted.h, so it saves a lot of build time. It logs all refs, unrefs and adoptPtrs, so that any anomaly in the refcounting can be traced and investigated later. To use it, just make your class inherit from LoggedRefCounted instead of RefCounted.
Example output:
void WTF::adopted(WTF::LoggedRefCounted<T>*) [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount 1
void WTF::adopted(WTF::LoggedRefCounted<T>*) [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount 1
^^^ Two adopts, this is not good.
void WTF::LoggedRefCounted<T>::ref() [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount 1 --> ...
void WTF::LoggedRefCounted<T>::ref() [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount ... --> 2
void WTF::LoggedRefCounted<T>::deref() [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount 2 --> ...
void WTF::LoggedRefCounted<T>::deref() [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount ... --> 1
void WTF::adopted(WTF::LoggedRefCounted<T>*) [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount 1
void WTF::LoggedRefCounted<T>::deref() [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount 1 --> ...
void WTF::LoggedRefCounted<T>::deref() [with T = WebCore::MediaSourceClientGStreamerMSE]: this=0x673c07a4, refCount 1 --> ...
^^^ Two recursive derefs, not good either.
It only works #if ENABLE(DEVELOPER_MODE), so you might want to remove those ifdefs if you’re building in Release mode.
Log tracers
In big pipelines (e.g. playbin) it can be very hard to find what element is replying to a query or handling an event. Even using gdb can be extremely tedious due to the very high level of recursion. My coworker Alicia commented that using log tracers is more helpful in this case.
GST_TRACERS=log enables additional GST_TRACE() calls all accross GStreamer. The following example logs entries and exits into the query function.
GST_TRACERS=log GST_DEBUG='query:TRACE'
The names of the logging categories are somewhat inconsistent:
log (the log tracer itself)
GST_BUFFER
GST_BUFFER_LIST
GST_EVENT
GST_MESSAGE
GST_STATES
GST_PADS
GST_ELEMENT_PADS
GST_ELEMENT_FACTORY
query
bin
The log tracer code is in subprojects/gstreamer/plugins/tracers/gstlog.c.
Safari Technology Preview Release 124 is now available for download for macOS Big Sur and macOS Catalina. If you already have Safari Technology Preview installed, you can update in the Software Update pane of System Preferences on macOS.
Again this is a status report about the work Igalia is doing on the implementation of :focus-visible in WebKit, which is part of the Open Prioriziatation campaign. Thanks everyone supporting this!
There has been some nice progress in April, though some things are still under ongoing discussion.
Script focus and :focus-visible
Finally we decided to reflect the reality in the script focus tests that I created, and merge them on WPT. The ones where the implementations Chromium and Firefox don’t match the agreed expectations (the ones when you click a non-focusable element, and that click moves focus elsewhere), where marked as .tentative.
So we opened a separated issue to explain the situation and gather more feedback about what to do here.
Once we had the tests I implemented the same behavior as other browsers, the current one reflected on the tests, in WebKit. The patch got reviewed and merged, so script focus and :focus-visible work the same in all browsers right now.
:focus-visible and Shadow DOM
The test that checks that :focus-visible doesn’t match on ShadowRoot was also merged in WPT. That’s the current behavior on WebKit implementation too. More about this in January’s post.
On the review of those patches, some new discussion started about different things related to :focus-visible feature, like why a keyboard input triggers :focus-visible matching. The discussion with Apple engineers is ongoing on the bug and let’s see how it ends.
Some numbers
Let’s take a look to the numbers again:
26 PRs merged in WPT (5 in April).
27 patches landed in WebKit (10 in April).
9 patches landed in Chromium (2 in April).
2 PRs merged in CSS spcs.
1 PR merged in HTML spec.
Next steps
Implementation is mostly over, now the goal is to close the discussions with the different parties and check the possibilities of shipping this in WebKit.
Thanks everyone that has provided input in the different discussions and jumped on the patches review. Your feedback has been really useful to keep moving this forward.