WebKit Open Source Web Browser Engine 2024-05-14T15:20:55Z https://webkit.org/feed/atom/ WordPress <![CDATA[WebKit Features in Safari 17.5]]> https://webkit.org/?p=15383 2024-05-14T15:20:55Z 2024-05-13T18:30:02Z Happy May! It’s time for another release of Safari — our third significant update of 2024. With just a month until WWDC24 and the unveiling of what’s coming later this year, we are happy to get these 7 features and 22 bug fixes into the hands of your users today.

CSS

There are several exciting new CSS features in Safari 17.5, including text-wrap: balance, the light-dark() color function, and @starting-style, plus the ability to use feature queries with @import rules. Let’s look at how you can put each one to use.

Text wrap balance

On the web, with its flexible container widths, inconsistent lengths of content, and variation between browsers, it can feel impossible to avoid having text wrap in such a way that too few words end up all by themselves on a very short last line.

Very long text headline wrapping using the normal algorithm — which leaves a single word on the last line, all by itself

When type was set by hand, typographers would painstakingly avoid this undesirable result by manually moving content around. Over the decades, web developers have tried a series of different tricks to avoid orphans in CSS, in HTML, in JavaScript, and in content management systems. None work very well. The attempts usually feel hacky, laborious, and fragile.

To solve this and other frustrations, the CSS Working Group has defined three new options that you can use to change how text will wrap. You can switch from default wrapping to another style with text-wrap. WebKit for Safari 17.5 adds support for the first of these new options — balancing.

The text-wrap: balance rule asks the browser to “balance” the lines of text and make them all about the same length.

A very long headline wrapped using text-wrap: balance, so each of the three lines are the same length as each other — and none of them fill all the horizontal space available

You can see how now the text no longer fills the containing block — there’s a large amount of space on the right of the words. This is expected, and something you’ll want to think about as you decide when to use text-wrap: balance.

Where exactly each line of text will break when using text-wrap: balance may be slightly different in each browser. The CSS Text level 4 web standard leaves it up to each browser engine team to decide which algorithm they want to use in determining how exactly to wrap balanced text.

It can be computationally expensive for the browser to count characters and balance multiple lines of text, so the standard allows browsers to limit the number of lines that are balanced. Chromium browsers balance 6 or fewer lines, Firefox balances 10 or fewer, while Safari/WebKit balances an unlimited numbers of lines.

For now, Safari does not balance text if it’s surrounding a float or initial letter. And Safari disables the balancer if the content contains preserved tabs or soft hyphens.

Text wrap shorthands and longhands

The text-wrap property is actually a shorthand for two longhand properties: text-wrap-style and text-wrap-mode.

The text-wrap-mode property provides a mechanism for expressing whether or not text should wrap.

text-wrap-mode: wrap; /* initial value */
text-wrap-mode: nowrap;

The wrap value turns it on, and the nowrap value turns it off, just like the values for white-space. (In fact, text-wrap-mode is the newly introduced longhand of white-space.) WebKit added support for text-wrap-mode: wrap and nowrap in Safari 17.4.

The text-wrap-style property selects how to wrap. The initial value is auto — asking text to wrap in the way it has for decades. Or, you can choose a value to switch to another “style” of wrapping.

WebKit for Safari 17.5 adds support for text-wrap-style: balance, stable, and auto.

text-wrap-style: auto; /* initial value */
text-wrap-style: balance;
text-wrap-style: stable;

Of course, the text-wrap shorthand is a way to combine text-wrap-mode and text-wrap-style and declare them together. If you write text-wrap: balance it’s the same as text-wrap: wrap balance, meaning: “yes, please wrap, and when you do, please balance the text”.

Full support will eventually include three properties and six values. No browser supports everything yet, so be sure to look up support for the text-wrap, text-wrap-mode, and text-wrap-style properties, as well as the balance, pretty, stable, auto, wrap, and nowrap values.

The balance, pretty, and stable values will simply fall back to auto in browsers without support, so progressive enhancement is easy. You can use these values today, no matter how many of your users don’t yet have a browser with support. They will simply get auto-wrapped text, just like they would if you didn’t use text-wrap. Meanwhile, those users with support will get an extra boost of polish.

Dark mode and the light-dark() color function

More and more, users expect websites and web apps to support dark mode. Since Safari 12.1, the prefers-color-scheme media query has given you the ability to write code like this:

body {
  background: white;
  color: black;
}
@media (prefers-color-scheme: dark) {
  body {
    background: darkslategray;
    color: white;
  }
}

Or perhaps you’ve used variables to define colors for both light and dark mode at once, making it easier to use them everywhere.

:root {
  --background: white;
  --text: black;
}
@media (prefers-color-scheme: dark) {
  :root {
    --background: darkslategray;
    --text: white;
  }
}
body {
  background: var(--background);
  color: var(--text);
}

Well, now there’s a new option — the light-dark() function. It makes defining colors for dark mode even easier.

First, inform the browser you are providing a design for both light and dark modes with the color-scheme property. This prompts the browser to switch the default user agent styles when in dark mode, ensuring the form controls appear in dark mode, for example. It’s also required for light-dark() to work correctly.

:root {
  color-scheme: light dark;
}

Then, any time you define a color, you can use the light-dark() function to define the first color for light mode, and the second color for dark mode.

color: light-dark(black, white);
background-color: light-dark(white, darkslategray);

You can still use variables, if you’d like. Perhaps you want to structure your code like this.

:root {
  color-scheme: light dark;
  --background: light-dark(black, white);
  --text: light-dark(white, darkslategray);
}
body {
  background: var(--background);
  color: var(--text);
}

An often-asked question when learning about light-dark() is “does this only work for colors?” Yes, this function only works for colors. Use the prefers-color-scheme media query to define the rest of your color-scheme dependent styles.

Starting style

WebKit for Safari 17.5 adds support for @starting-style. It lets you define starting values for a particular element. This is needed to enable a transition when the element’s box is created (or re-created).

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

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

Many developers are excited to use @starting-style along with display: none interpolation. To do so, WebKit also needs to support animation of the display property, which has not yet shipped in Safari. You can test this use case today in Safari Technology Preview.

Features queries for importing CSS

WebKit for Safari 17.5 adds the supports() syntax to @import rules. Now you can conditionally import CSS files based on whether or not there’s support for a certain feature.

@import <url> supports(<feature>);

For example, you could load different stylesheets based on whether or not CSS Nesting is supported.

@import "nested-styles.css" supports(selector(&));
@import "unnested-styles.css" supports(not selector(&));

Or you could load certain CSS files when a browser does not have support for Cascade Layers. (Note that any @import rules with layer() will automatically be ignored in a browser without layer support.)

@import url("reset.css") layer(reset);
@import url("framework.css") layer(framework);
@import url("custom.css") layer(custom);

@import url("unlayered-fallback-styles.css") supports(not at-rule(@layer));

Or simply test for a feature. Here, these layout styles will only be loaded if Subgrid is supported.

@import url("layout.css") supports(grid-template-columns: subgrid);

WebCodecs

WebKit for Safari 17.5 adds support for AV1 to WebCodecs when an AV1 hardware decoder is available.

WebGL

WebKit for Safari 17.5 adds WebGL support for EXT_conservative_depth and NV_shader_noperspective_interpolation.

WKWebView

WKWebView adds support for logging MarketplaceKit errors to the JavaScript console. This will make errors easier to debug.

Bug Fixes and more

In addition to these new features, WebKit for Safari 17.5 includes work polishing existing features.

Accessibility

  • Fixed a bug preventing VoiceOver word echoing in some text fields. (122451549) (FB13592798)

Animations

  • Fixed flickering with multiple accelerated animations and direction changes. (117815004)

Authentication

  • Fixed excludeCredentials property being ignored during a passkey registration request. (124405037)

CSS

  • Fixed the proximity calculation for implicit @scope. (124640124)
  • Fixed the Grid track sizing algorithm logical height computation avoid unnecessary grid item updates. (124713418)
  • Fixed any @scope limit making the element out of scope. (124956673)

Forms

  • Fixed native text fields becoming invisible in dark mode. (123658326)
  • Fixed fallback native <select> rendering in dark mode. (123845293)

Media

  • Fixed scrolling for an element when a video element with pointer-events: none is placed over it. (118936715)
  • Fixed HTML5 <audio> playback to continue to the next media activity when in the background. (121268089) (FB13551577)
  • Fixed AV1 to decode in hardware on iPhone 15 Pro. (121924090)
  • Fixed audio distortion over internal speakers when streaming content in web browsers. (122590884)
  • Fixed firing loadeddata events for <audio> and <video> on page load. (124079735) (FB13675360)

Rendering

  • Fixed adjusting the size of the scrollable area when changing betwen non-overlay and overlay scrollbars. (117507268)
  • Fixed flickering when showing a layer on a painted background for the first time by avoiding async image decoding. (117533495)
  • Fixed line breaking before or between ruby sequences. (122663646)

