Skip to content

Never emit bare text nodes as block/flex/grid layout children - #657

Open
nicoburns wants to merge 1 commit into
mainfrom
devin/1786205114-style-panic-nonelement
Open

Never emit bare text nodes as block/flex/grid layout children#657
nicoburns wants to merge 1 commit into
mainfrom
devin/1786205114-style-panic-nonelement

Conversation

@nicoburns

@nicoburns nicoburns commented Aug 8, 2026

Copy link
Copy Markdown
Member

Summary

Fixes 91 WPT crashes: `style` is not available on this node kind (panic in universal_accessors! when Taffy asks for a text node's style during block/flex/grid layout). Box construction leaked bare text nodes as layout children of non-inline containers via three paths:

  1. Internal table displays outside a table (display: table-row-group etc. with no table ancestor, common in the CSS2 anonymous-table and generated-content tests): these fell into collect_layout_children's catch-all arm, which pushed text children raw. They now fall into the flow-container arm (Blitz does not yet generate anonymous table wrapper boxes), so text/inline children get anonymous-block wrapping or an inline root as usual.

  2. display: contents hoisting: push_hoisted_children_and_pseudos pushed hoisted children raw, so text inside a contents node became a bare layout child of the ancestor block/flex/grid container. Hoisting now threads a WrapContext { container_node_id, needs_wrap } describing the nearest non-contents ancestor's wrapping policy, and wraps hoisted children with push_wrapped exactly as if they were direct children of that container:

// contents recursion inside collect_complex_layout_children
collect_layout_children_with_wrap(doc, child_id, out, Some(WrapContext { container_node_id, needs_wrap }))

The all-out-of-flow path uses text_item_needs_wrap (text only): inline elements were previously pushed raw there and are safe (they carry styles), and wrapping them regressed css/css-images/cross-fade-natural-size.html.

  1. Flex/grid containers whose only content is a display:contents pseudo (.flex::before { display: contents; content: "A" }): the has_text_node_or_contents check only looked at DOM children, so the pseudo was pushed raw. The check now includes ::before/::after.

Results (full wpt css run)

  • CRASH: 244 → 153 (91 fixed, 0 new); this panic: 91 → 0
  • PASS: 11021 → 11041 (20 new, 0 regressions)
  • cargo test --workspace, cargo fmt, cargo clippy --workspace clean

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/62b3de4395af4598a23a5918918f3239
Requested by: @nicoburns

WPT results

20 newly passing, 0 newly failing (net +20), 71 other status changes.

Full diff (91 changed tests)
! Crash => Fail css/CSS2/bidi-009.xht
! Crash => Fail css/CSS2/bidi-text/bidi-009a.xht
! Crash => Fail css/CSS2/bidi-text/bidi-009b.xht
! Crash => Fail css/CSS2/generated-content/after-content-display-008.xht
! Crash => Fail css/CSS2/generated-content/after-content-display-009.xht
! Crash => Fail css/CSS2/generated-content/after-content-display-010.xht
! Crash => Fail css/CSS2/generated-content/after-content-display-011.xht
! Crash => Fail css/CSS2/generated-content/after-content-display-012.xht
! Crash => Fail css/CSS2/generated-content/after-content-display-013.xht
! Crash => Fail css/CSS2/generated-content/before-after-display-types-001.xht
! Crash => Fail css/CSS2/generated-content/before-after-table-parts-001.xht
! Crash => Fail css/CSS2/generated-content/before-content-display-008.xht
! Crash => Fail css/CSS2/generated-content/before-content-display-009.xht
! Crash => Fail css/CSS2/generated-content/before-content-display-010.xht
! Crash => Fail css/CSS2/generated-content/before-content-display-011.xht
! Crash => Fail css/CSS2/generated-content/before-content-display-012.xht
! Crash => Fail css/CSS2/generated-content/before-content-display-013.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-089.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-090.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-103.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-104.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-105.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-106.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-109.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-110.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-111.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-112.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-159.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-160.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-197.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-198.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-201.xht
! Crash => Fail css/CSS2/tables/table-anonymous-objects-202.xht
! Crash => Fail css/css-break/table/repeated-section/fixedpos-in-footer-forced-break-print.html
+ Crash => Pass css/css-contain/contain-size-007.html
+ Crash => Pass css/css-contain/contain-size-008.html
+ Crash => Pass css/css-contain/contain-size-009.html
+ Crash => Pass css/css-contain/contain-size-010.html
+ Crash => Pass css/css-display/display-contents-before-after-001.html
! Crash => Fail css/css-display/display-contents-before-after-003.html
+ Crash => Pass css/css-display/display-contents-button.html
+ Crash => Pass css/css-display/display-contents-details.html
! Crash => Fail css/css-display/display-contents-dynamic-before-after-001.html
! Crash => Fail css/css-display/display-contents-dynamic-before-after-first-letter-001.html
+ Crash => Pass css/css-display/display-contents-dynamic-flex-001-inline.html
+ Crash => Pass css/css-display/display-contents-dynamic-flex-001-none.html
! Crash => Fail css/css-display/display-contents-dynamic-flex-002-inline.html
! Crash => Fail css/css-display/display-contents-dynamic-flex-002-none.html
! Crash => Fail css/css-display/display-contents-dynamic-flex-003-inline.html
! Crash => Fail css/css-display/display-contents-dynamic-flex-003-none.html
! Crash => Fail css/css-display/display-contents-dynamic-inline-flex-001-inline.html
! Crash => Fail css/css-display/display-contents-dynamic-inline-flex-001-none.html
! Crash => Fail css/css-display/display-contents-dynamic-list-001-inline.html
! Crash => Fail css/css-display/display-contents-dynamic-list-001-none.html
! Crash => Fail css/css-display/display-contents-dynamic-table-001-inline.html
! Crash => Fail css/css-display/display-contents-dynamic-table-001-none.html
+ Crash => Pass css/css-display/display-contents-fieldset.html
+ Crash => Pass css/css-display/display-contents-flex-001.html
! Crash => Fail css/css-display/display-contents-flex-002.html
! Crash => Fail css/css-display/display-contents-flex-003.html
+ Crash => Pass css/css-display/display-contents-float-001.html
! Crash => Fail css/css-display/display-contents-inline-flex-001.html
! Crash => Fail css/css-display/display-contents-line-height.html
! Crash => Fail css/css-display/display-contents-list-001.html
+ Crash => Pass css/css-display/display-contents-oof-001.html
+ Crash => Pass css/css-display/display-contents-oof-002.html
! Crash => Fail css/css-display/display-contents-shadow-dom-1.html
! Crash => Fail css/css-display/display-contents-table-001.html
! Crash => Fail css/css-display/display-contents-table-003.html
! Crash => Fail css/css-display/display-contents-text-inherit.html
+ Crash => Pass css/css-display/display-contents-text-only-001.html
! Crash => Fail css/css-display/run-in/run-in-contains-inline-005.xht
! Crash => Fail css/css-display/run-in/run-in-contains-table-row-001.xht
! Crash => Fail css/css-display/run-in/run-in-contains-table-row-group-001.xht
+ Crash => Pass css/css-display/run-in/run-in-table-row-between-001.xht
! Crash => Fail css/css-display/run-in/run-in-table-row-between-003.xht
! Crash => Fail css/css-lists/inline-list-with-table-child.html
! Crash => Fail css/css-multicol/table/table-cell-content-change-000.html
! Crash => Fail css/css-multicol/table/table-cell-content-change-001.html
! Crash => Fail css/css-ruby/ruby-layout-internal-boxes.html
! Crash => Fail css/css-tables/insert-after-col.html
! Crash => Fail css/css-text/text-autospace/text-autospace-elements-002.html
! Crash => Fail css/css-text/text-autospace/text-autospace-elements-003.html
! Crash => Fail css/css-text/text-autospace/text-autospace-elements-004.html
! Crash => Fail css/css-text/text-autospace/text-autospace-elements-006.html
! Crash => Fail css/css-text/text-autospace/text-autospace-elements-007.html
+ Crash => Pass css/css-transforms/transform-transformable-table-footer-group.html
+ Crash => Pass css/css-transforms/transform-transformable-table-header-group.html
+ Crash => Pass css/css-transforms/transform-transformable-table-row-group.html
+ Crash => Pass css/css-transforms/transform-transformable-table-row.html
! Crash => Fail css/css-ui/text-overflow-ruby.html

Generated by the WPT workflow.

Text nodes carry no style, so pushing one as a direct layout child of a
block/flex/grid container panics when Taffy asks for the child's style.
Two construction paths leaked bare text nodes:

- Internal table displays (table-row, table-row-group, etc.) occurring
  outside a table fell into a catch-all arm that pushed text children
  raw. They now lay out as flow containers (no anonymous table wrapper
  generation yet), wrapping text/inline children as usual.
- display:contents hoisting pushed hoisted text nodes raw into the
  ancestor container. Hoisted children are now wrapped in anonymous
  blocks per the nearest non-contents ancestor's wrapping policy.

Also detect display:contents ::before/::after pseudos on flex/grid
containers so their hoisted text content is wrapped as an item.
@nicoburns nicoburns self-assigned this Aug 8, 2026
@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

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