Skip to content

Retain stacking-context membership for out-of-flow paint - #806

Closed
nicoburns wants to merge 5 commits into
mainfrom
devin/1787757603-retained-stacking-contexts
Closed

Retain stacking-context membership for out-of-flow paint#806
nicoburns wants to merge 5 commits into
mainfrom
devin/1787757603-retained-stacking-contexts

Conversation

@nicoburns

@nicoburns nicoburns commented Aug 26, 2026

Copy link
Copy Markdown
Member

Summary

Alternative out-of-flow integration to #805 that keeps Taffy's finalized containing-block-relative geometry without rewriting Blitz's structural layout ancestry.

  • Record both containing block -> OOF children and inverse OOF child -> containing block geometry relations, eliminating the full-tree attach_hoisted_children reconciliation pass.
  • Rebuild context-owned negative / auto_and_zero / positive lists only for explicitly dirty real stacking contexts. Collection follows structural/rendering order, traverses positioned z-index:auto containers, and stops at nested real contexts.
  • Keep stacking membership and spatial/overflow resolution as separate post-layout passes. Paint and hit testing consume the same ordered lists in opposite directions and resolve geometry, scrolling, fixed reference frames, and overflow clips late.
  • Add regressions for dynamic containing-block changes, context-boundary/z-index restyles, structural OOF ordering, clipping/scroll ancestry, negative-z phases, and measure-pass layout preservation.

Validation: cargo fmt --all, cargo clippy --workspace, cargo check --workspace, and cargo test --workspace.

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/b6714d5757fa4fa7b858caed09087cdb
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/b6714d5757fa4fa7b858caed09087cdb?variant=devin-insiders
Requested by: @nicoburns

WPT results

146 newly passing, 34 newly failing (net +112).