Web API

  • Fixed mousemove events in an iframe when the mouse is clicked from outside the iframe and then moves into it while the button is held down. (120540148) (FB13517196)

Web Apps

  • Fixed several issues that caused Web Push to not show notifications when the web app or Safari was not already running. (124075358)

Web Inspector

  • Fixed info and debug buttons not appearing in the Console Tab until new console messages are displayed. (122923625)

WebRTC

  • Fixed WebCodecs to correctly use the VP9 hardware decoder. (123475343)
  • Fixed no incoming video in Teams VA. (124406255)
  • Fixed the camera pausing occasionally when torch is enabled. (124434403)

Updating to Safari 17.5

Safari 17.5 is available on iOS 17.5, iPadOS 17.5, macOS Sonoma 14.5, macOS Ventura, macOS Monterey and in visionOS 1.2.

If you are running macOS Ventura or macOS Monterey, you can update Safari by itself, without updating macOS. On macOS Ventura, go to  > System Settings > General > Software Update and click “More info…” under Updates Available.

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

Feedback

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

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

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

]]>
<![CDATA[Release Notes for Safari Technology Preview 194]]> https://webkit.org/?p=15406 2024-05-09T20:38:28Z 2024-05-09T20:38:28Z Safari Technology Preview Release 194 is now available for download for macOS Sonoma and macOS Ventura. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 277150@main…277790@main.

Accessibility

Resolved Issues

  • Fixed range input not firing an input event when incremented or decremented via accessibility APIs. (277182@main) (85707481)
  • Fixed setting aria-hidden on a slot not hiding the slot’s assigned nodes. (277359@main) (108762653)
  • Fixed aria-hidden=true to be ignored on the <body> and <html> elements. (277677@main) (123049663)

Canvas

Resolved Issues

  • Fixed OffscreenCanvas failing to render to the placeholder with nested workers. (277328@main) (126069375)
  • Fixed losing the contents layer of the placeholder canvas of OffscreenCanvas when switching off the tab. (277654@main) (126070648)

CSS

Resolved Issues

  • Fixed an incorrect difference between implicit and explicit initial values for custom properties. (277670@main) (124573975)
  • Fixed excluding -apple-pay-button from applying to any element that supports appearance: auto and is not a button. (277268@main) (126107516)
  • Fixed missing color interpretation methods added to CSS color specifications. (277481@main) (126444371)

JavaScript

New Features

  • Added support for the v flag with RegExp.prototype[Symbol.matchAll]. (277160@main) (126017731)
  • Added support for Unicode 15.1.0 characters in RegExp. (277786@main) (126863692)

Resolved Issues

  • Fixed RegExp.prototype.@@split to update the following legacy RegExp static properties: RegExp.input, RegExp.lastMatch, RegExp.lastParen, RegExp.leftContext, RegExp.rightContext, and RegExp.$1, ... RegExp.$9. (277559@main) (99865597)
  • Fixed logical assignment expressions to throw a syntax error when the left side of the assignment is a function call. (277536@main) (126540636)

Networking

Resolved Issues

  • Fixed incorrect Sec-Fetch-Site value for navigation of a nested document. (277662@main (109358563)

Rendering

Resolved Issues

  • Fixed incorrect View Transition snapshot position when page is scrolled. (277371@main) (123668892)
  • Fixed Masonry to correctly perform intrinsic sizing. (277403@main) (124240255)
  • Fixed visual overflow causing shifts in View Transitions. (277183@main) (125882228)
  • Fixed snapshot scaling when animating View Transitions. (277636@main) (125931538)
  • Fixed capturing the root background showing a white area at bottom in View Transitions. (272224@main) (125931851)
  • Fixed incorrect grid item positioning with out-of-flow sibling. (277300@main) (126207467)
  • Fixed visual overflow rect to be recomputed at every frame of View Transitions. (277482@main) (126240398)
  • Fixed break-word with a float discarding text. (277461@main) (126309547)
  • Fixed rendering 3D transforms in View Transitions. (277639@main) (126513916)
  • Fixed image capture rectangle not including positioned descendants. (277714@main) (126660740)

SVG

New Features

  • Added animation support for the d property in SVG. (277299@main) (126206354)

Resolved Issues

  • Fixed clipping an SVG when an SVGFilter is applied to its root. (265135@main) (118938065)

Web Animations

Resolved Issues

  • Fixed updating an animation when changing the value of a transform property while that property is animated with an implicit keyframe. (277257@main) (126126617)
  • Fixed animating with color-mix. (277256@main) (126148201)

Web API

New Features

  • Added support for serializing shadow roots through getHTML() as well as the corresponding shadow root serializable feature. (277374@main) (125513986)

Resolved Issues

  • Fixed throwing exceptions in navigation methods if in a detached state. (277487@main) (123898636)
  • Fixed a minor issue in URL’s host setter. (277531@main) (124363495)
  • Fixed GeolocationCoordinates to expose a toJSON() method. (277347@main) (126183686)
  • Fixed GeolocationPosition to expose a toJSON() method. (277413@main) (126247408)
  • Fixed setting CustomEvent.target when dispatching an event. (277435@main) (126311287)

Web Extensions

Resolved Issues

  • Fixed permissionState to be requested if an extension has the webNavigation permission. (277308@main) (126212951)

WebGL

New Features

  • Enabled support for the following approved extensions: EXT_render_snorm, OES_sample_variables, and OES_shader_multisample_interpolation. (277775@main) (126863775)
]]>
<![CDATA[JetStream 2.2]]> https://webkit.org/?p=15378 2024-04-30T20:42:01Z 2024-04-30T20:42:01Z JetStream 2 is a suite of JavaScript and WebAssembly performance benchmarks that runs 64 subtests. We recently fixed several minor issues we discovered that impact running the benchmark.

One issue happened when running JetStream on fast hardware where the resolution of time to run a subtest, as measured by the timing code, occasionally returned 0. This caused problems when calculating subtest scores. Two other issues were found in the sub-test segmentation, where the code didn’t create the initial Float32Array for the test and another race condition issue was fixed in the task queue code for that test.

Two other issues running JetStream via the command-line were found and fixed. The first was specific to the Spider Monkey command-line shell and the second was to fix the Wasm command-line script when running with both the D8 and Spider Monkey shells.

We are introducing JetStream 2.2, which fixes the issues mentioned above and includes the same subtests as JetStream 2.1 as well as the original JetStream 2. The fixes improve the stability of the benchmark. We measured JetStream 2.2’s benchmark scores from Chrome, Safari, and Firefox and found them to be as stable as JetStream 2.1. Measurements were performed using a 16“ MacBook Pro with an M3 processor. The system was not running other applications and the browser app being tested, configured without extensions enabled, was restarted before each JetStream run. A total of 10 runs were performed for each browser app JetStream version combination. Scores produced from JetStream 2.2 are not comparable to other versions of any JetStream benchmark.

]]>
<![CDATA[Release Notes for Safari Technology Preview 193]]> https://webkit.org/?p=15375 2024-04-24T23:23:59Z 2024-04-24T23:23:59Z Safari Technology Preview Release 193 is now available for download for macOS Sonoma and macOS Ventura. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 276610@main…277149@main.

Accessibility

Resolved Issues

  • Fixed hidden elements targeted by aria-labelledby to expose their entire subtree text, not just their direct child text. (276864@main) (125634439)
  • Fixed accessible name computation for elements with visibility: visible inside a container with visibility: hidden. (277004@main) (125738704)

CSS

Resolved Issues

  • Fixed the Grid track sizing algorithm logical height computation avoid unnecessary grid item updates. (276633@main) (124713418)
  • Fixed the style adjuster for @starting-style incorrectly invoking with a null element. (276993@main) (125837628)

Forms

Resolved Issues

  • Fixed the value attribute not getting displayed in an input element with type="email" and the multiple attribute. (276895@main) (125221858)

JavaScript

Resolved Issues

  • Fixed inconsistent output of Function.prototype.toString for accessor properties. (276904@main) (125739577)

Rendering

Resolved Issues

  • Fixed intrinsic inline size calculators to account for whitespace before an empty child with nonzero margins. (276875@main) (122586712)
  • Fixed overlapping elements with flex box when height: 100% is applied on nested content. (276880@main) (125572851)
  • Fixed block containers that are scroll containers to default to unsafe alignment. (276929@main) (125742095)

Web API

New Features

  • Added support for PopStateEvent’s hasUAVisualTransition. (277001@main) (125849073)

Resolved Issues

  • Fixed cloning of ShadowRoot nodes following a DOM Standard clarification. (277066@main) (125917138)

Web Inspector

