This Month in Ladybird: September 2025
September wrapped up with 207 merged PRs from 40 contributors. Let’s take a look at some highlights:
Welcoming new sponsors
Ladybird is entirely funded by the generous support of companies and individuals who believe in the open web. This month, we're excited to welcome the following new sponsors:
- Cloudflare with $100,000 (their announcement)
- K15t with $5,000
- Unkey with $5,000
We're incredibly grateful for their support. If you're interested in sponsoring the project, please contact us.
Web Platform Tests (WPT)
Progress on WPT continues. This month we squeezed out 13,405 new passes for a total of 1,853,367.
For context, here are the current top 6 browser engines and their WPT scores today vs one month ago.
HTTP caching enabled by default
An HTTP cache has been in the works for a while, but until now it was hidden behind a flag. This month we enabled it by default, making repeated navigations much faster. (PR #6119)
CSS custom property handling
Ladybird has supported CSS custom properties (also known as "variables") for a while, but there were lots of little gaps. This month we fixed !important
handling (PR #6329), exposed them in CSSStyleProperties
(PR #5989), ensured the correct order is preserved (PR #6226), and added them to the properties list in dev tools (PR #6311).
DevTools improvements
We also fixed some issues where layout information would not be displayed (PR #6312), and where the style sheets list would sometimes not appear (PR #6315).
CSS Typed OM API
Carrying on from last month, we now implement the various CSSStyleValue classes for transform functions, which is finally enough for most of the WPT tests to run instead of crashing immediately. There's still a lot to do, but it's been nice to finally see some results! (PRs #6162, #6176, and #6227)
View transitions
View transitions are a way of animating between page states (for example, from a product list to the details for one product), or even between different pages. This month, a lot of the underlying machinery for this has been implemented (PR #5155). It's going to take a bit longer before it's usable, but it's a good start to an exciting new web feature.
Trusted Types
Carrying on the initial work from last month, we now support the following features of Trusted Types:
- TrustedTypePolicyFactory#emptyHTML
- TrustedTypePolicyFactory#emptyScript
- require-trusted-types-for
- Trusted Types support in
HTMLScriptElement
There is still ongoing work to accommodate Trusted Types in other areas, such as the DOM APIs. (PRs #5828, #6057, #6058)
Global Privacy Control
Global Privacy Control (GPC) notifies websites that they are not permitted to sell or share information about your activity. Unlike the old Do Not Track header, GPC is backed by legislation requiring websites to respect it. This month, we've replaced our DNT implementation with GPC (PR #6175), and it can be enabled in the settings page.
Generating application menus
Ladybird currently has two UI frontends: AppKit on macOS, and Qt6 on other systems. The biggest remaining duplication between them was around menus. This month we introduced shared menu definitions in LibWebView, from which each frontend now generates its menus (PR #6062, PR #6221). This makes maintaining our UIs simpler, and developers can now define a menu item once and have it appear in both.
Improved selection handling
Copying selected text often gave the wrong result, due to how whitespace collapsing shifted offsets. This also caused our Range offsets to be wrong. We now better map the original text offsets to what you see on screen. (PR #6169)
::slotted() pseudo-element support
We added support for the ::slotted()
pseudo-element, which allows styling of elements passed into shadow DOM slots.
Animation compositing
This month we added support for animation compositing via the animation-composition
property (PR #6233) and the KeyframeEffect.composite
API (PR #6213). These features let authors control how animations are combined when multiple animations affect the same property.
Stable setTimeout() firing order
When many setTimeout(..., 1)
calls arrive in quick succession, several timers can share the same deadline. Previously, those ties were resolved arbitrarily. We now assign each timer a sequence ID and break ties by (deadline, sequence_id), ensuring equal-deadline timers fire in the order they were registered. (PR #6260)
HarfBuzz text shaping cache
One of the most common tasks in any browser engine is measuring text. We delegate this work to HarfBuzz, but shaping text is complex and can be slow. We now cache shaping results to avoid recalculating them, which drastically improves performance on text-heavy pages like Wikipedia and news sites. (PR #6254)
Credits
We'd like to thank everyone who contributed code this month:
Ali Mohammad Pur, Aliaksandr Kalenik, Andreas Kling, ayeteadoe, Callum Law, euro20179, Feng Yu, InvalidUsernameException, Jan Koudijs, Jelle Raaijmakers, Julian Dominguez-Schatz, Kemal Zebari, Kenneth Myhra, Lorenz A, Louis Dalibard, luizgfc, Luke Wilde, Marcos Del Sol Vives, me-it-is, MFMF-EGY, mikiubo, Niccolo Antonelli Dziri, Nico Weber, Pascal Pomper, Pavel Shliak, Pratyush Nair, Psychpsyo, R-Goc, Rocco Corsi, Sam Atkins, Shannon Booth, Shivendra Kumar, Tete17, Tim Ledbetter, Timothy Flynn, Veeti Paananen, zac, Zaggy1024