Skip to content

Anonymous table box construction fixups (CSS 2.2 §17.2.1) - #783

Open
nicoburns wants to merge 2 commits into
devin/1787612328-anon-node-generalizationfrom
devin/1787608304-table-anon-fixups
Open

Anonymous table box construction fixups (CSS 2.2 §17.2.1)#783
nicoburns wants to merge 2 commits into
devin/1787612328-anon-node-generalizationfrom
devin/1787608304-table-anon-fixups

Conversation

@nicoburns

@nicoburns nicoburns commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #781 (based on its branch) implementing the anonymous table box fixups of CSS 2.2 §17.2.1 with real anonymous nodes instead of #781's one-pseudo-cell-per-child simplification.

Generalized anonymous box creation (construct.rs): create_anonymous_block is now a thin wrapper over

create_anonymous_node(doc, container, kind: AnonKind /* Block | Table | TableRow | TableCell */)

which styles the node via stylo's precomputed ServoAnonymousBox/Table/TableRow/TableCell pseudo-elements. Since precomputed pseudos take their declarations from UA rules, default.css gains ::-servo-anonymous-table { display: table } (etc.), so the anonymous boxes carry honest computed displays.

Fixups inside tables (table.rs): collect_table_cells is rewritten as a TableBuilder walker with open_anon_row / open_anon_cell run state:

  • a consecutive run of non-table-internal children (blocks, flex/grid, nested tables, non-whitespace text) shares a single anonymous cell (real node, so it gets paint/hit-test geometry);
  • cells — real or generated — occurring outside a row share a single anonymous row;
  • runs are closed by any table-internal sibling (row, row group, real cell); display: contents stays transparent to runs.

build_table_context returns the generated node ids, which are registered in LayoutChildren::anonymous_blocks so the existing reconstruction/deallocation lifecycle applies unchanged. Real-cell-only behavior (colspan/rowspan attrs, first_cell_border) is preserved.

Missing-parent fixups (construct.rs): flow/flex/grid child collection now wraps consecutive runs of table-internal children (display: table-cell/table-row/row-group/...) in an anonymous display: table box (LayoutChildren::anonymous_table_id), which then flows through normal table construction. Whitespace between table-internal boxes is discarded rather than closing the run, per spec.

Testing

New tests/blitz-tests/tests/table_box_fixups.rs covers: run-sharing of misplaced children, real cells closing runs, anonymous rows around bare cells, bare text under tables, stray cells/rows in block containers, incremental mutation geometry updates, and anonymous-node leak checks across 20 reconstructions. cargo fmt / clippy --workspace / test --workspace all pass.

Regression triage (vs main baseline)

Fixed in this PR (follow-up commit):

  • Captions (s-11-1-1b-001/008/009, caption-margin-002/005, contain-size-056, contain-size-table-caption-001, content-visibility-094/095, table-as-item-flex-cross-size, table-as-item-stretch-cross-size{,-2}): TableBuilder was pushing display: table-caption children into anonymous cells; they are now excluded from the grid (matching pre-PR behavior — full caption layout needs the table wrapper box and remains future work).
  • Replaced elements (outline-applies-to-016/017): CSS 2.2 table box generation applies to non-replaced elements only, so internal-table display on e.g. <img> no longer triggers the missing-parent anonymous-table wrapping.
  • Also hardened invalidation: changes to border-spacing/border-collapse etc. now mark box-tree rebuild damage, since these are baked into TableContext at construction time.