Resolved Issues

  • Fixed Console and code editor completion not auto-scrolling the suggestion into view. (277034@main) (124979790)
  • Fixed search in the DOM tree view unexpectedly chaning the text display. (277073@main) (125797803)
]]>
<![CDATA[Help us invent CSS Grid Level 3, aka “Masonry” layout]]> https://webkit.org/?p=15269 2024-04-21T02:12:22Z 2024-04-19T22:00:55Z If you’ve been making websites for years, you know how frustrating it was to lay out a web page with CSS floats. Managing sizes and placement was tedious and time consuming. Being creative was often impossible. CSS Grid greatly eased that pain with Grid Level 1 in 2017, and now with Grid Level 2, aka Subgrid. But even with the powerful CSS of today, not every layout imaged by designers is possible. In fact, when CSS Grid shipped, one of the most commonly asked questions was: “how do I write CSS to accomplish a masonry layout?” Sadly, for the last seven years the answer has been — you can’t.

What is masonry layout?

What do we mean by the term “masonry layout”? Basically it’s the pattern seen in the following image — where content packs together like a brick or stone wall. That’s where it gets the name “masonry”. It’s also frequently called “waterfall layout”, as a metaphor for how content flows down the page like a waterfall.

Two dozen photos of different aspect ratios laid out using a "masonry" pattern

This layout is popular because it solves a few problems that other layouts do not.

  1. It allows for content of different aspect ratios, and avoids the need to crop or truncate content in order to turn everything into uniform rectangles.
  2. It distributes content across the page (instead of flowing down each column, one by one). This follows the natural reading order as you scroll the page. And it lets the website lazy-load additional content at the bottom without moving existing content around.

This layout creates uniformly-sized columns, without any rows. It’s quite possible that because this layout has required JavaScript, anything more creative or complex has been too hard to pull off — and we’ve been left with an expectation that masonry layout should only ever be a simple pattern with uniformly-sized columns. Let’s see what’s possible if we build it into CSS instead.

Inventing masonry

A mechanism in CSS for “masonry layout” was first proposed by Mozilla in January 2020 as an extension of CSS Grid, and implemented as an experiment behind a flag in Firefox Nightly. In 2022, Apple started implementing this CSS Grid Level 3 proposal in Safari Technology Preview (where it’s currently on by default), and we’ve been helping to move the web standard along to reach maturity.

However, there are big questions still being asked about how CSS should handle masonry-style layouts. Some people remain skeptical that this capability should be part of CSS Grid, and want it to instead be its own separate display type. Others are questioning whether or not this kind of layout is needed on the web at all — they aren’t sure that well-known websites will use it. With such fundamental disagreements at play, no browser can ship. We must first come to consensus in the CSS Working Group.

This is where we need your help. We’d like real-world web designers and developers to weigh into the discussion, and express what it is that you want. Your input really can make a difference.

In this article, we’ll walk through how the CSS Grid Level 3 proposal works, and how you can use its new capabilities. We’ll show you why we believe these features should be part of CSS Grid, and explain what the alternative would be if the CSS Working Group creates display: masonry instead. And then, we’ll ask you to join the debate to help move us forward. Please do read to the end.

Four demos

To show why we at Apple believe this capability should be part of CSS Grid, we created four demonstrations. If you’d like, try them yourself at webkit.org/demos/grid3. View these demos in a browser that supports Grid Level 3 — currently Safari Technology Preview or Firefox after you’ve turned on the feature flag.

Note there’s a control panel for each demo, with the relevant layout code printed to the page. Turn on “Number items” to see the relationship between the HTML order of content and the layout placement of that content.

Screenshot showing the controls of the demo — here with the numbers turned on

Each demo has a multitude of variations. Switch between variations from the dropdown menu, which changes only the CSS. The HTML stays the same.

Screenshot showing the controls of the demo — here with the dropdown of various alternative layouts showing

Creating a classic masonry / waterfall layout

First, let’s take a look at how to build a classic masonry/waterfall layout. In this gallery of photos, each image is wrapped with a figure element, and the figures are direct children of a main element.

<main>
  <figure><img src="photo-1.jpg"></figure>
  <figure><img src="photo-2.jpg"></figure>
  <figure><img src="photo-3.jpg"></figure>
</main>

We start by applying display: grid to the main element to create the Grid container. Then we can define grid-template-columns however we’d like.

In this case, let’s use grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)) to ask the browser to repeat a looped definition to create a set of flexible columns that are each of a minimum of 14rem. This results in uniformly-sized columns, typical of the classic masonry/waterfall layout. The gap: 1rem; rule creates a 1rem-wide space between the items — both between the columns, and horizontally between items.

And then, we’ll define the “rows” with the masonry value. (It’s likely the name of this value will change before this ships in browsers — more on that at the end of this article. For now, masonry is what works.)

main { 
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: 1rem;
  grid-template-rows: masonry;
}

The grid-template-rows: masonry rule tells the browser: “Please do not create rows. Instead pack the content into a masonry/waterfall-like pattern.”

That’s it! In four lines of CSS, with zero media queries or container queries, we’ve created a flexible layout that works on screens of all sizes. And there’s no need to crop content to force everything into same-sized boxes.

masonry layout of photos

In graphic design, a layout that has uniformly-sized columns and no rows is often called a “symmetrical columnar grid”. For centuries, columnar grids were the dominant type of grid used in page design.

Leveraging Grid’s full power to define columns

Now let’s dive into the advantages of combining the full power of CSS Grid with masonry/waterfall packing. CSS Grid provides many options for defining grid our columns. Using fr units to create a symmetrical grid is only one option of many.

How could these possibilities be used for a masonry/waterfall-style layout? Let’s try mixing fixed-sized columns with flexible columns. We could make the first and last column fixed-sized, while the middle columns are flexible, changing in both size and number.

Specifically, the first and last columns are exactly 14 characters wide, while the middle columns are flexible (at least 28 characters wide) and change in number to fill the available space.

main { 
  display: grid;
  grid-template-columns: 14ch repeat(auto-fill, minmax(28ch, 1fr)) 14ch;
  grid-template-rows: masonry;
  gap: 1rem;
}

This is just one of many, many possibilities.

CSS Grid allows for a lot of creativity with its options for defining grid tracks:

  • fixed sizes defined in any unit (px, em, rem, cqi, lh, ch, ic, cap, vw, svh, and many more)
  • max-content and min-content
  • the full power of fr units
  • minmax() functions
  • %-sized
  • auto

These options in CSS Grid allow you to create something much more dynamic and flexible in interesting ways. You can create two stages of flexibility, because the fr-unit sized columns grow and shrink in a separate stage from the minmax()-sized columns. The max-content and min-content values let you size the columns based on the content size, rather than sizing the content based on the column size. The fr units can easily be used to create compound or asymmetrical grids, where the columns are different sizes. The options are endless.

By adding the ability to pack content in a masonry/waterfall pattern to CSS Grid, we maintain the full power of Grid for defining our columns in whichever manner we like.

For example, let’s use grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr) minmax(16rem, 2fr)) minmax(8rem, 1fr) to create a pattern of alternating narrow and wider columns, where all the columns are flexible. More columns are added when there’s space. And there’s always an odd number of columns, ending with a narrow one.

three web browser windows next to each other, showing how the layout adjusts for narrow, medium and wider windows

Even when we define columns using only the simple fr unit, the full power provided in CSS Grid means different columns can be set to different sizes. For fun, let’s use fr units to define a set of columns sized to inject the vibes of the golden ratio by using the Fibonacci sequence in grid-template-columns: 1fr 1fr 2fr 3fr 5fr 8fr;

layout of photos where the columns on the left are very narrow, getting bigger and bigger as them move to the right, in a fibonacci sequence

In a more practical example, let’s use max-content when defining our columns. Content-based sizing is an incredibly power feature of CSS Grid. This demo of a mega menu layout uses grid-template-columns: repeat(auto-fill, minmax(max-content, 30ch)); to ensure that every column is big enough to fit every link without wrapping text.

a menu with a ton of links, like in a website footer, laid out with Grid Level 3

Mega menus have been hard to code, especially across multiple screen sizes. With CSS Grid Level 3, it becomes incredibly easy. A few lines of code creates a dynamic layout which adds and removes columns one at a time as space allows — without any media/container queries, and with wrapping prevention.

Many of these examples could never be created with masonry as a separate display type. The discussion of display: masonry is to only allow symmetrical columns (columns that are the same size as each other), much like multicolumn layout does today.

Leveraging Grid’s ability to let content span columns

CSS Grid also lets us span items across multiple columns. Let’s use that capability to see what interesting options might emerge. How about making every 5th image span two grid columns, while the rest of the images are span one column.

same photo layout, now with random photos being bigger

What if instead, we put a wider class on specifically on images that have a wider aspect-ratio, to make those images span multiple columns. We can also change the styling a bit, making the corners square instead of round, and reducing the grid gap to zero. This gives us a different way to pack photos of different aspect ratios together.

