Integrate Taffy out-of-flow hoisting support - #805
Open
nicoburns wants to merge 13 commits into
Open
Conversation
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
staging-devin-ai-integration
Bot
force-pushed
the
devin/1787748926-taffy-oof-hoisting-latebound
branch
3 times, most recently
from
August 26, 2026 13:43
9c79ae6 to
4f6fbef
Compare
staging-devin-ai-integration
Bot
force-pushed
the
devin/1787748926-taffy-oof-hoisting-latebound
branch
2 times, most recently
from
September 2, 2026 16:26
89c20f9 to
0b9c934
Compare
…ize) passes Guard inline child taffy layout writes behind RunMode::PerformLayout, snapshot/restore the Parley layout around measure passes, and only run the out-of-flow positioning pass on PerformLayout passes.
Update the taffy pin to the current stack-top rev (grid-area edge-case fixes and cache-invalidation tests). Hoisted out-of-flow boxes were appended to the end of their containing block's paint_children, painting them above every in-flow sibling. Insert them at their (paint level, tree order) position instead, so z-index:auto positioned boxes paint in tree order among positioned siblings (CSS 2.1 Appendix E step 8). Fixes grid-items-relative-positioned-containing-block-004/005/006.
staging-devin-ai-integration
Bot
force-pushed
the
devin/1787748926-taffy-oof-hoisting-latebound
branch
from
September 2, 2026 17:03
69eb1e0 to
1aff5fc
Compare
Taffy's claim policy moved from a LayoutContainingBlock::oof_claims tree hook to CoreStyle::is_containing_block (DioxusLabs/taffy#1170/#1172), so that the grid algorithm's static-position emission and the positioning pass share one source of truth. Implement it on TaffyStyloStyle, moving the style-only fixed/absolute containing-block predicates into stylo_taffy::convert, and reuse the same claims in the damage pass. Bumps taffy to the rewritten stack top (5e71ce15).
… block Hoisted (absolute/fixed) children were recursed into from their layout parent (discarding the result) and then again from the containing block's hoisted list, so nested positioned boxes cost 2^depth visits when damaged. Skip them in the layout-children and pseudo-element loops, mirroring Taffy's round_layout_inner, and share the predicate via Node::is_hoisted.
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.
Summary
Integrates Taffy's browser-style out-of-flow (OOF) hoisting (dispatcher-owned pass) into Blitz. Depends on the Taffy PR stack (DioxusLabs/taffy stack #1173: DioxusLabs/taffy#1140 / DioxusLabs/taffy#1170 / DioxusLabs/taffy#1171 / DioxusLabs/taffy#1172) landing first — the
taffydependency is pinned torev = "76a28695"(stack top, includes the late-bound static-position representation, theCoreStyle::is_containing_blockclaim policy, grid-area edge-case fixes and cache-invalidation tests).This PR replaces #763 (closed; branch preserved as
backup/taffy-oof-hoisting-prelatebound). The only change is the taffy pin bump to the late-bound stack: Blitz's inline emitter already usesStaticPosition::from_edge, which maps unchanged onto the new area+alignment representation.Key changes:
stylo_taffy::convert::position):static → Position::Static,fixed → Position::Fixed(previouslyRelative/Absolute).blitz-dom/layout/mod.rs): per-nodehoisted_childrenstorage viaset_hoisted_children/add_hoisted_children;RoundTree::{is_hoisted, hoisted_child_count, get_hoisted_child_id};LayoutContainingBlockimpl (get_oof_item_style,get_detailed_layout_info);compute_oof_layoutruns in thecompute_cached_layoutcache-miss closures (normal + block dispatch), onPerformLayoutpasses only; forwarded throughTableTreeWrapper.stylo_taffy):TaffyStyloStyleimplementsCoreStyle::is_containing_block, returningContainingBlockClaimsvia the newstylo_taffy::convert::containing_block_claims— positioned elements claim absolute candidates; elements establishing a fixed containing block (transform/translate/rotate/scale/perspective,filter/backdrop-filter,will-change: transform/perspective+ fixpos bits,contain: layout/paint,container-type != normal) claim both. The predicates moved fromNodeintostylo_taffy::convert(establishes_fixed_containing_block/establishes_absolute_containing_block) since they are purely style-derived; the damage pass reuses the same claims viaNode::containing_block_claims().for_position(..).layout/inline.rs): the locallayout_abspos_childroutine is removed; inline layout now emitsOofCandidates (with hypothetical-box static positions, RTL-aware inline-level edges) and anOofPositioningArea, bubbling candidates from float/inline-block subtrees. Measure (ComputeSize) passes no longer mutate persistent state: child Taffy layouts are only written onPerformLayout, and the Parley layout is snapshotted/restored around measure passes (fixes a pre-existing cache-clobber bug where a hover restyle could leave measure-time line breaks in painted output — reproduced on GitHub's repo page; regression test intests/blitz-tests/tests/measure_clobber.rs).ElementData.detailed_grid_info: Option<Box<DetailedGridInfo>>→detailed_layout_info: DetailedLayoutInfo, needed for grid-area-based OOF positioning of non-direct grid descendants.Layout.locationis now relative to its containing block's border box, a post-layout pass (BaseDocument::attach_hoisted_children) repoints each hoisted box'slayout_parentat its CB (soabsolute_position,offsetLeft/Topetc. accumulate through the CB chain) and splices hoisted boxes into the CB'spaint_childrenat their (paint level, tree order) position per CSS 2.1 Appendix E step 8 (z-indexed hoisted boxes are instead pushed into the nearest ancestor stacking context, matching the existing z-index hoisting inflush_styles_to_layout; boxes with an intermediate atomic paint-effect ancestor — opacity/filter/clip-path/mask — are painted inside that ancestor's effect layers with a compensated offset). OOF boxes remain in their DOM parent's paint list when the parent is itself their CB (preserving in-tree paint order) and are skipped otherwise. Paint and hit-testing cancel the CB's scroll offset forposition: fixedchildren — using the viewport scroll when the CB is the root element (fixed boxes don't scroll with the document); scrollable-overflow resolution (resolve_transforms) accounts OOF boxes at their CB instead of their DOM parent.tests/blitz-tests/tests/oof_dynamic_cb.rs): toggling containing-block-establishing properties (transform/will-change/filter/contain/position, via style attributes and:hoverrestyles), DOM insertion/removal of fixed boxes,display:nonetoggles, and content changes inside hoisted subtrees, all under incremental layout.WPT results (before → after, tests fully passing)
Net across these suites: 55 newly passing, 8 regressed (+47 overall). The paint-order splice + taffy stack-top bump added a further net +3 on css/css-grid (
grid-items-relative-positioned-containing-block-004/005/006now pass) with no css-position regressions.Known regressions (grouped)
grid-items-relative-positioned-containing-block-003,position-relative-016,position-sticky-stacking-context-002,CSS2/backgrounds/background-position-002): the tree-order splice fixes ordering within one containing block's paint list, but a box hoisted to a distant CB (e.g. the ICB) still paints after the entire subtree containing a later positioned z-index:auto box, because Blitz paints hierarchically. A correct fix needs CSS 2.1 Appendix E step-8 semantics (collect all positioned z-index:auto descendants at the stacking-context level, painted after in-flow content in tree order) — deferred pending a design that doesn't hurt paint performance.nested-inline-abspos-child*): aposition:relativeinline span is not a Taffy node, so it cannot claim candidates; abspos boxes inside it now bubble past it. The old code positioned them relative to the inline root, which happened to pass these tests.anchor-scroll-fixedpos-003/004,anchor-scroll-nested,position-visibility-anchors-visible-in-overflow,filter-cb-abspos-inline-002/003): false passes on main (both test and ref mis-rendered); the branch now renders the refs correctly so they fail honestly. They needanchor()support (and inline CBs) to pass.Testing
cargo fmt --all,cargo clippy --workspace(clean),cargo check --workspace,cargo test --workspace(all pass).main, including focused reruns ofcss/css-grid/**andcss/css-position/**before/after the paint-order splice.Requested by: @nicoburns
WPT results
Subtests: 1971 newly passing, 44 newly failing (net +1927). Timeouts: +1.
Full diff (232 changed tests)
Generated by the WPT workflow.
Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/f415f9517ff44a2c84e7a03f9de5148a
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/f415f9517ff44a2c84e7a03f9de5148a?variant=devin-insiders