AnonymousBoxType, CachedInlineLayout, LayoutNode, LayoutNodeHot, LayoutNodeWarm, LayoutNodeCold, LayoutTree, PseudoElement,
// +spec:block-formatting-context:a4e6f9 - float rules reference only elements in the same BFC (scoped via BfcState)
// +spec:floats:2fa329 - Float positioning (left/right shift), content flow along sides, and clear property
// +spec:floats:148fcd - floating boxes reduce available line box width between containing block edges
// +spec:floats:49a491 - Line boxes stacked with no separation except float clearance, never overlap
// +spec:floats:af94f2 - content displaced by float: line boxes shrink to avoid float margin boxes
// +spec:inline-formatting-context:7cbe58 - shortened line boxes due to floats; shift down if too small
// +spec:containing-block:4b0c44 - line boxes shortened by floats resume containing block width after float
// +spec:inline-formatting-context:e70328 - line box width reduced by floats between containing block edges
// +spec:writing-modes:e55820 - line-relative mappings: left/right interpreted as line-left/line-right per writing mode
// +spec:block-formatting-context:d06e6e - clearance computation for clear property on blocks and floats (CSS 2.2 § 9.5.2)
// +spec:floats:31a3d5 - Clearance computation: places border edge even with bottom outer edge of lowest float to be cleared
// +spec:block-formatting-context:7f6bde - CSS 2.2 § 9.5.2 clear property: clearance places border edge below bottom outer edge of cleared floats
// +spec:block-formatting-context:ef493f - clearance computation: places border edge even with bottom outer edge of lowest float to be cleared; inhibits margin collapsing
// +spec:floats:415066 - Clear property: top border edge below bottom outer edge of cleared floats
// +spec:floats:7e4ad6 - clear property: element box may not be adjacent to earlier floats; only considers floats in same BFC
// +spec:overflow:1a7aff - clearance calculation (incl. negative clearance) and clear on floats (constraint #10)
// +spec:positioning:1c2508 - clearance calculation: places border edge even with bottom outer edge of lowest cleared float (CSS 2.2 § 9.5.2)
// +spec:positioning:fe0912 - clearance computation: places border edge below bottom outer edge of cleared floats
// +spec:floats:054a1e - Clearance computation: positions border edge below bottom outer edge of cleared floats
// +spec:floats:cb984c - Clearance can be negative per spec example 2; inhibits margin collapsing
// +spec:block-formatting-context:b04653 - dispatches layout by formatting context type (BFC, IFC, Table, Flex, Grid)
// +spec:block-formatting-context:e46499 - inner display type determines formatting context (BFC, IFC, table, flex, grid)
// +spec:block-formatting-context:06a24f - CSS 2.2 § 9.4: block-level boxes → BFC, inline-level → IFC
// +spec:block-formatting-context:9428cf - block container can establish both BFC and IFC simultaneously
// +spec:inline-formatting-context:8bfe73 - display:flow generates inline box (Inline) or block container (Block) based on outer display type
// +spec:inline-formatting-context:a180ed - IFC establishment: inline-level boxes fragmented into line boxes with baseline alignment
// +spec:display-property:1f5ddf - inline-level boxes with non-flow inner display establish new formatting context
// +spec:inline-formatting-context:1ad004 - atomic inline (inline-block) establishes new formatting context
// +spec:inline-block:8d21f6 - inline-block generates inline-level block container (BFC inside, atomic inline outside)
// +spec:table-layout:753687 - CSS 2.2 §17.2 table model: display values map to FormattingContext variants and dispatch table layout
FormattingContext::Table => layout_table_fc(ctx, tree, text_cache, node_index, constraints)
LayoutWidth::MinContent | LayoutWidth::MaxContent | LayoutWidth::FitContent(_) => (None, false),
LayoutHeight::MinContent | LayoutHeight::MaxContent | LayoutHeight::FitContent(_) => (None, false),
// +spec:floats:167a2c - Float positioning rules (CSS 2.2 § 9.5.1): left/right/none, precise placement constraints
// +spec:floats:afc8e2 - Float positioning rules (CSS 2.2 § 9.5 rules 1-8): left/right edge containment, earlier-float stacking, outer-top constraints, and "move down" when insufficient space
// +spec:box-model:db0f02 - Float positioning: line boxes shortened by floats, floats shift down if no space, BFC elements must not overlap float margin boxes
// +spec:containing-block:136e45 - Float shifted left/right until outer edge touches containing block edge or another float
// +spec:floats:45fce7 - Float positioning: pulled out of flow, line boxes shortened around float
// +spec:height-calculation:86142a - CSS 2.2 §9.5 float positioning, clearance, and margin non-collapsing
// +spec:width-calculation:761677 - float positioning: content flows around floats, line boxes shortened by float presence
/// 1. **Sizing Pass:** It first iterates through its children and triggers their layout recursively
/// by calling `calculate_layout_for_subtree`. This ensures that the `used_size` property of each
/// 2. **Positioning Pass:** It then iterates through the children again. Now that each child has a
// +spec:display-property:f38f52 - BFC handles normal flow, relative positioning offsets, and float extraction (CSS 2.2 § 9.8)
// +spec:block-formatting-context:4f4ff6 - writing-mode determines block flow direction (main axis) for ordering block-level boxes in BFC
// +spec:containing-block:42b75f - Block element establishes containing block for inline content (IFC)
// +spec:inline-block:17944a - orthogonal flow roots get infinite available inline space here (not yet detected)
// +spec:inline-block:a60e22 - other layout models pass through infinite inline space to contained block containers
// +spec:positioning:447b06 - Absolute positioning pulls element out of flow, skip from normal layout
// +spec:positioning:7dd6d1 - Absolutely positioned boxes are taken out of the normal flow (no impact on later siblings, no margin collapsing)
// +spec:block-formatting-context:98b633 - CSS 2.2 § 9.4.1: boxes laid out vertically, margins collapse
// +spec:display-property:9f6e18 - BFC dispatches normal flow, floats, and relative positioning (CSS 2.2 §9.8)
// +spec:floats:f6c0b2 - floats only processed in BFC; other formatting contexts (flex/grid) inhibit floating
// +spec:floats:d0d163 - clear on floats adds constraint #10: float top below cleared floats' bottom
// +spec:floats:7adb9d - Clear on floats: constraint #10, top outer edge must be below earlier cleared floats
// +spec:block-formatting-context:0f802c - margins use containing block's writing mode for collapsing/auto expansion in orthogonal flows
let child_escaped_bottom = if !has_margin_collapse_blocker(&child_bp, writing_mode, false) {
// +spec:floats:dc195a - Clear property only applies to block-level elements (CSS 2.2 § 9.5.2)
// +spec:floats:148ee6 - clear:left pushes element below float; clearance added above top margin
"[layout_bfc] Child {} clearance check: cleared_position={}, hypothetical={} (main_pen={} + collapse({}, {}))",
// +spec:block-formatting-context:1dada5 - Normal flow boxes in BFC touch containing block edge
// +spec:block-formatting-context:9f56cb - each box's left outer edge touches containing block left edge; new BFC may shrink due to floats
// +spec:display-property:796059 - BFC/replaced/table border box must not overlap float margin boxes; line boxes shorten around floats
// +spec:floats:5214a6 - BFC/replaced/table border box must not overlap float margin boxes; shrink or clear below
// +spec:floats:a29f70 - BFC roots, tables, and block-level replaced elements must not overlap float margin boxes
let (child_margin_cloned, child_margin_auto, child_used_size, is_inline_fc, child_dom_id_for_debug) = {
let remaining = (available_cross - child_used_size.cross(writing_mode) - child_margin.right).max(0.0);
"[layout_bfc] Child {} centering check: available_cross={}, child_cross_size={}, margin_auto.left={}, margin_auto.right={}",
// +spec:block-formatting-context:d52ce5 - auto margins resolved per containing block's writing mode for centering
// +spec:width-calculation:0c5044 - auto margins center element on cross axis (respects writing mode)
// +spec:width-calculation:25c2fc - §10.3.3: block-level margin auto centering and over-constrained resolution
// +spec:width-calculation:ba691f - auto margins treated as zero when element overflows containing block (via .max(0.0) on remaining_space)
// +spec:width-calculation:324e7e - both margin-left and margin-right auto => equal used values (centering)
// +spec:box-model:218643 - over-constrained: drop end margin per containing block writing mode
// +spec:width-calculation:d172a4 - over-constrained: LTR ignores margin-right, RTL ignores margin-left
// +spec:block-formatting-context:7954a2 - 10.6.3: auto height for block-level non-replaced elements in normal flow
// +spec:box-model:4eebed - auto height for BFC = top margin-edge of topmost child to bottom margin-edge of bottommost child
// +spec:box-model:4eebed - auto height = top margin-edge of topmost child to bottom margin-edge of bottommost child
// +spec:positioning:1a05bb - 10.6.7 auto height for BFC roots: block children use margin edges,
// abspos ignored (skipped in Pass 1/2), relative considered without offset (applied after layout),
// +spec:positioning:f94d22 - 10.6.3: block-level non-replaced auto height = distance from top content edge to last in-flow child bottom margin edge (or zero)
// +spec:block-formatting-context:f73d3e - BFC root grows to fully contain its floats; floats from outside cannot protrude in
// +spec:box-model:1d4798 - auto height includes floats whose bottom margin edge exceeds content edge
// only floats participating in this BFC are counted (not floats inside abspos descendants or nested BFCs)
// +spec:inline-formatting-context:2227a4 - atomic inline baseline for inline-block/inline-table
/// 1. **Collect Content**: Traverse the direct children of the IFC root and convert them into a
/// - Recursively laying out `inline-block` children to determine their final size and baseline,
/// 2. **Translate Constraints**: Convert the `LayoutConstraints` (available space, floats) from
/// 3. **Invoke Text Layout**: Call the `text3` cache's `layout_flow` method to perform the complex
/// +spec:display-property:e96c82 - inline formatting context: flow of elements/text wrapped into lines
/// - Store the rich layout result on the IFC root `LayoutNode` for the display list generation
// +spec:display-property:574e7b - text-box-trim for inline boxes trims block-end to content edge (TODO: implement trimming per text-box-edge metric)
// +spec:display-property:da284a - IFC: flow inline-level boxes into line boxes, size/position each fragment
// +spec:inline-formatting-context:275f64 - IFC: boxes laid out horizontally into line boxes, respecting margins/borders/padding
// +spec:display-property:7f3c1d - Anonymous inline boxes: text directly in block containers treated as anonymous inline elements in IFC
// +spec:display-property:5a795c - root inline box: block container generates anonymous inline box holding all inline-level contents, inheriting from parent
// +spec:display-property:a469a6 - line boxes created as needed for inline-level content in IFC
// +spec:display-property:f3c875 - calculate layout bounds (size contributions) of each inline-level box
let _text_count = inline_content.iter().filter(|i| matches!(i, InlineContent::Text(_))).count();
let _shape_count = inline_content.iter().filter(|i| matches!(i, InlineContent::Shape(_))).count();
// +spec:display-property:a0d0ab - IFC height = top of topmost line box to bottom of bottommost line box
// +spec:display-property:a63b8f - baseline-source defaults to auto (last baseline for inline-block/IFC)
// +spec:box-model:02e0f9 - text-box-trim: trim-end and trim-both, no effect with non-zero padding/border
let ifc_node_state = &ctx.styled_dom.styled_nodes.as_container()[ifc_root_dom_id].styled_node_state;
let has_pad_or_border_top = match get_css_padding_top(ctx.styled_dom, ifc_root_dom_id, ifc_node_state) {
let has_pad_or_border_bottom = match get_css_padding_bottom(ctx.styled_dom, ifc_root_dom_id, ifc_node_state) {
let trim_start = matches!(text_box_trim, StyleTextBoxTrim::TrimStart | StyleTextBoxTrim::TrimBoth)
// +spec:block-formatting-context:40e03e - BFC root: block container establishing new BFC (contains floats, excludes external floats, suppresses margin collapsing)
// +spec:block-formatting-context:241d22 - block container establishes new BFC or continues parent's, based on overflow/position/float/display
// +spec:block-formatting-context:9fe441 - BFC establishment based on position, float, overflow, and display properties
// +spec:display-property:3c7369 - block boxes establishing independent FC create new BFC; flex containers already do; non-replaced inlines cannot
// +spec:positioning:1e94f6 - floats, abspos, inline-blocks/table-cells/table-captions, overflow!=visible establish new BFC
fn establishes_new_bfc<T: ParsedFontTrait>(ctx: &LayoutContext<'_, T>, node: &LayoutNodeHot, cold: Option<&LayoutNodeCold>) -> bool {
// +spec:block-formatting-context:f39cd3 - table wrapper box establishes a BFC (CSS 2.2 §17.4)
let position = crate::solver3::positioning::get_position_type(ctx.styled_dom, Some(dom_id));
// +spec:display-property:14bae6 - flow-root establishes a formatting context that contains/excludes floats
// +spec:overflow:f6a186 - overflow:clip does NOT establish BFC; use display:flow-root for that
// +spec:positioning:6feb32 - overflow:clip does NOT establish new formatting context; hidden/scroll/auto do
// +spec:overflow:b34aef - Block boxes with overflow other than 'visible' or 'clip' establish BFC
// +spec:block-formatting-context:33e6cd - block container with different writing-mode than parent establishes independent BFC
let parent_state = &ctx.styled_dom.styled_nodes.as_container()[parent_dom_id].styled_node_state;
let parent_wm = get_writing_mode(ctx.styled_dom, parent_dom_id, parent_state).unwrap_or_default();
// NOTE: align-content != normal should also establish BFC per CSS-DISPLAY-3, but align-content is not yet implemented for block containers
// +spec:display-property:5e5420 - replaced element identification (glossary: replaced elements have natural dimensions, establish independent formatting context)
fn is_block_level_replaced<T: ParsedFontTrait>(ctx: &LayoutContext<'_, T>, node: &LayoutNodeHot) -> bool {
// +spec:display-property:21f728 - vertical-align shorthand resolves inline-level box alignment
// +spec:display-property:98fa8e - alignment-baseline values for inline-level boxes in IFC (implemented via vertical-align shorthand)
// +spec:display-property:1f71ad - baseline-shift + alignment-baseline longhands mapped through vertical-align
// +spec:display-property:89dd7b - line-relative shift values (top/center/bottom) and aligned subtree alignment
// +spec:inline-formatting-context:21da06 - vertical-align uses line-over/line-under sides via writing_mode logical mapping
// +spec:inline-formatting-context:295603 - baseline alignment: vertical-align determines how inline boxes align (baseline, super, sub, etc.)
// +spec:inline-formatting-context:7351bf - default alignment baseline is alphabetic in horizontal typographic mode
// +spec:inline-formatting-context:85de3d - vertical-align shorthand: alignment within line box
// +spec:inline-formatting-context:aa8af0 - alignment baseline chosen by vertical-align, defaults to parent's dominant baseline
// +spec:inline-formatting-context:e475d2 - baseline and vertical-align control transverse alignment of inline content on line boxes
// +spec:overflow:d44eac - vertical-align inline box alignment (CSS 2.2 model covers baseline/top/middle/bottom/sub/super/text-top/text-bottom)
// +spec:writing-modes:313575 - alignment-baseline: inline-level boxes align baselines within parent inline box's alignment context along inline axis
// +spec:writing-modes:0127e5 - line-relative directions: line-over/under map to vertical-align top/bottom
// +spec:inline-formatting-context:686f8b - vertical-align shorthand: alignment-baseline + baseline-shift for inline boxes
// +spec:inline-formatting-context:e579b6 - vertical-align / baseline alignment in inline context
// +spec:display-property:c03a6b - baseline-shift (sub/super/length/percentage) and line-relative (top/center/bottom) shifts handled via vertical-align
// +spec:inline-formatting-context:fe563c - vertical-align: super shifts inline to superscript position
// +spec:inline-formatting-context:fe563c - vertical-align:super shifts child to superscript position
// +spec:block-formatting-context:987746 - text-orientation property (mixed/upright/sideways) for vertical writing modes
// +spec:inline-formatting-context:cbe738 - text-orientation (mixed/upright/sideways) bi-orientational transform for vertical text
// +spec:writing-modes:09a1bb - vertical typesetting orientation (upright/sideways) for vertical-rl/vertical-lr
// +spec:writing-modes:2eb1b2 - text-orientation (mixed/upright/sideways) applied to vertical text layout
// +spec:block-formatting-context:a606e6 - sideways text typeset rotated 90° CW in vertical modes
// +spec:display-property:8364c0 - direction property (ltr/rtl) sets paragraph embedding level for bidi algorithm
// +spec:text-alignment-spacing:97b93a - direction property affects text-align:justify last-line alignment
// +spec:writing-modes:73aaff - block elements inherit base direction from parent via CSS direction property
// +spec:writing-modes:8a888b - line box inline base direction from containing block's direction
// +spec:display-property:da3b59 - direction property specifies inline base direction for ordering inline-level content
// +spec:inline-formatting-context:97af40 - direction property sets inline base direction for bidi, text alignment, overflow
// +spec:writing-modes:fbb332 - in vertical writing modes, text-orientation:upright forces used direction to ltr
// +spec:containing-block:0d4914 - unicode-bidi: plaintext causes P2/P3 heuristics instead of HL1 override
// +spec:display-contents:5f95ac - text-indent: percentage=0 for intrinsic sizing, each-line and hanging keywords
// +spec:positioning:1e32b1 - text-indent with hanging/each-line keywords resolved and passed to text layout
// +spec:intrinsic-sizing:0e8625 - percentage text-indent treated as 0 for intrinsic size contributions
// +spec:block-formatting-context:fd60a8 - initial letter box is in-flow in its BFC, originating line box
// +spec:block-formatting-context:c5ba02 - initial letter inline flow layout (alignment, white space collapsing)
// +spec:block-formatting-context:fef28d - initial letter box is in-flow in its BFC, part of originating line box
// +spec:box-model:c3ce58 - initial letter block-start margin edge must be below containing block content edge
// +spec:display-property:a89adb - initial letter boxes from non-replaced inline boxes and atomic inlines
// +spec:display-property:4b59ce - initial-letter applies to inline-level boxes at start of first line
// +spec:display-property:756cad - initial-letter sizing: drop/raise/sunken initial computation
// +spec:display-property:8b08f4 - initial-letter applied to first inline-level child of block container
// +spec:display-property:8c1dce - initial-letter property: size/sink for drop caps on inline-level boxes
// +spec:display-property:b5e149 - initial letters are in-flow inline-level content, not floats
// +spec:line-height:306d87 - initial-letter sizing must use containing block's line-height, not spanned lines' heights
// +spec:writing-modes:903310 - atomic initial letters use normal sizing; only positioning is special
azul_css::props::style::text::StyleInitialLetterAlign::Auto => text3::cache::InitialLetterAlign::Auto,
azul_css::props::style::text::StyleInitialLetterAlign::Alphabetic => text3::cache::InitialLetterAlign::Alphabetic,
azul_css::props::style::text::StyleInitialLetterAlign::Hanging => text3::cache::InitialLetterAlign::Hanging,
azul_css::props::style::text::StyleInitialLetterAlign::Ideographic => text3::cache::InitialLetterAlign::Ideographic,
// +spec:display-property:5af252 - initial-letter on inline-level box not at line start uses normal
// +spec:text-alignment-spacing:a17609 - sunken initial letters suppress letter-spacing and justification (not word-spacing) with adjacent content
// +spec:writing-modes:c89d19 - initial-letter block-axis positioning: sink determines block offset
// +spec:display-property:b67500 - initial-letter size/sink: values other than normal make box an initial letter box (inline-level, in-flow)
// +spec:display-property:416f27 - initial-letter sink defaults to "drop" (sink = size floored) when omitted
// +spec:floats:c5e23f - floats in subsequent lines adjacent to a sunk initial letter must clear it
// +spec:line-breaking:9f150a - text-combine-upright:all composes glyphs horizontally, ignoring letter-spacing and forced line breaks
// +spec:line-breaking:1b88cd - text-combine-upright:all layout: inline-block with 1em square, ignoring forced line breaks
// +spec:inline-formatting-context:c8d8d9 - text-combine-upright compression passed to text shaping engine
// +spec:inline-formatting-context:f4ef7d - text-combine-upright layout rules (1em square composition)
// +spec:text-alignment-spacing:6cb965 - text-align shorthand sets text-align-all (mapped here from computed value)
// +spec:text-alignment-spacing:838967 - map text-align values (start/end/left/right/center/justify) to inline alignment
// +spec:text-alignment-spacing:d9ea45 - property index: text-align, text-justify, letter-spacing mapped to layout
// +spec:text-alignment-spacing:600fda - text-align values (left/right/center/justify) mapped per CSS Text §6.1
// +spec:text-alignment-spacing:0ea31d - text-justify inter-word/inter-character/distribute mapped per §6.4
// +spec:text-alignment-spacing:01244f - text-justify: none disables justification, auto uses inter-word as universal default
LayoutTextJustify::Distribute => text3::cache::JustifyContent::InterCharacter, // distribute computes to inter-character
// +spec:line-height:79f3aa - line-height resolved: normal defaults to 1.2, <number>/<percentage> × font-size
// +spec:inline-formatting-context:48ce44 - overflow-wrap property: break at otherwise disallowed points to prevent overflow
// +spec:line-breaking:bbb5f7 - overflow-wrap: anywhere vs break-word distinction for min-content
// +spec:white-space-processing:bc5f7b - line-break with break-spaces allows breaking before first space
// +spec:display-property:d887c0 - Table wrapper box BFC, caption-side, table grid layout (§17.4-17.5)
// +spec:positioning:930891 - Table formatting context implementation (CSS 2.2 § 17 introduction)
// +spec:inline-formatting-context:9c272d - CSS table model: row-primary structure, display-to-table-element mapping, visual formatting as rectangular grid
// +spec:inline-formatting-context:440ca9 - border-collapse/border-spacing/visibility:collapse table properties (CSS 2.2 §17.5-17.6)
// +spec:table-layout:485791 - Six superimposed table layers: table, column-group, column, row-group, row, cell (bottom to top)
// +spec:table-layout:dcdf1b - Collapsing border model: border conflict resolution uses layer priority (cell > row > row-group > column > column-group > table)
// +spec:block-formatting-context:f772ae - border style priority for table border conflict resolution
// +spec:box-model:2255c2 - Collapsing border conflict resolution (hidden wins, then none loses, then wider wins, then style priority)
// +spec:box-model:b42c79 - border conflict resolution: hidden wins, then wider, then style priority, then source
// +spec:box-model:503e9e - border conflict resolution: hidden wins, then wider, then style priority, then source priority
// +spec:box-model:7eb217 - Border conflict resolution: hidden > none < wider > style priority > source priority > left/top
// +spec:overflow:1fb482 - Border conflict resolution per CSS 2.2 §17.6.2.1 (hidden wins, then wider, then style priority, then source priority)
// +spec:table-layout:882560 - Border conflict resolution (17.6.2.1): hidden wins, none loses, wider wins, style priority, source priority
// +spec:table-layout:21053b - border conflict resolution: hidden suppresses all, style priorities
// +spec:table-layout:076617 - border conflict resolution algorithm and border style semantics in collapsing model
// +spec:table-layout:c5e446 - table-layout property (auto|fixed) controls layout algorithm selection
let node_state = ctx.styled_dom.styled_nodes.as_container()[dom_id].styled_node_state.clone();
let node_state = ctx.styled_dom.styled_nodes.as_container()[dom_id].styled_node_state.clone();
let node_state = ctx.styled_dom.styled_nodes.as_container()[dom_id].styled_node_state.clone();
// +spec:inline-formatting-context:9f5f31 - visibility:collapse for table rows/columns, border-collapse and border-spacing
// +spec:box-model:547563 - visibility:collapse removes table rows/columns; elsewhere same as hidden
/// // +spec:overflow:ebb1f9 - For non-table elements, collapse == hidden (no special handling needed)
let node_state = ctx.styled_dom.styled_nodes.as_container()[dom_id].styled_node_state.clone();
// +spec:table-layout:dcdf1b - empty-cells property controls rendering of borders/backgrounds around empty cells in separated borders model
// +spec:table-layout:235e8e - CSS 2.2 §17.1-17.2 table model: fixed/auto algorithms, row/column/cell/caption structure
// +spec:table-layout:a6422d - CSS table model: table structure analysis, row/column/cell layout, caption, border-collapse
let intrinsic = tree.warm(node_index).and_then(|w| w.intrinsic_sizes).unwrap_or_default();
// +spec:table-layout:ff5671 - table-layout property (fixed vs auto) controls column width algorithm
// +spec:width-calculation:7a5b23 - table-layout property determines fixed vs auto algorithm (CSS 2.2 §17.5.2)
// +spec:containing-block:cc1453 - collapsing border model: border-collapse property drives table border handling
// +spec:width-calculation:431d60 - fixed vs auto table layout column width algorithms (CSS 2.2 §17.5.2.1, §17.5.2.2)
// +spec:box-model:494f6b - collapsing border model: row-width formula and table border width computation
// +spec:box-model:e7d0a3 - Separated borders model: border-spacing, empty-cells, collapsing border width calculation
// +spec:box-model:acb81f - separated borders model: border-spacing between adjoining cell borders
// +spec:box-model:e480b1 - table width = left inner padding edge to right inner padding edge (including border-spacing)
// +spec:table-layout:24dbf9 - §17.4 table wrapper box model: caption positioning, BFC establishment
// +spec:width-calculation:600f98 - caption-side positions caption above/below table box (CSS 2.2 §17.4)
// +spec:box-model:52fcfe - overflow_size must include borders that spill into margin in collapsing border model
// +spec:display-property:f47f8a - Table structure analysis: caption positioning, row/column/row-group traversal per CSS 2.2 §17.4-17.5
// +spec:width-calculation:0a2766 - table internal elements form rectangular grid of rows/columns (CSS 2.2 §17.5)
// +spec:display-property:7f167c - Table grid cell placement: rows fill table top-to-bottom, cells placed left-to-right with colspan/rowspan
// +spec:inline-formatting-context:3f8091 - table visual layout: cells occupy grid cells, row/column spanning
// +spec:overflow:66f584 - Fixed table layout: cells use overflow property to clip overflowing content
// +spec:positioning:46070a - Fixed table layout (17.5.2.1) and auto table layout (17.5.2.2) column width algorithms
// +spec:table-layout:875401 - Fixed table layout algorithm (17.5.2.1): column widths from first-row cells, remaining columns divide space equally, table width = max(width property, sum of columns)
// +spec:table-layout:8b72b3 - fixed table layout: column width from column elements/first-row cells, remaining columns equal division
// +spec:table-layout:c5e446 - Fixed table layout algorithm: column widths from col elements or first-row cells, remaining columns divide equally
/// +spec:width-calculation:8c958a - Fixed table layout: column widths from col elements, first-row cells, then equal distribution (CSS 2.2 §17.5.2.1)
// +spec:display-property:05c8e8 - CSS 2.2 §17.5.2.2 automatic table layout: column min/max widths, table width = max(W or CB, CAPMIN, MIN), extra width distributed over columns
/// +spec:overflow:29edde - CSS 2.2 §17.5.2.2 automatic table layout: MCW/max-content per cell, column min/max, colspan distribution, final width determination
// +spec:table-layout:23a215 - automatic table layout: MCW/max cell widths, column min/max, colspan distribution, table width from MAX/MIN/CAPMIN
// +spec:table-layout:5e1145 - Automatic table layout: MCW/max-content per cell, column min/max, colspan distribution, final width from MIN/MAX
// +spec:width-calculation:42dfca - CSS 2.2 §17.5.2.2 automatic table layout: MCW/max-content per cell, column min/max, multi-span distribution, final table width
/// +spec:width-calculation:335ef1 - Automatic table layout: width given by column widths and borders (CSS 2.2 §17.5.2.2)
// +spec:overflow:3fa86f - Table cell baseline: first in-flow line box or bottom of content edge; scrolling boxes treated as at origin
// +spec:inline-formatting-context:c4a20d - cell baseline: first in-flow line box or bottom of content edge
// +spec:inline-formatting-context:17a9c1 - vertical-align baseline/top/bottom/middle for table cells
// +spec:inline-formatting-context:27be38 - cell baseline is first in-flow line box or bottom of content edge
let (item_ascent, _) = crate::text3::cache::get_item_vertical_metrics_approx(&first_item.item);
/// +spec:box-model:72b495 - Table row height = max of computed height and MIN required by cells; baseline alignment
// +spec:display-property:728144 - Table height algorithm: row heights from cell content, rowspan distribution, vertical-align in cells (top/middle/bottom/baseline, sub/super/text-top/text-bottom/length/percentage fall back to baseline), cell baseline computation, and horizontal alignment via text-align
// +spec:positioning:3eaadd - Table height algorithms (§17.5.3): row height = max of cell heights/MIN,
// +spec:inline-formatting-context:87b90d - Table height algorithms: row height = max(computed height, cell heights, MIN); vertical-align in cells (baseline/top/middle/bottom, sub/super/etc. fall back to baseline)
// +spec:inline-formatting-context:a7c7a0 - row height = max of computed height, cell heights, and MIN; vertical-align per cell
// +spec:box-model:073652 - Table height: baseline-aligned cells establish row baseline, then top/bottom/middle cells positioned
// +spec:box-model:1e9cf1 - empty-cells:hide rows get zero height with v-spacing on only one side
// +spec:overflow:a44925 - CSS 2.2 §17.6.1.1: empty-cells:hide suppresses borders/backgrounds; all-hidden rows get zero height
// +spec:table-layout:dc8bc3 - separated borders model: border-spacing, empty-cells, row zero-height
// +spec:box-model:0ab9b0 - empty-cells:hide suppresses borders/backgrounds, row gets zero height if all cells hidden+empty
// +spec:box-model:54e86a - Separated borders model: individual cell borders, border-spacing between cells, empty-cells handling
// row/column/rowgroup/colgroup backgrounds are invisible in border-spacing area (table bg shows through);
// (table padding is already accounted for by the containing block; h_spacing is the border-spacing)
let (h_spacing, v_spacing) = if table_ctx.border_collapse == StyleBorderCollapse::Separate {
let total_col_width: f32 = table_ctx.columns.iter().map(|c| c.computed_width.unwrap_or(0.0)).sum::<f32>()
// +spec:inline-formatting-context:20e8e8 - table cell vertical-align alignment order (baseline first, then top, then bottom/middle)
// +spec:inline-formatting-context:4545e8 - vertical-align on table cells maps to align-content: top→start, bottom→end, middle→center
// +spec:inline-formatting-context:e216be - vertical-align on table cells (baseline, middle, top, bottom)
// +spec:positioning:156e49 - table cell vertical-align ordering and extra padding per CSS 2.2 §17.5.3
let vertical_align_adjustment = if let Some(warm_node) = tree.warm(cell_info.node_index) {
let node_state = ctx.styled_dom.styled_nodes.as_container()[dom_id].styled_node_state.clone();
/// we can find its parent IFC's `inline_layout_result` via `ifc_membership.ifc_root_layout_index`.
// +spec:display-property:63a38b - inline box boundaries and out-of-flow elements are ignored for text adjacency (white space, line-breaking, text-transform)
let result = collect_and_measure_inline_content_impl(ctx, text_cache, tree, ifc_root_index, constraints)?;
"[collect_and_measure_inline_content] OK: Found text node (DOM {:?}) in anonymous wrapper: '{}'",
let style = Arc::new(get_style_properties(ctx.styled_dom, dom_id, ctx.system_style.as_ref(), PhysicalSize::new(ctx.viewport_size.width, ctx.viewport_size.height)));
// +spec:display-property:a37a9a - atomic inline-level boxes treated as neutral characters in bidi reordering
let intrinsic_size = tree.warm(child_index).and_then(|w| w.intrinsic_sizes.clone()).unwrap_or_default();
let overflow_x = get_overflow_x(ctx.styled_dom, dom_id, &styled_node_state).unwrap_or_default();
let overflow_y = get_overflow_y(ctx.styled_dom, dom_id, &styled_node_state).unwrap_or_default();
// +spec:box-model:66ad24 - inline-axis margins, borders, padding respected for inline-level boxes (no collapsing)
let span_style = get_style_properties(ctx.styled_dom, dom_id, ctx.system_style.as_ref(), PhysicalSize::new(ctx.viewport_size.width, ctx.viewport_size.height));
if let MultiValue::Exact(display) = get_display_property(ctx.styled_dom, Some(parent_dom_id)) {
node.dom_node_id == Some(list_dom_id) && tree.warm(*idx).and_then(|w| w.pseudo_element).is_none()
Arc::new(get_style_properties(ctx.styled_dom, list_dom_id_for_style, ctx.system_style.as_ref(), PhysicalSize::new(ctx.viewport_size.width, ctx.viewport_size.height)));
let style = Arc::new(get_style_properties(ctx.styled_dom, ifc_root_dom_id, ctx.system_style.as_ref(), PhysicalSize::new(ctx.viewport_size.width, ctx.viewport_size.height)));
let style = Arc::new(get_style_properties(ctx.styled_dom, dom_child_id, ctx.system_style.as_ref(), PhysicalSize::new(ctx.viewport_size.width, ctx.viewport_size.height)));
if let Some(&layout_idx) = tree.dom_to_layout.get(&dom_child_id).and_then(|v| v.first()) {
let intrinsic_size = tree.warm(child_index).and_then(|w| w.intrinsic_sizes.clone()).unwrap_or_default();
// For explicit height, calculate_used_size_for_node already gave us the correct border-box height
let overflow_x = get_overflow_x(ctx.styled_dom, dom_id, &styled_node_state).unwrap_or_default();
let overflow_y = get_overflow_y(ctx.styled_dom, dom_id, &styled_node_state).unwrap_or_default();
// +spec:replaced-elements:31a782 - replaced elements (img) not rendered purely by CSS box concepts
let span_style = get_style_properties(ctx.styled_dom, dom_id, ctx.system_style.as_ref(), PhysicalSize::new(ctx.viewport_size.width, ctx.viewport_size.height));
// +spec:display-property:c05c53 - inlinifying boxes can't contain block-level boxes; children are recursively inlinified
// +spec:box-model:b7428d - empty inline boxes still have margins, padding, borders, line-height
// +spec:box-model:cc79a4 - empty inline elements still have margins, padding, borders and line height
let padding_top = crate::solver3::getters::get_css_padding_top(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let padding_bottom = crate::solver3::getters::get_css_padding_bottom(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let padding_left = crate::solver3::getters::get_css_padding_left(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let padding_right = crate::solver3::getters::get_css_padding_right(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let border_top = crate::solver3::getters::get_css_border_top_width(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let border_bottom = crate::solver3::getters::get_css_border_bottom_width(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let border_left = crate::solver3::getters::get_css_border_left_width(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let border_right = crate::solver3::getters::get_css_border_right_width(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let margin_left = crate::solver3::getters::get_css_margin_left(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let margin_right = crate::solver3::getters::get_css_margin_right(ctx.styled_dom, span_dom_id, &node_state)
.exact().map(|pv| pv.to_pixels_internal(cb_width, font_size, DEFAULT_FONT_SIZE)).unwrap_or(0.0);
let total_height = resolved_line_height + padding_top + padding_bottom + border_top + border_bottom;
let child_style = get_style_properties(ctx.styled_dom, child_dom_id, ctx.system_style.as_ref(), PhysicalSize::new(ctx.viewport_size.width, ctx.viewport_size.height));
let intrinsic_size = tree.warm(child_index).and_then(|w| w.intrinsic_sizes.clone()).unwrap_or_default();
let overflow_x = get_overflow_x(ctx.styled_dom, child_dom_id, &styled_node_state).unwrap_or_default();
let overflow_y = get_overflow_y(ctx.styled_dom, child_dom_id, &styled_node_state).unwrap_or_default();
// +spec:display-property:0684c4 - block box inlinified: inner display becomes flow-root (treated as atomic inline)
let child_style = get_style_properties(ctx.styled_dom, child_dom_id, ctx.system_style.as_ref(), PhysicalSize::new(ctx.viewport_size.width, ctx.viewport_size.height));
// +spec:block-formatting-context:50d915 - overflow-x handles horizontal, overflow-y handles vertical
// +spec:box-model:63d6f2 - scrollable overflow extends beyond padding edge, needs scroll mechanism
// +spec:box-model:45b5fb - scrollbar space subtracted from content area, inserted between inner border edge and outer padding edge
// +spec:box-model:70a0a4 - UAs must start assuming no scrollbars needed, recalculate if they are
// +spec:box-model:c1b0b2 - scrollbar gutter is space between inner border edge and outer padding edge
// +spec:overflow:4f5b99 - scrollable overflow rectangle: content_size is the minimal axis-aligned rect containing scrollable overflow
// +spec:overflow:e983f4 - overflow:auto/scroll boxes must allow user to access overflowed content via scrollbars
// +spec:overflow:97c257 - relative positioning causing overflow in auto/scroll boxes must trigger scrollbar creation
// +spec:height-calculation:c5af64 - assume no scrollbars initially; only add if content overflows
// +spec:box-model:c3d73f - scrollbar presence affects available content area; padding preserved at scroll end
/// Used for white-space modes that preserve segment breaks (pre, pre-wrap, pre-line, break-spaces).
// +spec:white-space-processing:af4e3f - each newline/segment break in text is treated as a segment break, interpreted per white-space property
// +spec:block-formatting-context:b78223 - fullwidth/wide chars treated as vertical script, halfwidth as horizontal per UAX#11
/// +spec:white-space-processing:159dbf - segment breaks converted to spaces (default transform)
// +spec:white-space-processing:7e9529 - Segment break transformation rules (§4.1.3): collapse consecutive breaks, remove around ZWSP/CJK, else convert to space
// +spec:white-space-processing:3c3680 - remove tabs/spaces around segment break before transform
// +spec:white-space-processing:b64e38 - parser may normalize/collapse whitespace before CSS; CSS cannot restore
/// 2. If `pre`, `pre-wrap`, or `pre-line`: splits text by `\n` and inserts `InlineContent::LineBreak`
// +spec:display-property:1389e3 - bidi control characters per UAX #9 for Unicode bidirectional algorithm
// +spec:display-property:aad99b - inline boxes can be split into fragments due to bidi text processing
/// +spec:white-space-processing:1188f6 - only spaces, tabs, and segment breaks are document white space
// +spec:white-space-processing:efbece - white-space property controls collapsing/preserving of formatting characters for rendering
// +spec:writing-modes:cdd4f1 - white space trimming before bidi reordering preserves end-of-line spaces per UAX9 L1
// +spec:inline-block:381c0c - white-space property: collapsing, wrapping, and forced breaks per mode
// +spec:display-property:8acfaa - Phase I white-space collapsing for each inline in an IFC, ignoring bidi controls
// +spec:white-space-processing:3a0f58 - HTML newlines normalized to U+000A, each treated as segment break
// +spec:white-space-processing:6eb1a2 - CR (U+000D) not treated as segment break by HTML; handle if inserted via DOM
// +spec:white-space-processing:bd11da - white-space property: new lines, spaces/tabs, wrapping per value table
// +spec:white-space-processing:b166c5 - segment breaks preserved as forced line feeds for pre/pre-wrap/break-spaces/pre-line
// For `pre`, `pre-wrap`, `pre-line`, and `break-spaces`, newlines must be preserved as forced breaks
let had_leading = segment.chars().next().map(|c| is_css_document_whitespace(c)).unwrap_or(false);
let had_trailing = segment.chars().last().map(|c| is_css_document_whitespace(c)).unwrap_or(false);
else if had_trailing && had_leading && collapsed.is_empty() { /* already have one space */ }
// +spec:white-space-processing:5e3f70 - text-transform applied after Phase I collapsing, before Phase II trimming
// within preserved white space, because non-preserved spaces were already collapsed in Phase I above.
// +spec:box-model:c93797 - initial-letter alignment points determined from contents (not border-box)
// +spec:width-calculation:7f4f68 - initial-letter-wrap exclusion area (none behavior; first/grid require glyph outlines)
// +spec:overflow:dd0679 - auto-sized initial letter content box fits exactly to content; alignment props do not apply
// +spec:width-calculation:170742 - atomic initial letters with auto block size use inline initial letter sizing
// +spec:containing-block:67fd99 - block-axis positioning: size >= sink shifts by (sink-1)*line_height toward block-end