another layout of image, this time where wider images are wider

We also experimented with combining the classic masonry/waterfall layout of photos with View Transitions. When a user clicks/taps on any photo, it grows to span multiple columns. The browser automagically animates the transition. (This demo requires Safari Technology Preview 192 or later.)

These variations of the Photos and Mega Menu demos are just a small taste of all of the many possibilities you get when leveraging the full power of CSS Grid in the column direction, while simultaneously turning off rows.

Columnar vs. Modular Grids

What happens when we keep experimenting? Let’s let go of thinking about “masonry”, and start imagining Grid Level 3 purely as an expansion of Grid. At its core, CSS Grid Level 3 provides a mechanism for turning off rows. It lets us create a columnar grid — a grid that’s made up of columns alone.

By contrast, a modular grid is a grid where everything is lined up in both columns and rows. They became popular in the 20th century during the dominance of modernism in graphic design. CSS Grid Level 1 is really good at making modular grids… that’s what it wants to do. In fact, float-based layouts also encouraged the creation of modular grids on the web, since you had to make all your content the same height to get your floats to clear. Images need to be the same aspect ratio. Text has to be the same length. This is often accomplished on the back-end with policies enforced by the content management system, or on the front-end by CSS that truncates/crops the content.

It’s incredible common for websites to do some variation of this sort of modular grid, laid out here with CSS Grid Level 1.

layout of text, in ridged sets of boxes, row after row, all the same height

Of course, this example is overly simplistic. The article ledes look bare with no teaser images. The uniformity is so strict and formal, the design lacks life. Real websites find other ways to breathe life back into the design.

But what if the layout itself could also provide some vitality and interest? What will happen if we use CSS Grid to create a columnar grid as easily as it creates a modular grid? What if we don’t truncate content, and instead let it be the size that it wants to be — and get the layout to work for the content, rather than forcing the content to work for the layout?

A classic masonry/waterfall layout with various lengths of text looks like this, which is already more engaging since a user can read more about each article:

same article teaser text, this time laid out in columns set with masonry

Although, that’s still a bit visually repetitive. Symmetrical columnar grids often are. We need the rest of the power of CSS Grid to do something more interesting. Let’s make the latest article much bigger, and have it span four columns. A handful of other recent articles can be medium-sized and span two columns. While the older content is smaller, spanning one column.

same text, this time laid out with much more dynamic use of 'masonry-style layout plus spanning columns

Now this otherwise visually boring text is starting to look fairly lively. If we were to add images to each of these articles, it would be it even more dynamic.

Let’s experiment with mixing images and text together on a webpage for a museum. The first grid item is a header that introduces the museum, and provides navigation to other resources. The rest of the content consists of pieces of artwork and their information: title, artist, year, medium, catalog number and location.

Because the paintings are gorgeous, the content looks pretty great in a classic masonry/waterfall layout.

layout of cards, each with a painting and text

Let’s see what else we can do by utilizing two more powerful features of CSS Grid — subgrid and explicit placement.

Using subgrid and explicit placement

The functionality provided by subgrid in CSS Grid Level 2 is incredible, and it’s finally supported in most browsers.

Instead of listing the painting’s metadata in a single left-aligned column, let’s see how we might better use the available space. By using subgrid, we can put the year and catalog number on the right of each card — and line up this data for one painting with the same data for the other paintings.

close-up of this layout, with the Grid Inspector showing, with lines marking columns showing how subgrid works

By adding this new functionality to CSS Grid Level 3, we get the benefit of existing developer tools. You can use the Grid Inspector in Safari Technology Preview today as you try out grid-template-rows: masonry.

If masonry is its own display type, and not part of CSS Grid, it will not get the benefit of subgrid.

We can also use the power of CSS Grid Level 1 to explicitly place the header into the last two columns, moving it to the top right corner of the page with grid-column: -3 / -1.

In just a few lines of layout code, we are using the full power of CSS Grid Levels 1, 2, and 3 to create flexible columns that change in number to accommodate the available size — without using any media queries or container queries.

Hopefully you can see the advantages of fully combining a mechanism for masonry/waterfall layouts with CSS Grid — providing many more creative possibilities than masonry alone.

The Debate

So let’s get into the debate that’s been blocking the CSS Working Group from moving forward. Our hope is that web designers and developers chime in (post to social media, write blog posts) with your thoughts about which direction CSS should take.

Some people, including those of us at Apple, like having “Masonry” be part of CSS Grid. We believe this functionality is a mechanism to expand CSS Grid — allowing it to finally create columnar grids as well as modular grids. And we want this functionality to be mixed with all the other features of Grid, including the powerful options for defining a columns, track spanning, explicit placement, and subgrid.

Other people instead believe Masonry should be its own separate display type. At first glance, defining Masonry with a new display type might make a lot of sense. You do get a tidy separation between layout types.

display: block;
display: inline;
display: flexbox;
display: grid;
display: masonry;

The CSS Working Group has not discussed how the syntax for a separate Masonry display type would work, but perhaps it would be patterned after Multicolumn layout.

main { 
  display: masonry;
  columns: 28ch;
}

Or perhaps the syntax would be patterned after Grid, but with significant limitations:

main {
  display: masonry;
  masonry-columns: repeat(5, minmax(28ch, 1fr)); 
                   /* where only one repeating width is allowed */
}

Either way, it’s clear that advocates of this option want Masonry to be limited to a symmetrical grid — where all the columns are the same size as each other. None of the rest of CSS Grid’s track sizing capabilities would be allowed.

Making masonry a simple and separate layout type would avoid the work necessary to keep Grid and Masonry working together in combination — both now and in the long term. Doing this would simplify the layout model, make it easier to implement in browsers, reduce the potential for performance traps, and allow the feature sets of Grid and Masonry to diverge.

Conversely, we believe the effort needed to add this capability to CSS Grid is worth the many benefits to be had. The CSS Grid Level 3 specification has already been written, and implemented in two browser engines. And yes, while making CSS Grid more complex will make it harder to extend in the future, we believe there’s an advantage to having these two types of grid layouts intertwined. This way the CSS Working Group will always define all new additions for both modular and columnar grids. There won’t be something added to display: grid that will be left out of display: masonry, or vice versa. For example, many developers want CSS Grid Level 4 to provide a mechanism for styling grid areas and grid lines — perhaps a way to add a background color to a track, or create a rule line in a gap. It’d be great to ensure that will work for both modular and columnar grids from Day 1.

Another argument made by advocates of display: masonry is that that masonry is conceptually a fundamentally different layout type from CSS Grid, and therefore should have its own display type. They often describe CSS Grid as inherently being about “lining things up in two-dimensions”, and since masonry only lines things up in one dimension, “it’s not a grid”. (In fact, some have advocated that Masonry is more like Flexbox, since “both line things up in one direction”.)

In many ways, your perspective on this question might depends on what you imagine a grid is.

What is a grid?

Grids are an incredibly important aspect of graphic design. Grids are used to line up text, images and other content in a regular pattern. They help readability and usability by making things predictable.

You can trace their use through thousands of years of history.

two photos of book pages, showing historic columnar layouts from long ago
[Left] Antoine Vérard published a French translation of Boccaccio in 1498. Seen in Thirty Centuries of Graphic Design, by James Craig and Bruce Barton, 1987. [Right] A Russian illustrated sheet proclaiming that ‘Hops are head above all other fruit’. Seen in Books: A Living History by Martyn Lyons, 2011.

It wasn’t until the 20th century that European and American modernists started promoting the idea that “proper” graphic design grids should line content up in both directions — in rows as well as columns.

spread from a book, showing two different newspaper home pages, where articles line up in a modular grid — in both row and column directions
Massimo Vignelli is especially well-known for promoting the idea that lining things up in both columns and rows is a superior practice of graphic design. Two examples from The Vignelli Canon, 2010.

Even today, there is a lot of debate about which type of grid is the best grid or the only legitimate grid. Many designers claim a 12 column grid is the only correct way to design a web page — or 12 columns for “desktop”, 8 columns for “tablet”, and 4 columns for “phones”. At times designers have gotten quite religious about their ideas of what a “proper grid” looks like.

Mark Boulton argued for years that symmetrical columnar grids are incredibly formulaic and boring. He promoted the use of asymmetrical compound grids in design for the web. Today, luckily CSS Grid Level 1 makes it incredibly easy to create both asymmetrical grids and compound grids, giving designers the freedom to create what they want. But only if they also want all their grids to be a modular grids.

Both modular and columnar grids are in fact grids. And CSS Grid deserves the ability to also create columnar grids.

photograph of an open book, showing many many hand-drawn diagrams of how different layouts can use different numbers of columns
Ideas for designing columnar grids, in Editing by Design, by Jan V. White.