Full diff (180 changed tests)
+ Fail => Pass css/CSS2/abspos/static-inside-inline-002.html
+ Fail => Pass css/CSS2/backgrounds/background-bg-pos-204.xht
- Pass => Fail css/CSS2/backgrounds/background-position-002.xht
+ Fail => Pass css/CSS2/backgrounds/background-root-023.xht
+ Fail => Pass css/CSS2/box-display/containing-block-007.xht
+ Fail => Pass css/CSS2/box-display/containing-block-008.xht
+ Fail => Pass css/CSS2/box-display/containing-block-009.xht
+ Fail => Pass css/CSS2/box-display/containing-block-010.xht
+ Fail => Pass css/CSS2/floats-clear/clear-applies-to-012.xht
+ Fail => Pass css/CSS2/floats-clear/clear-applies-to-014.xht
+ Fail => Pass css/CSS2/floats-clear/clear-clearance-calculation-004.xht
+ Fail => Pass css/CSS2/floats-clear/clear-clearance-calculation-005.xht
+ Fail => Pass css/CSS2/floats-clear/floats-019.xht
+ Fail => Pass css/CSS2/generated-content/before-after-positioned-002.html
+ Fail => Pass css/CSS2/generated-content/before-after-positioned-003.html
+ Fail => Pass css/CSS2/margin-padding-clear/margin-collapse-037.xht
+ Fail => Pass css/CSS2/positioning/absolute-non-replaced-width-026.xht
+ Fail => Pass css/CSS2/positioning/abspos-014.xht
+ Fail => Pass css/CSS2/positioning/abspos-015.xht
+ Fail => Pass css/CSS2/positioning/abspos-016.xht
+ Fail => Pass css/CSS2/positioning/abspos-017.xht
+ Fail => Pass css/CSS2/positioning/abspos-018.xht
+ Fail => Pass css/CSS2/positioning/abspos-022.xht
+ Fail => Pass css/CSS2/positioning/abspos-026.xht
+ Fail => Pass css/CSS2/positioning/abspos-containing-block-005.xht
+ Fail => Pass css/CSS2/positioning/abspos-containing-block-006.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-001.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-002.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-003.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-004.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-005.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-006.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-007.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-008.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-009.xht
+ Fail => Pass css/CSS2/positioning/abspos-overflow-012.xht
+ Fail => Pass css/CSS2/positioning/dynamic-top-change-003.xht
+ Fail => Pass css/CSS2/positioning/left-applies-to-009.xht
+ Fail => Pass css/CSS2/positioning/left-applies-to-012.xht
+ Fail => Pass css/CSS2/positioning/left-applies-to-013.xht
+ Fail => Pass css/CSS2/positioning/left-applies-to-014.xht
+ Fail => Pass css/CSS2/positioning/position-fixed-007.xht
+ Fail => Pass css/CSS2/positioning/position-static-001.xht
+ Fail => Pass css/CSS2/positioning/relpos-calcs-001.xht
+ Fail => Pass css/CSS2/positioning/relpos-calcs-002.xht
+ Fail => Pass css/CSS2/positioning/relpos-calcs-007.xht
+ Fail => Pass css/CSS2/positioning/right-applies-to-009.xht
+ Fail => Pass css/CSS2/positioning/right-applies-to-012.xht
+ Fail => Pass css/CSS2/positioning/right-applies-to-013.xht
+ Fail => Pass css/CSS2/positioning/right-applies-to-014.xht
+ Fail => Pass css/CSS2/positioning/toogle-abspos-on-relpos-inline-child.html
+ Fail => Pass css/CSS2/positioning/top-applies-to-009.xht
+ Fail => Pass css/CSS2/positioning/top-applies-to-012.xht
+ Fail => Pass css/CSS2/positioning/top-applies-to-013.xht
+ Fail => Pass css/CSS2/positioning/top-applies-to-014.xht
+ Fail => Pass css/CSS2/tables/separated-border-model-007.xht
+ Fail => Pass css/CSS2/tables/separated-border-model-008.xht
+ Fail => Pass css/CSS2/tables/separated-border-model-009.xht
+ Fail => Pass css/CSS2/visuren/anonymous-boxes-001b.xht
+ Fail => Pass css/CSS2/visuren/inherit-static-offset-001.xht
+ Fail => Pass css/CSS2/visuren/inherit-static-offset-002.xht
+ Fail => Pass css/CSS2/visuren/inherit-static-offset-003.xht
+ Fail => Pass css/CSS2/visuren/position-absolute-percentage-inherit-001.xht
+ Fail => Pass css/compositing/isolation/isolation-establishes-stacking-context.html
+ Fail => Pass css/css-align/abspos/align-self-with-flex-grid-parent.html
- Pass => Fail css/css-anchor-position/anchor-position-dynamic-005.html
- Pass => Fail css/css-anchor-position/anchor-scroll-fixedpos-003.html
- Pass => Fail css/css-anchor-position/anchor-scroll-fixedpos-004.html
- Pass => Fail css/css-anchor-position/anchor-scroll-nested.html
- Pass => Fail css/css-anchor-position/position-visibility-anchors-visible-in-overflow.html
+ Fail => Pass css/css-backgrounds/background-origin-006.html
+ Fail => Pass css/css-backgrounds/background-rounded-image-clip-001.html
+ Fail => Pass css/css-backgrounds/background-size-percentage-root.html
- Pass => Fail css/css-break/grid/grid-item-oof-012.html
+ Fail => Pass css/css-break/out-of-flow-in-multicolumn-091.html
+ Fail => Pass css/css-break/out-of-flow-in-multicolumn-092.html
+ Fail => Pass css/css-break/out-of-flow-in-multicolumn-104.html
+ Fail => Pass css/css-conditional/container-queries/no-layout-containment-abspos.html
+ Fail => Pass css/css-conditional/container-queries/no-layout-containment-fixedpos.html
- Pass => Fail css/css-contain/contain-layout-020.html
- Pass => Fail css/css-contain/contain-layout-ignored-cases-ib-split-001.html
+ Fail => Pass css/css-contain/contain-layout-stacking-context-001.html
+ Fail => Pass css/css-contain/contain-paint-clip-001.html
- Pass => Fail css/css-contain/contain-paint-ignored-cases-ib-split-001.html
- Pass => Fail css/css-contain/contain-paint-ignored-cases-ruby-stacking-and-clipping-001.html
+ Fail => Pass css/css-contain/contain-paint-stacking-context-001a.html
+ Fail => Pass css/css-contain/contain-paint-stacking-context-001b.html
- Pass => Fail css/css-contain/content-visibility/content-visibility-095.html
+ Fail => Pass css/css-flexbox/flex-item-position-relative-001.html
+ Fail => Pass css/css-flexbox/flexbox-items-as-stacking-contexts-001.xhtml
+ Fail => Pass css/css-flexbox/flexbox-paint-ordering-003.html
+ Fail => Pass css/css-flexbox/flexbox_margin-left-ex.html
+ Fail => Pass css/css-flexbox/order/order-abs-children-painting-order.html
- Pass => Fail css/css-flexbox/table-as-item-large-intrinsic-size.html
- Pass => Fail css/css-grid/abspos/grid-abspos-staticpos-align-self-safe-outer-cb-003.tentative.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendant-containing-block-004.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendant-containing-block-006.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendant-containing-block-007.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendant-containing-block-008.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendant-containing-block-012.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendant-containing-block-014.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendant-containing-block-015.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendant-containing-block-016.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-descendants-017.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-items-019.html
+ Fail => Pass css/css-grid/abspos/positioned-grid-items-024.html
+ Fail => Pass css/css-grid/grid-items/grid-items-percentage-paddings-015.html
+ Fail => Pass css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/column-subgrid-abs-pos-001.html
+ Fail => Pass css/css-grid/subgrid/abs-pos-001.html
- Pass => Fail css/css-images/image-orientation/image-orientation-img-object-fit.html
+ Fail => Pass css/css-inline/text-box-trim/text-box-trim-inline-box-001.html
+ Fail => Pass css/css-inline/text-box-trim/text-box-trim-inline-box-002.html
+ Fail => Pass css/css-multicol/multicol-contained-absolute.html
+ Fail => Pass css/css-overflow/overflow-clip-margin-021.html
+ Fail => Pass css/css-overflow/overflow-clip-margin-022.html
+ Fail => Pass css/css-page/fixedpos-005-print.html
+ Fail => Pass css/css-page/fixedpos-007-print.html
+ Fail => Pass css/css-page/fixedpos-008-print.html
+ Fail => Pass css/css-position/multicol/vlr-ltr-ltr-in-multicols.html
+ Fail => Pass css/css-position/multicol/vrl-ltr-ltr-in-multicols.html
+ Fail => Pass css/css-position/position-absolute-center-003.html
+ Fail => Pass css/css-position/position-absolute-center-004.html
+ Fail => Pass css/css-position/position-fixed-overflow-print.html
+ Fail => Pass css/css-position/position-relative-table-td-left.html
+ Fail => Pass css/css-position/position-relative-table-td-top.html
+ Fail => Pass css/css-position/position-static-001.html
+ Fail => Pass css/css-position/sticky/position-sticky-large-top-2.tentative.html
+ Fail => Pass css/css-position/sticky/position-sticky-large-top.tentative.html
- Pass => Fail css/css-pseudo/first-letter-width-2.html
+ Fail => Pass css/css-pseudo/highlight-z-index-001.html
+ Fail => Pass css/css-tables/absolute-tables-010.tentative.html
+ Fail => Pass css/css-text-decor/text-decoration-thickness-overline-001.html
+ Fail => Pass css/css-text-decor/text-decoration-thickness-underline-001.html
+ Fail => Pass css/css-text-decor/text-shadow/text-shadow-emoji-transparent.html
- Pass => Fail css/css-transforms/backface-visibility-hidden-animated-001.html
- Pass => Fail css/css-transforms/backface-visibility-hidden-animated-002.html
- Pass => Fail css/css-transforms/individual-transform/stacking-context-001.html
+ Fail => Pass css/css-transforms/individual-transform/stacking-context-002.html
+ Fail => Pass css/css-transforms/individual-transform/stacking-context-003.html
+ Fail => Pass css/css-transforms/individual-transform/stacking-context-004.html
- Pass => Fail css/css-transforms/perspective-containing-block-dynamic-1b.html
- Pass => Fail css/css-transforms/perspective-split-by-zero-w.html
+ Fail => Pass css/css-transforms/preserve-3d-flat-grouping-properties-containing-block-inline.tentative.html
- Pass => Fail css/css-transforms/transform-containing-block-dynamic-1b.html
+ Fail => Pass css/css-transforms/transform-stacking-001.html
+ Fail => Pass css/css-transforms/ttwf-transform-translatex-001.html
+ Fail => Pass css/css-transforms/ttwf-transform-translatey-001.html
- Pass => Fail css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-new.html
- Pass => Fail css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html
- Pass => Fail css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-new.html
- Pass => Fail css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html
- Pass => Fail css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter.html
+ Fail => Pass css/css-viewport/zoom/explicit-inherit/bottom.html
+ Fail => Pass css/css-viewport/zoom/explicit-inherit/left.html
+ Fail => Pass css/css-viewport/zoom/explicit-inherit/right.html
+ Fail => Pass css/css-viewport/zoom/explicit-inherit/top.html
- Pass => Fail css/css-will-change/will-change-abspos-cb-dynamic-001.html
+ Fail => Pass css/css-will-change/will-change-fixpos-cb-height-1.html
+ Fail => Pass css/css-will-change/will-change-fixpos-cb-position-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-backdrop-filter-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-clip-path-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-filter-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-isolation-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-mask-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-mask-image-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-mix-blend-mode-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-offset-path-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-opacity-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-perspective-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-position-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-transform-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-transform-style-1.html
+ Fail => Pass css/css-will-change/will-change-stacking-context-translate-1.html
+ Fail => Pass css/css-writing-modes/float-rgt-orthog-htb-in-vrl-003.xht
- Pass => Fail css/cssom-view/cssom-getBoundingClientRect-vertical-rl.html
- Pass => Fail css/filter-effects/backdrop-filter-clip-rect-zoom.html
- Pass => Fail css/filter-effects/backdrop-filter-plus-mask-large.html
- Pass => Fail css/filter-effects/filter-cb-abspos-inline-002.html
- Pass => Fail css/filter-effects/filter-cb-abspos-inline-003.html
- Pass => Fail css/filter-effects/filter-cb-dynamic-1b.html

