Engine gaps, scrollbar passthrough, and the taffy track-sizing hang fix - #42
Merged
Conversation
added 5 commits
August 16, 2026 16:17
`blitz-paint/scrollbars` paints the thumb and `blitz-dom/scrollbars` tracks drag and hover, but nothing above them could ask for either: `blitz-shell` passed through `svg` and not this, so an embedder had no route to it. The cost of that is quiet. An app whose CSS sets `scrollbar-width` or `scrollbar-color` - both of which `blitz-dom` implements - gets neither the styling nor a thumb, and nothing reports that the properties went nowhere. Still off by default: a shell that draws no scrollbars should not pay for the code.
Same gap as scrollbars: the features exist in `blitz-dom` and `blitz-paint`
and nothing above them could ask.
shadow-dom attachment and slot distribution. chuzz needs it - a page using
custom elements with a shadow root gets no distribution without
it.
floats CSS `float`, which taffy leaves off by default, so a floated
element lays out as a block and nothing says why.
font-embolden / apple-font-embolden
synthetic bold for faces with no bold cut. The apple variant
matches CoreText's own weighting and is the one for macOS.
`data-uri` was already exposed. `complex-scripts` is deliberately left off:
it is Arabic and Indic shaping, and no consumer here needs it.
I described this as Arabic and Indic shaping and left it off. The flag's own comment says what it is: dictionary-based line breaking, which is Thai, Khmer, Lao and Burmese. Those scripts put no spaces between words, so finding a break point takes a dictionary rather than a space scan, and without it the text does not wrap or wraps mid-word. An app that already ships a Chinese locale is one that has readers in that part of the world, so this is not a hypothetical.
`fetch_async` returns `(String, Bytes)`, the final URL and the body, and throws away what the server said the bytes were. An embedder loading a WebAssembly module cannot check `Content-Type` before handing the bytes to a parser that will report an offset into a file that is not a module at all. The headers already existed: `fetch_http` read them off the response and dropped them on the way out. `fetch_response_async` returns the `FetchResponse` the fetch path already has: url, status, headers, body. Additive rather than a widening of `fetch_async`, on purpose. Changing that return type touches every caller in every embedder for a need only some of them have, and `HeaderMap` is a heap-allocated multimap the hot path would then build and clone for every subresource on the page. Existing callers keep the cheap shape and allocate nothing new. `fetch_http_response` is a sibling of `fetch_http` rather than a wrapper: that one consumes the response to reach the body and so cannot hand back what it read on the way. The per-host permit, the user agent and the non-2xx handling are duplicated between them and have to move together. `data:` states its own mime type, so it reports a real `Content-Type`. `file:` has no server and reports none, which a caller must read as unknown rather than as a mismatch. Tested for all three schemes without a network. Checked by mutation: dropping the data-URL header insert fails the first test.
ps-taffy 4355471d ends an infinite loop in `distribute_space_up_to_limits`, where f32 rounding could leave a track counted as growable while its remaining headroom computed as exactly zero. Every subsequent iteration then recomputed the same values and the loop never returned. Blitz reaches it through table column sizing, so a document containing a table whose column contributions land a rounding step apart would wedge layout and pin whichever thread called `resolve_layout` at 100% CPU.
This was referenced Aug 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacks the outstanding engine work onto master and takes the ps-taffy fix that ends a layout hang.
The hang fix (the reason this is urgent)
ps-taffy4355471d fixes an infinite loop indistribute_space_up_to_limits. A track stays counted as growable whileproperty + increase < limit, but the iteration is sized fromlimit - property - increase, and f32 rounding lets the first be true while the second is exactly0.0. The computed increase is then zero, theincrease > 0.0guard rejects every track, nothing changes, and the next iteration recomputes identical values forever.Blitz reaches this through table column sizing. A document containing a table whose column contributions land a rounding step apart wedges
resolve_layoutand pins the calling thread at 100% CPU. For an embedder running layout on the UI thread that is an unrecoverable application hang: it survives SIGTERM, because the shutdown path needs the thread the loop is holding.Upstream detail and the trace are in pathscale/ps-taffy#4.
Also stacked
feat(shell): expose the scrollbars feature to embeddersfeat(shell): pass through shadow-dom, floats and font embolden toofeat(shell): pass through complex-scripts as wellfeat(net): a fetch that keeps the response metadataThese were sitting unlanded on
feat/scrollbars-passthroughandfix/engine-gaps. They cherry-picked onto master with no conflicts.Note on Cargo.lock
The diff is one line, the ps-taffy rev. Running any cargo command here rewrites the
ps-anyrenderentries from registry to git sources, because master's lock and Cargo.toml already disagree about them. That churn is deliberately excluded; this PR does not attempt to fix the pre-existing rot.