We believe there’s an opportunity for CSS to enable a rich history of design grids to come to the web — and would be greatly disappointed to see the new masonry feature limited to only allowing symmetrical columnar grids.

But what do you think?

We want to hear from you

This is where you come in. Try some demos of your own. Write about your thoughts on your own blog. Comment at the CSS Working Group in this issue.

  • Should “masonry”/“waterfall” be part of CSS Grid?
  • Do you want the capabilities to define a columnar grid with CSS Grid — to use subgrid, spanning, explicit placement, and all the many options for track sizing? Or do you only want the ability to define a classic masonry layout with equal-sized columns?
  • Will you use this? What might you create with it?
  • Do you have links to demos you’ve made? We want to see your ideas and use cases.
  • Are there things you want to do that you can’t do with this model?

Often, thinking about something theoretically and actually seeing it in use can be very different. To make sure the CSSWG gets the design of this feature correct, we need developers to gain some hands-on experience, and articulate your thoughts about what it’s like.

The WebKit team has been working on Masonry for a year and a half. It was first enabled by default in Safari Technology Preview 163 in February 2023. There’s a bit more polish needed, and details to work out (naming being one). But we would like to ship this feature soon. To do so, these fundamental questions need to be resolved.

Thank you for your help!

P.S. About the name…

It’s likely masonry is not the best name for this new value. Names in CSS are usually simple words that directly describe the result they create — like center, under, contain, clip, revert, ltr, always, break-word, hidden, allow-end, scale-down, wrap, smooth.

The word “masonry” is more of a metaphor, where the meaning has to be explained with a backstory. Such a term is harder to remember for developers who do not speak English. And arguably, the syntax could just as easily be grid-template-rows: waterfall instead, since that’s the dominant word for this layout used in certain regions, not masonry.

Plus, once you start to write a lot of code using this feature, it’s likely you’ll come to the realization that we did — this really isn’t about the layout used by Pinterest or other similar sites. This is a mechanism for telling the browser, “please create a grid, but without any rows.

Perhaps the best syntax could be grid-template-rows: none; to convey “please do not give me any rows”. Sadly, it’s too late to use this name, because none is the default value for grid-template-* and means “please give me only implicit rows, no explicit ones”.

Instead we could use the name off to convey “please turn off the grid in the row direction, and give me only columns”.

main { 
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  grid-template-rows: off; 
}

The CSSWG is debating this name in this issue. If you have ideas or preferences for a name, please join that discussion.

Meanwhile, masonry is the value that is currently implemented in Safari Technology Preview, since that’s what the Editor’s Draft currently uses. And so that’s what we used in our demos above, and what you should use in yours. But do expect the name of this value to change in the future. And perhaps prepare for a future where we call this “columnar grid” or “Grid Level 3” instead of “Masonry”.

]]>
<![CDATA[Release Notes for Safari Technology Preview 192]]> https://webkit.org/?p=15260 2024-04-11T00:13:53Z 2024-04-10T21:30:54Z Safari Technology Preview Release 192 is now available for download for macOS Sonoma and macOS Ventura. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 276247@main…276863@main.

Authentication

Resolved Issues

  • Fixed setting the cancel flag once the cancel completes regardless of a subsequent request occurring. (276368@main) (124727713)

CSS

New Features

  • Added support for View Transitions. (276426@main) (123128491)

Resolved Issues

  • Fixed setting white-space to a non-default value dynamically on a whitespace or a new line. (276277@main) (92559818)
  • Fixed the proximity calculation for implicit @scope. (276345@main) (124640124)
  • Fixed the Grid track sizing algorithm logical height computation avoid unnecessary grid item updates. (276633@main) (124713418)
  • Fixed any @scope limit making the element out of scope. (276359@main) (124956673)
  • Fixed the contrast of Menu and MenuText system colors. (276597@main) (125270664)
  • Fixed keeping the shorthand value for CSS gap as-is in serialized and computed values. (276794@main) (125335787)

Deprecations

  • Removed WEBKIT_KEYFRAMES_RULE and WEBKIT_KEYFRAME_RULE in CSSRule. (276264@main) (97084520)
  • Removed the SVGAnimateColorElement interface. (276683@main) (122586568)

Editing

Resolved Issues

  • Fixed certain text being duplicated or misplaced. (276317@main) (123642870)
  • Fixed word completion suggestion interfering with a website’s suggestion results. (276344@main) (124727588)

JavaScript

Resolved Issues

  • Fixed Object.groupBy and Map.groupBy to work for non-objects. (276736@main) (125485685)
  • Fixed Array.fromAsync to not call the Array constructor twice. (276752@main) (125509304)

Media

New Features

  • Added support for MSE in workers. (276389@main) (123052315)

Resolved Issues

  • Fixed scrolling for an element when a video element with pointer-events: none is placed over it. (276807@main) (118936715)
  • Fixed allowing a video’s currentTime to be further than the gap’s start time. (276761@main) (124186726)
  • Fixed sampleRate and numberOfChanges to be required and non-zero in a valid AudioEncoderConfig. (276413@main) (125107934)
  • Fixed media elements appending the same media segment twice. (276821@main) (125386530)

Rendering

Resolved Issues

  • Fixed adjusting the size of the scrollable area when changing betwen non-overlay and overlay scrollbars. (276439@main) (117507268)
  • Fixed flickering when showing a layer on a painted background for the first time by avoiding async image decoding. (276513@main) (117533495)
  • Fixed line breaking before or between ruby sequences. (276353@main) (122663646)

Web API

New Features

  • Added support for URL.parse(). (276656@main) (125376520)
  • Added support for shadowRootDelegatesFocus and shadowRootClonable to <template>. (276631@main) (125401993)

Resolved Issues

  • Fixed URL text fragment directives not fully stripped from JavaScript. (276370@main) (107326333)
  • Fixed inserting a <picture> element displaying the same image twice. (276754@main) (123795045)

Web Extensions

New Features

  • Updated to use the web extension architecture in open-source WebKit code. Web extension authors are encouraged to test your extensions and report issues. (123908710)

Web Inspector

Resolved Issues

  • Fixed console clearing unexpectedly when Web Inspector reopens. (276705@main) (124171190)
]]>
<![CDATA[Optimizing WebKit & Safari for Speedometer 3.0]]> https://webkit.org/?p=15249 2024-04-10T16:07:51Z 2024-04-10T17:00:50Z The introduction of Speedometer 3.0 is a major step forward in making the web faster for all, and allowing Web developers to make websites and web apps that were not previously possible. In this article, we explore ways the WebKit team made performance optimizations in WebKit and Safari based on the Speedometer 3.0 benchmark.

In order to make these improvements, we made an extensive use of our performance testing infrastructure. It’s integrated with our continuous integration, and provides the capability to schedule A/B tests. This allows engineers to quickly test out performance optimizations and catch new performance regressions.

Improving Tools

Proper tooling support is the key to identifying and addressing performance bottlenecks. We defined new internal JSON format for JavaScriptCore sampling profiler output to dump and process them offline. It includes a script which processes and generates analysis of hot functions and hot byte codes for JavaScriptCore. We also added FlameGraph generation tool for the dumped sampling profiler output which visualizes performance bottlenecks. In addition, we added support for JITDump generation on Darwin platforms to dump JIT related information during execution. And we improved generated JITDump information for easy use as well. These tooling improvements allowed us to quickly identify bottlenecks across Speedometer 3.0.

Improving JavaScriptCore

Revising Megamorphic Inline Cache (IC)

Megamorphic IC offers faster property access when one property access site observes many different object types and/or property names. We observed that some frameworks such as React contain a megamorphic property access. This led us to continuously improve JavaScriptCore’s megamorphic property access optimizations: expanding put megamorphic IC, adding megamorphic IC for the in operation, and adding generic improvements for megamorphic IC.

Revising Call IC

Call IC offers faster function calls by caching call targets inline. We redesigned Call IC and we integrated two different architectures into different tiers of Just-In-Time (JIT) compilers. Lower level tiers use Call IC without any JIT code generation and the highest level tier uses JIT code generatiton with the fastest Call IC. There is a tradeoff between code generation time and code efficiency, and JavaScriptCore performs a balancing act between them to achieve the best performance across different tiers.

Optimizing JSON

Speedometer 3.0 also presented new optimization opportunities to our JSON implementations as they contain more non-ASCII characters than before. We made our fast JSON stringifier work for unicode characters. We also analyzed profile data carefully and made JSON.parse faster than ever.

Adjusting Inlining Heuristics

There are many tradeoffs when inlining functions in JavaScript. For example, inline functions can more aggressively increase the total bytecode size and may cause memory bandwidth to become a new bottleneck. The amount of instruction cache available in CPU can also influence how effective a given inlining strategy is. And the calculus of these tradeoffs change over time as we make more improvements to JavaScriptCore such as adding new bytecode instruction and changes to DFG’s numerous optimization phases. We took the release of the new Speedometer 3.0 benchmark as an opportunity to adjust inlining heuristics based on data collected in modern Apple silicon Macs with the latest JavaScriptCore.

Make JIT Code Destruction Lazy

Due to complicated conditions, JavaScriptCore eagerly destroyed CodeBlock and JIT code when GC detects they are dead. Since these destructions are costly, they should be delayed and processed while the browser is idle. We made changes so that they are now destroyed lazily, during idle time in most cases.

Opportunistic Sweeping and Garbage Collection

In addition, we noticed that a significant amount of time goes into performing garbage collection and incremental sweeping across all subtests in both Speedometer 2.1 and 3.0. In particular, if a subtest allocated a large number of JavaScript objects on the heap, we would often spend a significant amount of time in subsequent subtests collecting these objects. This had several effects:

  1. Increasing synchronous time intervals on many subtests due to on-demand sweeping and garbage collection when hitting heap size limits.
  2. Increasing asynchronous time intervals on many subtests due to asynchronous garbage collection or timer-based incremental sweeping triggering immediately after the synchronous timing interval.
  3. Increasing overall variance depending on whether timer-based incremental sweeping and garbage collection would fall in the synchronous or asynchronous timing windows of any given subtest.

At a high level, we realized that some of this work could be performed opportunistically in between rendering updates — that is, during idle time — instead of triggering in the middle of subtests. To achieve this, we introduced a new mechanism in WebCore to provide hints to JavaScriptCore to opportunistically perform scheduled work after the previous rendering update has completed until a given deadline (determined by the estimated remaining time until the next rendering update). The opportunistic task scheduler also accounts for imminently scheduled zero delay timers or pending requestAnimationFrame callbacks: if it observes either, it’s less likely to schedule opportunistic work in order to avoid interference with imminent script execution. We currently perform a couple types of opportunistically scheduled tasks:

  • Incremental Sweeping: Prior to the opportunistic task scheduler, incremental sweeping in JavaScriptCore was automatically triggered by a periodically scheduled 100 ms timer. This had the effect of occasionally triggering incremental sweeping during asynchronous timing intervals, but also wasn’t aggressive enough to prevent on-demand sweeping in the middle of script execution. Now that JavaScriptCore is knowledgable about when to opportunistically schedule tasks, it can instead perform the majority of incremental sweeping in between rendering updates while there aren’t imminently scheduled timers. The process of sweeping is also granular to each marked block, which allows us to halt opportunistic sweeping early if we’re about to exceed the deadline for the next estimated rendering update.
  • Garbage Collection: By tracking the amount of time spent performing garbage collection in previous cycles, we’re able to roughly estimate the amount of time needed to perform the next garbage collection based on the number of bytes visited or allocated since the last cycle. If the remaining duration for performing opportunistically scheduled tasks is longer than this estimated garbage collection duration, we immediately perform either an Eden collection or full garbage collection. Furthermore, we integrated activity-based garbage collections into this new scheme to schedule them at appropriate timing.

Overall, this strategy yields a 6.5% total improvement in Speedometer 3.0*, decreasing the time spent in every subtest by a significant margin, and a 6.9% total improvement in Speedometer 2.1*, significantly decreasing the time spent in nearly all subtests.

* macOS 14.4, MacBook Air (M2, 2022)

Various Miscellaneous Optimizations for Real World Use Cases

We extensively reviewed all Speedometer 3.0 subtests and did many optimizations for realistic use cases. The examples include but are not limited to: faster Object.assign with empty objects, improving object spread performance, and so on.

Improving DOM code

Improving DOM code is Speedometer’s namesake, and that’s exactly what we did. For example, we now store the NodeType in the Node object itself instead of relying on a virtual function call. We also made DOMParser use a fast parser, improved its support of li elements, and made DOMParser not construct a redundant DocumentFragment. Together, these changes improved TodoMVC-JavaScript-ES5 by ~20%. We also eliminated O(n^2) behavior in the fast parser for about ~0.5% overall progression in Speedometer 3.0. We also made input elements construct their user-agent shadow tree lazily during construction and cloning, the latter of which is new in Speedometer 3.0 due to web components and Lit tests. We devirtualized many functions and inlined more functions to reduce the function call overheads. We carefully reviewed performance profile data and removed inefficiency in hot paths like repeated reparsing of the same URLs.

Improving Layout and Rendering

We landed a number of important optimizations in our layout and rendering code. First off, most type checks performed on RenderObject are now done using an inline enum class instead of virtual function calls, this alone is responsible for around ~0.7% of overall progression in Speedometer 3.0.

Improving Style Engine

We also optimized the way we compute the properties animated by Web Animations code. Previously, we were enumerating every animatable properties while resolving transition: all. We optimized this code to only enumerate affected properties. This was ~0.7% overall Speedometer 3.0 progression. Animating elements can now be resolved without fully recomputing their style unless necessary for correctness.

Speedometer 3.0 content, like many modern web sites, uses CSS custom properties extensively. We implemented significant optimizations to improve their performance. Most custom property references are now resolved via fast cache lookups, avoiding expensive style resolution time property parsing. Custom properties are now stored in a new hierarchical data structure that reduces memory usage as well.

One key component of WebKit styling performance is a cache (called “matched declarations cache”) that maps directly from a set of CSS declarations to the final element style, avoiding repeating expensive style building steps for identically styled elements. We significantly improved the hit rate of this cache.

We also improved styling performance of author shadow trees, allowing trees with identical styles to share style data more effectively.

Improving Inline Layout

We fixed a number of performance bottlenecks in inline layout engine as well. Eliminating complex text path in Editor-TipTap was a major ~7% overall improvement. To understand this optimization, WebKit has two different code paths for text layout: the simple text path, which uses low level font API to access raw font data, and the complex text path, which uses CoreText for complex shaping and ligatures. The simple text path is faster but it does not cover all the edge cases. The complex text path has full coverage but is slower than the simple text path.

Previously, we were taking the complex text path whenever a non-default value of font-feature or font-variant was used. This is because historically the simple text path wouldn’t support these operations. However, we noticed that the only feature of these still missing in the simple text path was font-variant-caps. By implementing font-variant-caps support for the simple text path, we allowed the simple text path to handle the benchmark content. This resulted in 4.5x improvement in Editor-TipTap subtest, and ~7% overall progression in Speedometer 3.0.

In addition to improving the handling of text content in WebKit, we also worked with CoreText team to avoid unnecessary work in laying out glyphs. This resulted in ~0.5% overall progression in Speedometer 3.0, and these performance gains will benefit not just WebKit but other frameworks and applications that use CoreText.

Improving SVG Layout

Another area we landed many optimizations for is SVG. Speedometer 3.0 contains a fair bit of SVG content in test cases such as React-Stockcharts-SVG. We were spending a lot of time computing the bounding box for repaint by creating GraphicsContext, applying all styles, and actually drawing strokes in CoreGraphics. Here, we adopted Blink’s optimization to approximate bounding box and made ~6% improvement in React-Stockcharts-SVG subtest. We also eliminated O(n^2) algorithm in SVG text layout code, which made some SVG content load a lot quicker.

Improving IOSurface Cache Hit Rate

Another optimization we did involve improving the cache hit rate of IOSurface. An IOSurface is a bitmap image buffer we use to paint web contents into. Since creating this object is rather expensive, we have a cache of IOSurface objects based on their dimensions. We observed that the cache hit rate was rather low (~30%) so we increased the cache size from 64MB to 256MB on macOS and improved the cache hit rate by 2.7x to ~80%, improving the overall Speedometer 3.0 score by ~0.7%. In practice, this means lower latency for canvas operations and other painting operations.

Reducing Wait Time for GPU Process

Previously, we required a synchronous IPC call from the Web Process to the GPU process to determine which of the existing buffers had been released by CoreAnimation and was suitable to use for the next frame. We optimized this by having the GPUP just select (or allocate) an appropriate buffer, and direct all incoming drawing commands to the right destination without requiring any response. We also changed the delivery of any newly allocated IOSurface handles to go via a background helper thread, rather than blocking the Web Process’s main thread.

Improving Compositing

Updates to compositing layers are now batched, and flushed during rendering updates, rather than computed during every layout. This significantly reduces the cost of script-incurred layout flushes.

Improving Safari

In addition to optimizations we made in WebKit, there were a handful of optimizations for Safari as well.

Optimizing AutoFill Code

One area we looked at was Safari’s AutoFill code. Safari uses JavaScript to implement its AutoFill logic, and this execution time was showing up in the Speedometer 3.0 profile. We made this code significantly faster by waiting for the contents of the page to settle before performing some work for AutoFill. This includes coalescing handling of newly focused fields until after the page had finished loading when possible, and moving lower-priority work out of the critical path of loading and presenting the page for long-loading pages. This was responsible for ~13% progression in TodoMVC-React-Complex-DOM and ~1% progression in numerous other tests, improving the overall Speedometer 3.0 score by ~0.9%.

Profile Guided Optimizations

In addition to making the above code changes, we also adjusted our profile-guided optimizations to take Speedometer 3.0 into account. This allowed us to improve the overall Speedometer 3.0 score by 1~1.6%. It’s worth noting that we observed an intricate interaction between making code changes and profile-guided optimizations. We sometimes don’t observe an immediate improvement in the overall Speedometer 3.0 score when we eliminate, or reduce the runtime cost of a particular code path until the daily update of profile-guided optimizations kicks. This is because the modified or newly added code has to benefit from profile-guided optimizations before it can show a measurable difference. In some cases, we even observed that a performance optimization initially results in a performance degradation until the profile-guided optimizations are updated.

Results

With all these optimizations and dozens more, we were able to improve the overall Speedometer 3.0 score by ~60% between Safari 17.0 and Safari 17.4. Even though individual progressions were often less than 1%, over time, they all stacked up together to make a big difference. Because some of these optimizations also benefited Speedometer 2.1, Safari 17.4 is also ~13% faster than Safari 17.0 on Speedometer 2.1. We’re thrilled to deliver these performance improvements to our users allowing web developers to build websites and web apps that are more responsive and snappier than ever.

]]>
<![CDATA[Release Notes for Safari Technology Preview 191]]> https://webkit.org/?p=15243 2024-04-11T00:09:18Z 2024-03-28T14:16:05Z Safari Technology Preview Release 191 is now available for download for macOS Sonoma and macOS Ventura. If you already have Safari Technology Preview installed, you can update it in System Settings under General → Software Update.

This release includes WebKit changes between: 274942@main…276246@main.

Accessibility

New Features

  • Added support for ariaBrailleLabel and ariaBrailleRoleDescription element reflection properties. (275591@main) (123926949)

Resolved Issues

  • Fixed a bug preventing VoiceOver word echoing in some text fields. (275694@main) (122451549) (FB13592798)
  • Fixed time input accessibility by adding labels to subfields. (275301@main) (122590568)
  • Fixed datetime values being exposed to assistive technologies in the wrong timezone. (275265@main) (123522296)
  • Fixed time control accessibility by adding a label to the meridiem component. (276195@main) (123781149)
  • Fixed wrong datetime value being exposed to assistive technologies for datetime-local inputs. (275548@main) (123803281)
  • Fixed the computed role for these elements: dd, details, dt, em, hgroup, option, s, and strong. (276240@main) (124641956)

Authentication

Resolved Issues

  • Fixed excludeCredentials property being ignored during a passkey registration request. (276006@main) (124405037)

CSS

Resolved Issues

  • Fixed getComputedStyle() to work with functional pseudo-elements like ::highlight(). (274846@main) (117864743)
  • Fixed changing color scheme to update gradients with system colors or light-dark(). (275645@main) (121285450)
  • Fixed :empty selector to work with animations. (275832@main) (122838142)
  • Fixed starting style to inherit from its parent’s after-change style. (275061@main) (123302667)
  • Fixed CSS container style queries to allow !important. (275104@main) (123374708)
  • Fixed preserving whitespace when serializing custom properties. (275236@main) (123491915)
  • Fixed updating style correctly for non-inherited custom property mutations. (275445@main) (123645196)
  • Fixed element removed by parent to end up losing the last remembered size. (275606@main) (123975513)
  • Fixed resolving container units in style queries. (275689@main) (124058441)

Forms

Resolved Issues

  • Fixed native text fields becoming invisible in dark mode. (275363@main) (123658326)
  • Fixed <select multiple> scrollbars to match the used color scheme. (275505@main) (123807167)
  • Fixed fallback native <select> rendering in dark mode. (275532@main) (123845293)

JavaScript

Resolved Issues

  • Fixed String.prototype.replace to not take the fast path if the pattern is RegExp Object and the lastIndex is not numeric. (275255@main) (101122567)
  • Fixed Symbol.species getters to not share a single JS Function. (275064@main) (120416817)
  • Fixed try/catch to not intercept errors originated in [[Construct]] of derived class. (275353@main) (121959506)
  • Fixed TypedArray sorting methods to have a special-case for camparator returning false. (276130@main) (122093956)
  • Fixed emitReturn() to load this value from arrow function lexical environment prior to the TDZ check. (275425@main) (122430056)
  • Fixed NFKC normalization to work with Latin-1 characters. (275062@main) (123328161)
  • Fixed parsing of private names with Unicode start characters. (275152@main) (123425805)
  • Fixed instanceof to not get RHS prototype when LHS is primitive. (275318@main) (123629166)
  • Fixed bracket update expression to resolve property key at most once. (275531@main) (123872374)
  • Fixed bracket compound assignement to resolve the property key at most once. (276014@main) (124420301)

Lockdown Mode

Resolved Issues

  • Fixed: Updated the trusted list of fonts to address missing glyphs on some sites. (275705@main) (123658932)

Media

New Features

  • Added support for WebCodecs AV1 when an AV1 hardware decoder is available. (275174@main) (123433815)

Resolved Issues

  • Fixed audio distortion over internal speakers when streaming content in web browsers. (275262@main) (122590884)
  • Fixed playback for MSE videos on some sites. (275443@main) (123528095)
  • Fixed firing loadeddata events for <audio> and <video> on page load. (275997@main) (124079735) (FB13675360)

SVG

Resolved Issues

  • Fixed returning the initial value for the SVG gradient stop-color if it is not rendered in the page. (274997@main) (123262508)
  • Fixed the SVG marker segment calculations if the marker path consists of sub-paths. (275167@main) (123434203)

Web API

Resolved Issues

  • Fixed cssTextsetter to change the style attribute when the serialization differs. (276176@main) (29861252) (FB5535475)
  • Fixed mousemove events in an iframe when the mouse is clicked from outside the iframe and then moves into it while the button is held down. (275157@main) (120540148) (FB13517196)
  • Fixed getGamepads() to no longer trigger an insecure contexts warning. (274962@main) (123039555)

Deprecations

  • Removed HashChangeEvent‘s non-standard initHashChangeEvent() method. (276232@main) (124736521)

Web Inspector

Resolved Issues

  • Fixed expanded sections of Storage to not collapse. (275783@main) (107687975)
  • Fixed CSS font property values marked !important not getting overridden when using the interactive editing controls. (275389@main) (112080113)
  • Fixed runtimes to be aligned in the Audit tab. (275298@main) (121810292)
  • Fixed info and debug buttons not appearing in the Console Tab until new console messages are displayed. (275914@main) (122923625)
  • Fixed remembering the message type selection in the Console tab. (275143@main) (122924275)
  • Fixed autocomplete for the text-indent property suggesting prefixed properties instead of each-line or hanging. (274979@main) (123240715)
  • Fixed background autocompletion suggestion to include repeating-conic-gradient. (275160@main) (123428709)

WebRTC

Resolved Issues

]]>
<![CDATA[Introducing Natural Input for WebXR in Apple Vision Pro]]> https://webkit.org/?p=15162 2024-03-19T19:20:19Z 2024-03-19T16:30:13Z Apple Vision Pro is here, and with it, a lot of excitement about the possibilities for WebXR in visionOS. Support is in progress, where you can test it today.

WebXR now includes a more natural and privacy-preserving method for interaction — the new transient-pointer input mode — available for Safari 17.4 in visionOS 1.1. Let’s explore how natural input for WebXR works, and how to leverage it when developing a WebXR experience for Apple Vision Pro.

Background

WebXR lets you transform 3D experiences created in WebGL into immersive, spatial experiences available directly in the browser. Defined in web standards governed by the W3C, WebXR is an excellent choice for presenting immersive content to your users.

One challenge, though, is that because it’s completely immersive — and rendered entirely through WebGL — it’s not possible to provide interaction via DOM content or the traditional two-dimensional input available on a typical web page via a mouse or trackpad. This, combined with the fact that a spatial experience requires spatial input, means WebXR needs a totally new interaction model.

The interaction model of visionOS, known as natural input, uses a combination of eyes and hands for input. A user simply looks at a target and taps their fingers to interact. (Or uses the alternatives provided by accessibility features.)

The initial web standards for WebXR assumed all input would be provided by persistent hardware controllers. Since the natural input interaction model of visionOS differs from XR platforms which rely on listening to physical controllers and button presses, many existing WebXR experiences won’t work as intended on Apple Vision Pro.

We’ve been collaborating in the W3C to incorporate support for the interaction model of visionOS into WebXR. And we’re excited to help the WebXR community add support to popular WebXR frameworks.

Using Natural Input interactions