Generated by the WPT workflow.

@staging-devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1787757603-retained-stacking-contexts branch from 8d1b069 to 9da4ddb Compare August 26, 2026 17:01
@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1787757603-retained-stacking-contexts branch from 9da4ddb to f8de72a Compare August 26, 2026 17:04
Out-of-flow boxes found while laying out inline content were emitted as
OOF candidates of the IFC root and bubbled to the nearest positioned
Taffy node. A positioned (or filtered) non-atomic inline between them is
not a Taffy node, so it was skipped and the box landed on an outer block
or the ICB. With the stretched-link pattern (a{position:relative} +
a::after{position:absolute;inset:0;z-index:1}) this produced a
viewport-sized invisible overlay that swallowed hover/hit-testing for
the whole page (e.g. the header links on examples/assets/bbc.html).

Inline layout now walks the inline ancestor chain of each out-of-flow
inline box. If an ancestor within the IFC establishes the containing
block, the candidate is recorded with a positioning area spanning the
ancestor's first to last fragment box (CSS 2.1 §10.1) and laid out in
the inline root's OOF pass via compute_oof_layout, re-homed to the
inline root for paint and hit-testing. Otherwise it bubbles as before.

Non-atomic inlines are not transformable, so only position, filters and
the corresponding will-change values make them containing blocks
(Node::inline_establishes_fixed_containing_block).
A stacking-context root with stacked entries is descended into for every hit test regardless of whether the point lies within its own box, since an entry may be positioned anywhere. The inline root's text hit-test then ran unguarded, and Parley's line lookup clamps out-of-range block offsets to the first/last line, so a point thousands of pixels above a heading such as `h4 { position: relative; z-index: 3 }` (with a hoisted abspos self-link) reported a glyph hit on that heading. On html.spec.whatwg.org this made every hit in the table of contents resolve to a heading in section 15, breaking hover and click on all TOC links.

Guard the text hit-test with the node's own box/overflow checks, as text can never lie outside them.
Replace the unconditional `has_stacked_content` escape in `hit_inner` with
the node's `scrollable_overflow` rect, so hit testing no longer descends
into every stacking context with entries (thousands of headings on the
HTML spec) but only those whose painted bounds contain the point.

For that to be sound the overflow rect must cover everything painted as
part of the node's subtree. `resolve_transforms` now also unions in
hoisted stacked entries and, via `escaping_oof_rect`, out-of-flow
descendants whose containing block is an ancestor of the node (their
geometry is otherwise only accounted for at the containing block).

Also computes the context root's geometry origin once per hit test rather
than once per entry, and defers the clip check for an entry until it
actually reports a hit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant