💥 Discover this insightful post from Hacker News 📖
📂 **Category**:
✅ **What You’ll Learn**:
align presentational attribute, fixing button placement on bricklink.com. (#9177)
| Before | After |
|---|---|
![]() |
![]() |
stroke-dasharray interpolation : SVG dashes finally animate smoothly. (#9133)
autofocus : Elements with the autofocus attribute actually receive focus on page load now. (#9016)
List markers in RTL text : Bullets now sit on the right side of right-to-left text, fixing list rendering on Arabic Wikipedia. (#9099)
| Before | After |
|---|---|
![]() |
![]() |
Inline flex/grid baselines : An inline flex or grid container now derives its baseline from its child’s first line box, not its last wrapped line. Fixes link text and icon alignment on nos.nl. (#9183)
| Before | After |
|---|---|
![]() |
![]() |
Networking
getaddrinfo no longer blocks the event loop. LibDNS now runs lookups on a thread pool, fires A and AAAA queries in parallel (RFC 8305-ish), and coalesces concurrent lookups for the same name. RequestServer’s preconnect path was sneaking past our resolver and letting libcurl spawn its own threaded resolver that would pthread_join us on the main thread; that’s now routed through the same DNS pool. (#9109)
Profile of loading x.com when DNS is slow, before and after:


Over in RequestServer, draining queued response data was O(n²) when WebContent was slower than the network. RequestServer was spending ~30 seconds in memcpy and 3 seconds in Vector::remove while opening a YouTube video! Switching AllocatingMemoryStream to a singly-linked chunk list made consumption O(1). (#9028)
We now advertise AVIF and WebP in our Accept header for image requests, matching other engines. Some CDNs use the Accept header to decide whether to serve modern formats or fall back to JPEG. (#9046)
Style invalidation
Selector invalidation used to be straightforward: selectors always looked downward. :host ruined that. :has() made it way worse. Any descendant change can now force you to walk up the tree finding ancestors whose :has() arguments just flipped, and a lot of this month’s invalidation work is about making that walk less wasteful.
Four big wins this month:
- Reddit rule cache rebuilds: 13.2s → 3.2s. Stylesheet mutations no longer rebuild every style scope’s cache when only one scope changed. (#9138)
- Reddit infinite scroll: 11% fewer pointless recomputes. Sibling structural invalidation stopped fanning out to descendants that don’t observe the position. (#9155)
-
:has()mutation invalidation skips unaffected anchors , with substantial reductions measured on azure.com. (#9168) -
:has()child-list visits on the Intel ISA PDF: 71k → 1.6k. Coalesced when pending data already covers every concrete feature bucket the scope cares about, saving ~650ms on the pdf.js load. (#9179)
A large new structural-invalidation test battery exposed and fixed several invalidation holes (#9095), and a string of smaller tightenings landed around hover, stylesheet mutation scope, custom-property maps, and computed-style diffing (#9077, #9049, #9079, #9080, #9141).
Linux GPU painting via dmabuf
On Linux Vulkan builds, GPU-backed painting was being secretly undone every frame: WebContent painted into a GPU-backed Skia surface, but the buffer it shared with the UI process was a CPU bitmap, which forced a full GPU-to-CPU readback on every flush. SharedImage can now carry a Linux dmabuf handle, so the front and back buffers stay GPU-resident the whole way to the UI process. (#8917, #8920)
mimalloc as the main allocator
Our C++ and Rust code now share a single allocator instance, mimalloc v2, instead of each going through the system allocator separately (#8752). We don’t override malloc() system-wide, so third-party libraries keep their own allocator contracts. JS benchmarks improved across the board.
Sites that work better
The biggest visible wins this month are on Reddit and YouTube.
Reddit image gallery carousels actually work now, after fixing two unrelated layout bugs around ::slotted() matching and absolutely positioned descendants of split inlines (#9148). And thanks to TextDecoderStream, the SPA stops swallowing link clicks, so you can finally open the comments! Infinite scroll also benefits from the structural-invalidation work covered above.
YouTube benefits from a stack of unrelated improvements: off-thread top-level JS compile, off-thread WOFF2 decompression (saves ~170ms on Gmail too, #8976), reduced @font-face fetch fanout (177 → ~9 fetches on initial load, #9032), the RequestServer memory churn fix, and zero-copy TransferArrayBuffer.
A handful of smaller fixes:
Web Platform Tests (WPT)
Our WPT score went from 2,003,537 to 2,067,263 this month, a headline gain of 63,726 subtests. There’s an asterisk on that number: WPT imported test262, the official ECMAScript conformance suite, upstream this month, which added 53,207 JavaScript subtests to the count. We pass 52,045 of them (a 97.8% pass rate), since we’ve been running test262 independently for years and LibJS conformance is in great shape. So roughly 52k of the 63.7k gain is from the import, and the remaining ~11.7k is genuine new browser-platform progress, in the same ballpark as January’s 13,690.
The upside of the import is that WPT now actually measures JavaScript conformance alongside the rest of the platform, which is the way it should have been all along.
Other notable changes
- Rust is now mandatory : The
ENABLE_RUSTbuild option is gone (#8742), and the GN build system was removed entirely, leaving CMake as the single source of truth (#8931). - Stack-zeroing for the GC’s benefit : We now compile with
-ftrivial-auto-var-init=zero, which overwrites stale GC pointers on function entry so our conservative stack scanner finds fewer of them. (#9171) - Selecting through ligatures : Selection and hit testing on text with ligatures used to assume one glyph per code unit, so trying to highlight half of a
ffiselected either all or none. We now walk grapheme clusters and split each glyph’s advance across the graphemes it covers. (#8829) - Layout state slimming : Rarely-used
UsedValuesproperties moved behind a lazy pointer, dropping the struct from 424 to 176 bytes and cuttingLayoutState::populate_node_from()from 139ms to 65ms while loading sainsburys.co.uk. (#9104) - Targeted ShadowRoot layout invalidation : Setting
innerHTMLon a shadow root no longer invalidates the entire document’s layout tree. Reduces layout-and-paint time on pomax.github.io/bezierinfo by 21%. (#9191) - Fetch body chunks straight into the stream : Fetched bytes used to be delivered through a pull-promise dance that allocated seven GC objects per chunk and did nothing useful. They now go directly into the byte stream controller. (#9169)
- Cross-site popup navigation : Navigating a popup tab to a different site no longer kills the parent’s WebContent process. (#8730)
-
Ctrl+Tabfor tab navigation : On the Qt UI,Ctrl+TabandCtrl+Shift+Tabcycle through open tabs. (#8704) - Middle-mouse autoscroll : Hold the middle mouse button and drag to scroll, or click in place to enter autoscroll mode. (#8881, #8928)
- Address-bar error page : When you type text into the address bar that can’t be sanitized into a URL or search query (for instance, with search disabled), you now get a proper error page instead of the input being silently dropped. (#9072)
-
TextDecoderStream: The streaming counterpart toTextDecoderis now implemented, including the partial-UTF-8 hold-back across chunk boundaries that makes the Reddit comments fix above possible. (#9143) - Cross-process
BroadcastChannel: Messages now route over IPC between WebContent and WebWorker processes, so aBroadcastChannelworks the same way it does in other browsers regardless of which process the listener is in. (#8865)
Credits
We’d like to give a special shout-out to the 7 people who made their first code contribution this month:
- CalebC48. Made the Qt UI open the new tab page when “New Window” is invoked without a URL (#8864)
- Darshanx256
- Preserved copy flags when recursing into directories in LibFileSystem (#8834)
- Updated documentation to use the
ReleaseCMake preset (#8814)
- j-stechmann. Added
Ctrl+TabandCtrl+Shift+Tabfor tab navigation in the Qt UI (#8704) - James Raspass. Switched the settings dialogs over to native light dismiss (#8808)
- jarusll. Added support for multiple
--debug-processarguments to LibWebView (#8841) - slydetector. Various devcontainer fixes to get things working again (#9149)
- Yayoi-cs. Reported and fixed a use-after-free in TypedArray views over shared
WebAssembly.Memory(GHSA-w89h-j2xg-c457)
And of course we’d like to thank everyone who contributed code this month:
Ali Mohammad Pur, Aliaksandr Kalenik, Andreas Kling, Andrew Kaster, ayeteadoe, CalebC48, Callum Law, Christian Frey, Darshanx256, Glenn Skrzypczak, InvalidUsernameException, James Raspass, Jelle Raaijmakers, Johan Dahlin, Jonathan Gamble, Jonathan (j-stechmann), jarusll, Luke Wilde, mikiubo, Nicolas Danelon, Ollie Hensman-Crook, Pavel Shliak, Psychpsyo, R-Goc, RubenKelevra, Sam Atkins, Shannon Booth, slydetector, Suraj Yadav, Tete17, Tim Ledbetter, Timothy Flynn, Undefine, Yayoi-cs, Zaggy1024
⚡ **What’s your take?**
Share your thoughts in the comments below!
#️⃣ **#Month #Ladybird #April**
🕒 **Posted on**: 1777757193
🌟 **Want more?** Click here for more info! 🌟