Since WebXR in visionOS requires the use of spatial inputs, rather than a trackpad, touch, or mouse, and the DOM isn’t visible within a WebXR session, inputs are provided as part of the XRSession itself. Events related to the input, e.g. select, selectstart and selectend are then dispatched from the session object. The XRInputSources are available within the xrSession.inputSources array. Because the default WebXR input in visionOS is transient, that array is empty — until the user pinches. At that moment, a new input is added to the array, and the session fires an inputsourceschange event followed by a selectstart event. You can use these for detecting the start of the gesture. To differentiate this new input type, it has a targetRayMode of transient-pointer.

Ongoing interaction

The XRInputSource contains references to two different positions in space related to the input: the targetRaySpace and the gripSpace. targetRaySpace represents the user’s gaze direction, this space begins with its origin between the user’s eyes and points to what the user was looking at the start of the gesture. The targetRaySpace is initially set to the direction of the user’s gaze but is updated with the movement of their hand rather than their eyes — that is, a movement of the hand to the left will move that ray to the left as well. The gripSpace represents the location of the user’s pinching fingers at the current point in time.

The targetRaySpace can be used for finding what the user wanted to interact with when they started the gesture typically by raycasting into the scene and picking the intersected object and the gripSpace can be used for the positioning and orientation of objects near the user’s hand for interaction purposes, e.g. to flip a switch, turn a dial or pick up an item from the virtual environment. To learn more about finding the pose of the input, see Inputs and input sources on MDN.

End of interaction

Three events are fired from the session object when the user releases the pinch.

First is a select and selectEnd event, both referencing the input as the event.inputSource object. The session then fires a final inputsourceschange event, indicating that this inputSource has been removed.

Benefits for simplicity and privacy

As each input represents a single, tracked point in space per pinch, and because it exists only for the duration of the user’s pinch, significantly less data about a user’s motion is required overall.

Combining transient inputs with hand tracking

WebXR on Safari in visionOS continues to support full hand tracking as well, supplying hand joint information for the duration of the experience. If the call to navigator.xr.requestSession has included hand-tracking as an additional feature and this is granted by the user, the first two inputs in the inputSources list will be standard tracked-pointers supplying this joint information. For information on requesting features in an XRSession, refer to the documentation at MDN. Because these inputs persist for the duration of the session, any transient-pointer inputs will appear further down the list. The hand inputs are supplied for pose information only and do not trigger any events.

Testing natural input on visionOS

Using the visionOS simulator

The new transient-pointer input mode works in the visionOS simulator, so you can test your Web XR experience without needing Apple Vision Pro. To get started:

  1. Download Xcode from the Mac App Store.
  2. Select the visionOS SDK for download and installation.
    Checklist of which platforms you would like to develop for — macOS and visionOS are checked, ready to press the download & install button.
  3. Launch Xcode to complete the installation process. This installs a new application named “Simulator”. Learn more about using the visionOS Simulator.
  4. Open the Simulator app on macOS.
  5. Create a new visionOS simulator or open an existing one.

If you have a website open in Safari on macOS, you can easily trigger that page to open in Safari in visionOS Simulator. On Mac, select Develop > Open Page With > Apple Vision Pro. (If the Develop menu is not present in the menu bar, enable features for web developers.)

Enabling WebXR support

Whether you have Apple Vision Pro or are using visionOS Simulator, you’ll want to turn on support for WebXR. From the Home View, go to Settings > Apps > Safari > Advanced > Feature Flags and enable WebXR Device API.

Identifying potential issues in existing WebXR scenes

WebXR is provided in visionOS 1.1 for testing purposes only. If you encounter bugs in the browser, please don’t attempt to target a user-agent string to work around them. Instead, report them to us via Feedback Assistant.

When hand-tracking is enabled I can’t select anything.

Your experience probably is only looking at events which correspond to the first two inputs in the inputSources array. Many examples in the popular framework Three.js only act on events related to inputs at index 0 and 1. When hand tracking is enabled, the inputs corresponding to the hand tracking data appear in entries 0 and 1 but the transient-pointer style inputs appear in entries 2 and 3.

When I attach an object to the controller it appears at the user’s face.

The input’s targetRaySpace originates between the user’s eyes and points in the direction of their gaze. To place an object in the user’s hand, instead attach objects to the input’s gripSpace position, which corresponds to the location of the user’s pinch.

The selection ray to interact with objects lags between the position of my current selection and my last one.

Some frameworks attempt to smooth out controller motion by interpolating the position of the controller each frame. This interpolated motion should be reset if a controller is disconnected and reconnected. You may need to file an issue with the framework author.

Let us know

We can’t wait to see what you make. To share your thoughts on WebXR, find us on Mastodon at @ada@mastodon.social, @jensimmons@front-end.social, @jondavis@mastodon.social, or on X at @zachernuk. Or send a reply on X to @webkit. You can also follow WebKit on LinkedIn. If you run into any issues, we welcome your feedback (to include a sysdiagnose from Apple Vision Pro), or your WebKit bug report about Web Inspector or web platform features. Filing issues really does make a difference. Thanks.

]]>
<![CDATA[Implementing Vertical Form Controls]]> https://webkit.org/?p=15190 2024-03-22T16:20:42Z 2024-03-18T15:00:35Z Safari 17.4 adds vertical writing mode support for form control elements across macOS, iOS, iPadOS, and visionOS.

all the form controls, typeset vertically

Setting written text vertically is commonly observed in East Asian languages. For example, Chinese, Japanese, and Korean (CJK) may be written vertically and read top-to-bottom, flowing in lines from right to left. Similarly, Traditional Mongolian is a vertical script that flows in lines from left to right.

Support for vertical text has been available in browsers for several years using the CSS writing-mode property. However, until recently, support for the vertical-lr and vertical-rl values for form controls was inconsistent among all browsers. Consequently, as part of Interop 2023, the industry committed to working towards vertical writing mode support in form controls. We are excited to see cross-browser support improve considerably. And we are proud that Safari 17.4 brings support for vertical writing modes to form controls everywhere you find Safari.

Using vertical writing modes with form controls

Adopting vertical form controls is as simple as adding a CSS declaration using the writing-mode property and applying it to your controls. For a right-to-left block flow direction, as observed in CJK languages, use writing-mode: vertical-rl. Or, for a left-to-right block flow direction, use writing-mode: vertical-lr.

button, textarea, progress, meter, input, select {
    writing-mode: vertical-rl;
}

Support for vertical writing mode is available for the following elements: <button>, <textarea>, <progress>, <meter>, <input>, and <select>. WebKit is committed to supporting vertical writing modes on any new controls moving forward, including <input type="checkbox" switch>, which was also added in Safari 17.4.

Note that in all browsers, any popup UIs associated with form controls, such as the menu for a <select> element, or the color picker for an <input type="color"> are still displayed horizontally.

Implementation

Adding vertical writing mode support for form control elements in WebKit was a large undertaking that involved over 10 unique types of controls. Below are some of the changes that were needed to support this feature in WebKit.

Style

WebKit’s user-agent stylesheet made frequent use of physical properties, such as width, height, and margin. In order to support vertical writing modes, this usage was updated to use logical properties, such as inline-size and block-size, where appropriate.

Layout

Form controls in WebKit make heavy use of custom layout code. While the interfaces were designed to support logical properties, the implementations often assumed a horizontal writing mode. We updated the methods to compute logical widths to ensure they considered the writing mode.

Additionally, custom baseline adjustment logic, which makes controls such as checkboxes and radio buttons look great alongside text, was updated to use the central baseline for vertical writing modes. This ensures that controls continue to look great alongside text in a vertical writing mode.

Rendering

Rendering form controls with a vertical writing mode required unique changes depending on the control and system capabilities.

On macOS, WebKit’s form controls match the look and feel of the operating system. However, macOS itself does not support vertical writing modes. Consequently, some controls, such as <progress>, are simply rotated after obtaining the system image in order to support vertical rendering.

two progress bars, laid out horizontally and vertically

Other controls cannot simply be rotated due to details such as shadows, for example <select>, so we used a custom fallback rendering approach.

the select menu typeset vertically

If a control was already custom painted in WebKit, it was updated to use logical coordinates rather than physical coordinates. We used this approach throughout the iOS rendering code and it was also necessary in our rewrite of the listbox (<select multiple>) on macOS.

the select multiple form control, typeset vertically

Finally, some controls, such as checkbox and radio buttons, did not require any rendering changes. As “glyph-like” controls, they look the same in all writing modes.

checkboxes and radio buttons on iOS typeset vertically

WebKit remains committed to improving internationalization support in our engine. By adding vertical writing mode support for form control elements in Safari 17.4, we hope to empower authors to create the best content for their local and global communities.

A special thanks goes to Tim Nguyen, who kicked off the vertical form controls project in WebKit, and Alan Baradlay, whose deep knowledge of CSS layout proved invaluable in driving the project to the finish line.

Feedback

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

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

]]>