Deze maand in Ladybird — augustus 2026

Nieuwe sponsors verwelkomen

Ladybird wordt volledig gefinancierd door de gulle steun van bedrijven en individuen die geloven in het open web. Deze maand verwelkomen we met trots de volgende nieuwe sponsor:

  • Arnav Guleria met $5.000

Je kunt iedereen die het project momenteel ondersteunt vinden op onze sponsorspagina. We zijn hen allemaal zeer dankbaar. Als je geïnteresseerd bent in het sponsoren van het project, neem dan contact met ons op.

Video op Twitch en meer formaten op YouTube

Onze implementatie van Media Source Extensions ondersteunt nu fragmented MP4, evenals de AVC (H.264), HEVC (H.265), AV1 en AAC-codecs. Ondersteuningsvragen communiceren nu onze werkelijke mogelijkheden (#11251). Hierdoor kunnen we adaptive-bitrate video afspelen op veel meer sites, waaronder Twitch en Plex. YouTube werkte al in Ladybird omdat alle video's beschikbaar zijn als VP9/Opus in WebM, maar sommige oudere of minder populaire video's hebben meerdere kwaliteitsniveaus alleen in AVC, en hogere kwaliteiten (vooral 8K) worden vaak alleen als AV1 aangeboden.

We hebben ook een reeks afspeelfouten opgelost:

  • Het hoveren over de video-previewkaarten van YouTube veroorzaakt geen crash meer (#10992).
  • Players die preload="none" gebruiken, lopen niet meer vast (#11454).
  • Op HLS.js gebaseerde players starten nu correct met afspelen (#11480).

CSS scroll snap

Scroll snap is geïmplementeerd (#10944): scroll-snap-type, scroll-snap-align en scroll-snap-stop. Het wiel, toetsenbord, slepen met de scrollbalk, touchpad flings en programmatische scrollTo() maken nu gebruik van snapping, en inhoud herstelt de snap-positie wanneer de layout verandert. (scrollIntoView() ondersteunt dit nog niet.) Carrousels en full-page scrollers zijn hierop gebouwd.

JavaScript-debugging in DevTools

Je kunt nu JavaScript debuggen in Ladybird: breakpoints zetten, regel voor regel door de code stappen en watch expressions evalueren (#11128). Dit is gebouwd op de nieuwe breakpoint-ondersteuning in onze JavaScript-engine (#10936). Wasm-debugging en DOM-mutatie-breakpoints ontbreken nog, maar de kernworkflow werkt. (Zoals eerder vermeld, is de Firefox DevTools-frontend een tijdelijke oplossing totdat we onze eigen versie bouwen.)

Downloads pauzeren en hervatten

Downloads kunnen nu worden gepauzeerd en hervat, en blijven behouden na een browserherstart, mits de server dit ondersteunt (#10932). Daarnaast downloaden we, waar mogelijk, via vier parallelle verbindingen.

Sessierestauratie

Het opnieuw openen van een gesloten tabblad met Ctrl+Shift+T herstelde voorheen alleen de URL. Nu wordt de volledige sessiegeschiedenis van het tabblad hersteld, inclusief de terug/vooruit-status, en dit blijft behouden na het herstarten van de browser (#11178). "Tabblad dupliceren" kopieert nu ook de sessiegeschiedenis (#11286). We bewaren bewust nog geen gegevens van formulierverzendingen; dat vereist eerst een zorgvuldige behandeling.

Browser UI

  • mailto-links: Deze openen nu je mailclient. Andere externe URL-schema's vragen om bevestiging, met bescherming tegen gevaarlijke schema's en recursieve lanceringen (#10846).
  • JavaScript-dialogen: alert(), confirm() en prompt() blokkeren niet langer de hele browser. In de Qt UI zijn dit nu in-page overlays in plaats van modale systeemdialogen. Hierdoor kan een while (true) alert("lol") je niet meer buitensluiten; je kunt tabbladen wisselen of het tabblad simpelweg sluiten (#11264).
  • Crash-herstel: Voegt geen nep-pagina's meer toe aan je sessiegeschiedenis; je krijgt een crash-scherm en je werkelijke geschiedenisvermelding blijft behouden. Een reload start precies waar de crash de actie onderbrak (#11402).
  • URL-balk: Het typen van localhost:8000 of 127.0.0.1 in de URL-balk navigeert nu via http in plaats van dat het faalt (#11007).
  • Wayland-fixes: De omnibox blijft op zijn plek staan (#10862, #11149), Tab-cycli navigeren correct door de focus van de pagina (#10868), en het slepen van de scrollbalk buiten het venster laat de muisrelease niet langer vallen (dit is voor alle Linux-systemen opgelost, #11343).

Regels voor sitecompatibiliteit

Sommige sites leveren defecte inhoud aan Ladybird op basis van User-Agent sniffing. We lossen dit nu op met declaratieve regels voor sitecompatibiliteit die tijdens runtime worden geladen, in plaats van per site hardgecodeerde regels in de browser (#11327). De regels zijn eenvoudige JSON-bestanden, zodat iedereen kan zien wat we voor welke site doen. De eerste gebruikers zijn nytimes.com en cnn.com, die beide correct renderen wanneer we "Ladybird" verbergen in de User-Agent string (#11333).

Sites die beter werken

  • chatgpt.com: Een crash op de inlogpagina en drie layout-fixes zijn opgelost (#11398).
  • vscode.dev: Crashte voorheen seconden na het openen van een nieuw bestand. Hit testing las pointer-events van het verkeerde element, wat werd getriggerd door de empty-editor hint van VS Code (#11342).
  • icloud.com: Sites die hun karaktercodering pas na meer dan 1KB in de pagina declareren, renderen niet langer als mojibake (#11219).
  • strava.com: Twee aparte fixes hebben de laadtijd van de activiteitskaart ongeveer gehalveerd (#11133, #11114). Grafiek-popups volgen nu de muis dankzij de implementatie van svg.getScreenCTM() (#11177). Bovendien groeit een activiteitspagina die voorheen doorging tot het geheugen vol was niet meer; het geheugengebruik daalde van 17,8 GiB naar 61 MiB (#11055).
  • outlook.com en Word: Een crash in Outlook is opgelost en het inloggen bij Word werkt nu (#11216).
  • milliondollarhomepage.com en xkcd: <area> image-map links zijn nu klikbaar (#10863).

CSS

  • 3D transformaties: Een reeks fixes heeft grote gaten in de 3D-rendering gevuld: flattening bij transform-style: flat grenzen (#11009), backface-visibility (#10960), en correcte dieptesortering van snijdende 3D-vlakken met behulp van een BSP-tree (#11045). De dieptesortering is getest tegen cssDOOM (een DOOM-renderer gebouwd uit pure CSS 3D transformaties): muren worden nu in de juiste volgorde getekend en oppervlakken flikkeren niet meer (#11466).
  • overflow-wrap: Lange, onbreekbare woorden worden nu in noodsituaties gebroken op grafeemgrenzen in plaats van dat ze buiten hun container vallen (RTL-tekst ondersteunt dit nog niet) (#11416). Dit is merkbaar beter op Google Translate.
  • Right-to-left (RTL) scroll containers: Deze scrollen nu in de juiste richting; voorheen was sommige RTL-inhoud onbereikbaar (#11023).
  • Tekstdecoraties: Deze worden nu getekend vanuit de decorerende box met de bijbehorende kleur, dikte en lettertype-geometrie. Dit loste de onvoorwaardelijke onderstrepingen op BBC Weather op (#10744).

Prestaties serieus nemen

We draaien continu browser-benchmarks tegen de master branch en volgen de resultaten op linegodown.lol. In augustus steeg onze Speedometer 2 score van ongeveer 47 naar ongeveer 64, Speedometer 3 van ongeveer 2,5 naar ongeveer 3,9, en StyleBench van ongeveer 3,5 naar ongeveer 83. Dit zijn nog bescheiden cijfers vergeleken met volwassen engines, maar de vooruitgang is reëel.

Een deel van deze push is systematisch: we benchmarken individuele operaties tegen andere browsers, zoeken waar we drastisch langzamer zijn en onderzoeken waarom. Een DOM-operatie die 100x langzamer is dan die van Chromium, is meestal niet om een diepgaande reden langzamer; er is simpelweg iets mis. Het vinden hiervan helpt echte pagina's. Een maand van dit proces heeft veel van deze ratio's met een factor tien verlaagd:

  • Canvas fillStyle parsing ging van ongeveer 90x langzamer dan Chromium naar ongeveer 2x (#11300).
  • replaceChildren() ging van 13x naar minder dan 4x (#11306).
  • Lijst-insertie en -verwijdering ging van meer dan 120x naar minder dan 20x (#11389).

De JavaScript-engine kreeg een reeks fast paths voor eigenschapsacces (#11175), spread (#11383), promise combinators (#11374) en JSON.stringify, die nu binnen ongeveer 15% van V8 zit in onze benchmark (#11329). De meest bevredigende fix: arrays die tijdelijk sparse werden, bleven voorheen eeuwig in langzame hash-table opslag staan. Na 30 regels code verbeterde Minecraft4k van 10,2 naar 14,5 FPS (#11423).

Een nieuwe style-engine

We hebben onze selector matching, style invalidation, cascade en computed-style retention vervangen door een nieuwe engine (#11088). We willen dat stijlwerk proportioneel is aan wat er is veranderd, niet aan wat er mogelijk is veranderd.

Historisch gezien markeren browser-engines wanneer iets verandert alles wat beïnvloed zou kunnen worden, om dit vervolgens opnieuw te matchen en te cascaderen. Moderne engines beperken die set met technieken zoals invalidation sets en style sharing, maar het patroon blijft hetzelfde: het openen van een menu kan nog steeds duizenden elementen ongeldig maken om de stijl van een paar tientallen te veranderen. De nieuwe engine behandelt DOM-mutaties, statuswijzigingen en CSSOM-bewerkingen als een stroom van getypeerde deltas, routeert elke delta naar de selectors die er afhankelijk van zijn, en stopt zodra een output ongewijzigd blijft. In database-termen: selectors zijn standing queries, en computed styles zijn materialized views die incrementeel up-to-date worden gehouden.

Correctheid is het moeilijkste deel. Een eenvoudige evaluator zonder cache dient als referentie-implementatie, en elk fast path wordt hieraan getoetst. De inputstroom van de engine kan ook worden opgenomen uit een echte browsersessie en deterministisch worden afgespeeld. Tijdens de ontwikkeling hebben we opnames van echte sites afgespeeld (met miljoenen events per stuk) en elke afwijking in output als een fout behandeld.

Op veel workloads is de nieuwe engine nog langzamer dan de oude aanpak, en er is nog veel tuning nodig. Maar op StyleBench, dat precies dit soort incrementele restyling meet, zitten we nu in dezelfde range als de grote engines.

Layout-resultaten cachen

Tot deze maand betekende bijna elke wijziging dat het hele document opnieuw moest worden opgebouwd, vanaf de viewport naar beneden. We hadden in enkele gevallen partiële relayout bij position: absolute en SVG, maar dat was alles. Volledige relayout is de makkelijkste manier om correctheid te garanderen in een jonge engine, maar op grote pagina's wordt veel werk herhaald dat elke keer hetzelfde resultaat geeft.

Layout-resultaten worden nu gecachet en hergebruikt op basis van hun input-beperkingen (#11113). Een formatting context run is gekoppeld aan alles wat de parent hem meegeeft: het type context, de sizing-beperkingen en de status van de root. Wanneer er niets in een subtree is veranderd, wordt de hele run uit de cache afgespeeld, inclusief geneste runs. Omdat fragment-offsets relatief zijn aan het containing block, kan een gecachte subtree op een nieuwe positie worden afgespeeld. Een shadow mode voert bij elke cache-hit de echte layout uit en geeft een foutmelding bij elke afwijking.

Het overige werk ging naar het verhogen van de kans op cache-hits. De cache was oorspronkelijk gekoppeld aan elke block-axis input, waardoor een ancestor die zijn hoogte bepaalde alle gecachte resultaten daaronder weggooide, ook al kijken de meeste elementen nooit naar die waarden. Op geneste flex-pagina's kon één leaf-element wel 29 keer in één layout worden gemeten. We registreren nu of een box die inputs daadwerkelijk observeert en negeert de inputs die hij niet gebruikt (#11346). Daarnaast stopten DOM-mutaties met het volledig herbouwen van layout-subtrees, ondersteund door tests die incrementeel bijgewerkte bomen vergelijken met rebuilds vanaf nul (#11126).

Samen met de nieuwe style-engine is dit waar het meeste van de bovengenoemde benchmark-verbeteringen vandaan komt.

CSS-animaties buiten de hoofdthread uitvoeren

Een actieve CSS-animatie wekte voorheen de hoofdthread 60Hz per seconde wakker om de stijl opnieuw te berekenen, de paint-status herbouwen en een nieuwe display-lijst te registreren. Hierdoor concurreerde de animatie met JavaScript om dezelfde thread, ondanks dat de volledige tijdlijn van de animatie bekend is via de keyframes.

Nu worden geschikte opacity- en transform-animaties één keer beschreven (met hun opgeloste keyframes, timing en easing) en overgedragen aan de compositor, die ze onafhankelijk van de eigen vsync voortstuwt (#11470). De hoofdthread is niet betrokken terwijl de animatie loopt. Wanneer een script via getComputedStyle, currentTime of playState vraagt naar een animatie, samplen we dit on-demand, zodat de antwoorden nooit verouderd zijn. Een eindige animatie krijgt precies één wake-up timer aan het einde om animationend te triggeren.

Animaties blijven nu vloeiend, zelfs als de hoofdthread druk is met het uitvoeren van JavaScript, en een pagina vol animaties zou minder stroom moeten verbruiken. Aangepaste eigenschappen (custom properties) binnen keyframes animeren nu ook correct (#11477).

Meer van LibWeb in Rust

In de nieuwsbrief van juli stond dat style en layout naar Rust verhuisden. In augustus is Rust de enige eigenaar geworden van diverse andere onderdelen:

  • CSS parsing: Is overgezet (#11318, #11334, #11240, #11360). Elke grammatica-familie werd gelanceerd in een A/B-modus die zowel de Rust- als de C++-parsers draaide en de output vergeleek over de volledige testsuite. C++ werd pas verwijderd bij nul mismatches. De Rust-versie is bovendien sneller dan de C++-versie die hij verving.
  • Computed style storage en computation: Zijn nu eigendom van Rust; volledige stijl-updates blijven nu in de Rust-pipeline zonder terug te grijpen naar C++ (#11155).
  • Painting pipeline: Inclusief display list recording en hit testing is geporteerd (#11222), en de oude C++ paintable objects zijn verwijderd (#11273).
  • Strings: Delen nu één representatie tussen de twee talen, zodat tekst de grens oversteekt zonder kopieerkosten (#11278).

Een speciale WebAssembly-compilerdienst

Wasm-compilatie draaide al buiten het paginaproces, maar elk paginaproces maakte zijn eigen compiler via forking. Deze forking botste met sandbox-beperkingen, waardoor we op veel systemen stilzwijgend terugvielen op de interpreter. Het browserproces start nu één enkele WasmCompiler-service met zijn eigen minimale sandbox. Paginaprocessen bereiken deze via IPC en verliezen volledig hun permissie om te forken (#11133). Dit heeft de sandbox verscherpt en tegelijkertijd de snelheid verhoogd: dit is de reden waarom Strava-kaarten bij een eerste bezoek nu ongeveer in de helft van de tijd laden.

JavaScript-waarden 'cagen' (isoleren)

Vorige maand hebben we de inhoud van ArrayBuffer en Wasm-geheugens gecaged. Deze maand hebben we caging toegevoegd voor celpointers die zijn opgeslagen in NaN-boxed JavaScript-waarden. Dit beperkt waar een gecorrumpeerde waarde naar kan wijzen; het caget de JavaScript-heap echter niet volledig. Voorheen was het corrumperen van één NaN-boxed waarde in de GC-heap voldoende om de engine naar willekeurig procesgeheugen te laten wijzen. Nu leven alle GC-heap blokken in één gereserveerde regio, en cel-payloads worden opgeslagen als offsets daarin. Bij decodering worden deze gemaskeerd, zodat een vervalste waarde alleen binnen die regio kan landen (#11086, #11316).

In de gegenereerde code van de interpreter is de cage base vastgezet in een CPU-register, waardoor het unboxen van een waarde bestaat uit een maskering en een optelling, zonder geheugenladingen. Vers gegenereerde bytecode wordt nu ook opgeslagen in read-only mappings, zodat heap-corruptie de instructiestromen niet direct kan herschrijven.

Netwerken

  • Onvolledige certificaatketens: Deze zorgen er niet langer voor dat sites op Linux niet werken; we halen ontbrekende tussenliggende certificaten op zoals andere browsers dat doen, waarbij ketens nog steeds moeten valideren naar een vertrouwde root (#10661).
  • Permanente redirects: Deze worden nu gecachet, waardoor een herbezoek aan een site die een 301-redirect geeft, de redirect-ronde bespaart (#11460).
  • Reload valideert nu echt: Het herladen van een gecachte pagina nam voorheen geen contact op met de server, zelfs niet als de server down was. Het onderzoek hiernaar bracht ook een bug in de HTML-specificatie aan het licht (#11004, whatwg/html#12760).

Web Platform Tests (WPT)

Onze WPT-score steeg deze maand van 2.079.020 naar 2.088.677, een winst van 9.657 subtests. Ter vergelijking: de winst in juli was 108.

De grootste winst kwam van ongeveer 2.500 referrer-policy subtests die behandelen hoe de referrer wordt doorgegeven via navigaties, iframes en srcdoc-documenten. Deze begonnen aan het einde van de maand te slagen; we passeren nu 99% van die suite. De rest volgt het werk aan functies van deze maand: 3D-transformaties, scroll snap en Media Source Extensions zijn allemaal zichtbaar in het verschil.

Andere opmerkelijke wijzigingen

  • WebGL: Werkt nu op Windows via de Direct3D 11-backend van ANGLE. Voorheen was dit volledig uitgeschakeld in de compilatie (#11312).
  • JPEG XL decodering: Is overgeschakeld naar jxl-rs, de Rust-decoder die ook door Chrome en Firefox wordt overgenomen voor hun aangekondigde JPEG XL-ondersteuning (#11386).
  • Verborgen vensters: Vensters die verborgen of occludeerd zijn, stoppen met werken: geen rasterisatie, geen smooth-scrolling ticks en geen frame presents voor pagina's die je niet kunt zien (#11365). Samen met de off-main-thread animaties zou dit de batterijduur moeten verbeteren.
  • Sessiegeschiedenis en navigatie: Deze zijn verplaatst naar het browserproces (#11116, #11270), een vereiste voor site isolation.
  • DOM scheiding: De DOM is gesplitst van zijn JavaScript-wrappers (#9973). Een DOM-node draagt nu geen JavaScript-objectstatus meer totdat een script dit daadwerkelijk aanraakt, wat het geheugengebruik vermindert. Dit maakt het ook mogelijk om per "world" aparte wrappers te hebben (de isolatie die content-scripts van browserextensies vereisen), wat een stap is richting ondersteuning voor extensies.
  • Systeemlettertypen: Deze worden nu één keer ontdekt en als een read-only catalogus doorgegeven aan helperprocessen, zodat deze processen geen sandbox-toegang meer nodig hebben tot lettertypmappen (#11465).
  • Plakken: Plakken via het contextmenu of de middelste muisknop activeert nu het paste event, zodat verificatiecode-formulieren die een geplakte code over meerdere velden verdelen, werken ongeacht hoe je plakt (#11120).

De weg naar de alpha

We hebben aangegeven dat de eerste alpha start in 2026, en dat is nog steeds het plan. Wat er nog overblijft voordat dit kan beginnen, is grotendeels geen engine-werk: crash-rapportage die niet vereist dat je vanuit een terminal start, ondertekende installeerbare builds voor macOS en Linux, een auto-update mechanisme zodat alpha-gebruikers verbeteringen direct ontvangen, een eenvoudige first-run experience (inclusief optionele adblock-filterlijsten zoals EasyList die automatisch updaten), en een plek om het te downloaden. De engine zal niet af zijn wanneer de alpha start. Dat is juist waar de alpha voor is!

Dat is alles voor augustus. Bedankt voor het lezen, en tot volgende maand!