Not regressions (fail identically on main in a local run; likely CI environment/flakiness): css/WOFF2/*.

Remaining, out of scope for this PR:

  • bidi-008b, inline-table-002a: previously the misplaced table content wasn't rendered at all, so test and ref trivially matched; the anon boxes now render it and expose pre-existing bidi/letter-spacing and inline-table baseline issues.
  • table-anonymous-border-spacing: requires script support (mutates style.borderSpacing from JS); the static rendering is now more correct.
  • transform-transformable-table-{row,row-group,header-group,footer-group}: transforms on stray table-internal boxes wrapped in an anonymous table; needs transform support on internal table boxes.
  • break-before-second-row: fragmentation.

WPT results

61 newly passing, 8 newly failing (net +53).

Full diff (69 changed tests)
- Pass => Fail css/CSS2/bidi-text/bidi-008b.xht
+ Fail => Pass css/CSS2/bidi-text/bidi-009a.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-001.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-001a.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-002.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-003.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-004.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-004a.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-005.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-006.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-007.xht
+ Fail => Pass css/CSS2/floats-clear/float-applies-to-015.xht
- Pass => Fail css/CSS2/normal-flow/inline-table-002a.xht
+ Fail => Pass css/CSS2/normal-flow/inline-table-width-001a.xht
+ Fail => Pass css/CSS2/normal-flow/inline-table-width-001b.xht
+ Fail => Pass css/CSS2/normal-flow/inline-table-width-002a.xht
+ Fail => Pass css/CSS2/normal-flow/inline-table-zorder-004.xht
+ Fail => Pass css/CSS2/normal-flow/inline-table-zorder-005.xht
+ Fail => Pass css/CSS2/tables/caption-side-applies-to-017.xht
- Pass => Fail css/CSS2/tables/table-anonymous-border-spacing.xht
+ Fail => Pass css/CSS2/tables/table-margin-004.xht
- Pass => Fail css/css-break/table/break-before-second-row.html
+ Fail => Pass css/css-break/table/monolithic-overflow-002.tentative.html
+ Fail => Pass css/css-break/table/repeated-section/abspos-uncontained-text.html
+ Fail => Pass css/css-break/table/repeated-section/background-001.tentative.html
+ Fail => Pass css/css-break/table/repeated-section/image.tentative.html
+ Fail => Pass css/css-break/table/table-border-002.html
+ Fail => Pass css/css-break/table/table-border-005.html
+ Fail => Pass css/css-contain/contain-inline-size-table.html
+ Fail => Pass css/css-contain/contain-size-012.html
+ Fail => Pass css/css-contain/contain-size-012b.html
+ Fail => Pass css/css-display/run-in/run-in-inline-table-between-001.xht
+ Fail => Pass css/css-display/run-in/run-in-table-between-001.xht
+ Fail => Pass css/css-flexbox/flexbox-align-self-horiz-001-table.xhtml
+ Fail => Pass css/css-flexbox/flexbox_flex-formatting-interop.html
+ Fail => Pass css/css-flexbox/flexbox_stf-table-cell.html
+ Fail => Pass css/css-flexbox/flexbox_stf-table-row-group.html
+ Fail => Pass css/css-flexbox/flexbox_stf-table-row.html
+ Fail => Pass css/css-flexbox/flexbox_stf-table-singleline-2.html
+ Fail => Pass css/css-flexbox/flexbox_stf-table-singleline.html
+ Fail => Pass css/css-flexbox/table-as-item-narrow-content.html
+ Fail => Pass css/css-flexbox/table-item-flex-percentage-min-width.html
+ Fail => Pass css/css-flexbox/table-item-flex-percentage-width.html
+ Fail => Pass css/css-flexbox/table-with-float-paint.html
+ Fail => Pass css/css-grid/grid-items/explicitly-sized-grid-item-as-table.html
+ Fail => Pass css/css-page/monolithic-overflow-009-print.html
+ Fail => Pass css/css-page/monolithic-overflow-010-print.html
+ Fail => Pass css/css-page/monolithic-overflow-011-print.html
+ Fail => Pass css/css-sizing/table-child-percentage-height-with-border-box.html
+ Fail => Pass css/css-sizing/table-percentage-max-width-beside-float.html
+ Fail => Pass css/css-tables/anonymous-table-cell-margin-collapsing.html
+ Fail => Pass css/css-tables/anonymous-table-ws-001.html
+ Fail => Pass css/css-tables/dynamic-table-cell-height.html
+ Fail => Pass css/css-tables/html-display-table.html
+ Fail => Pass css/css-tables/percent-height-overflow-auto-in-unrestricted-block-size-cell.tentative.html
+ Fail => Pass css/css-tables/percentages-grandchildren-quirks-mode-001.html
+ Fail => Pass css/css-tables/percentages-grandchildren-quirks-mode-002.html
+ Fail => Pass css/css-tables/table-row-group-color-inheritance-001.html
+ Fail => Pass css/css-tables/whitespace-001.html
+ Fail => Pass css/css-transforms/transform-abspos-006.html
+ Fail => Pass css/css-transforms/transform-abspos-007.html
+ Fail => Pass css/css-transforms/transform-display-002.html
+ Fail => Pass css/css-transforms/transform-display-003.html
- Pass => Fail css/css-transforms/transform-transformable-table-footer-group.html
- Pass => Fail css/css-transforms/transform-transformable-table-header-group.html
- Pass => Fail css/css-transforms/transform-transformable-table-row-group.html
- Pass => Fail css/css-transforms/transform-transformable-table-row.html
+ Fail => Pass css/css-ui/outline-011.html
+ Fail => Pass css/css-ui/outline-019.html

Generated by the WPT workflow.

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

@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/1787608304-table-anon-fixups branch from 0334ee0 to 0b593f0 Compare August 24, 2026 22:41
@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1787608304-table-anon-fixups branch from 0b593f0 to 897320b Compare August 24, 2026 22:54
- Generalize anonymous box creation to AnonKind (block/table/row/cell)
  styled via stylo's precomputed ServoAnonymous* pseudo-elements, with UA
  rules in default.css.
- Run-based fixups inside tables: consecutive misplaced children share a
  single anonymous cell; cells outside a row share an anonymous row.
- Missing-parent fixups: runs of table-internal boxes outside a table are
  wrapped in an anonymous table box, routed through normal table
  construction. Whitespace between table-internal boxes is discarded.
…ements as ordinary table content, rebuild tables on table-property style changes
@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1787608304-table-anon-fixups branch from 897320b to 640f18d Compare August 24, 2026 23:00
@staging-devin-ai-integration
staging-devin-ai-integration Bot changed the base branch from devin/1787604682-table-anonymous-cells to devin/1787612328-anon-node-generalization August 24, 2026 23:04
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