// +spec:display-property:b7f4bf - anonymous inline/block boxes are both called "anonymous boxes"
// +spec:display-property:ae4f16 - anonymous boxes are treated as descendants alongside pseudo-elements
report.dom_to_layout_bytes = entries * (core::mem::size_of::<NodeId>() + core::mem::size_of::<Vec<usize>>());
hot.box_props = crate::solver3::geometry::PackedBoxProps::pack(&cold.unresolved_box_props.resolve(¶ms));
pub fn get_inline_layout_for_node(&self, layout_index: usize) -> Option<&std::sync::Arc<UnifiedLayout>> {
layout_tree.subtree_needs_intrinsic = compute_subtree_needs_intrinsic(ctx.styled_dom, &layout_tree);
let self_stf = is_shrink_to_fit_context(styled_dom, hot.dom_node_id, &hot.formatting_context);
// +spec:display-property:2188b7 - builds box tree: each element's principal box is child of nearest ancestor's principal box, with anonymous boxes for tables/inline wrapping
let node_position = self.nodes.get(node_idx).map(|n| n.computed_style.position).unwrap_or_default();
let node_float = self.nodes.get(node_idx).map(|n| n.computed_style.float).unwrap_or_default();
let is_absolute_or_fixed = matches!(node_position, LayoutPosition::Absolute | LayoutPosition::Fixed);
.and_then(|p| self.nodes.get(p).map(|n| matches!(n.formatting_context, FormattingContext::Flex | FormattingContext::Grid)))
if let FormattingContext::Block { ref mut establishes_new_context } = node.formatting_context {
// +spec:display-property:2bb592 - list-item generates ::marker pseudo-element with list-style content
// +spec:display-property:a48f00 - additional boxes (marker, table wrapper) placed w.r.t. principal box
// +spec:display-property:a42905 - list-item generates ::marker pseudo-element with list-style content, principal box outer=block inner=flow
// +spec:display-contents:376f2e - display:contents removes principal box, children render normally
// +spec:display-contents:3c7066 - display:contents strips element from formatting tree, hoists children
// +spec:display-contents:3f4884 - replaced elements / form controls not specially handled yet (spec note: use display:none instead)
// +spec:display-contents:4f9129 - semantic container role preserved: children promoted but DOM structure unchanged
// +spec:display-contents:7558e8 - display:contents is rendering-time only; DOM relationships unaffected
// +spec:display-contents:a079e3 - display:contents generates no box; children promoted to nearest non-contents ancestor (writing-mode parent lookup skips these)
// +spec:display-contents:e202d5 - display:contents removes principal box, children render as normal
// +spec:display-contents:6bbdf4 - display:contents preserves semantic container role (visibility context)
// +spec:display-property:d7a8de - display:none/contents elements generate no box; anonymous box generation ignores them
// +spec:display-contents:61992e - element itself generates no boxes, children promoted to parent
// +spec:display-contents:b0a76b - display:contents generates no box; children promoted to parent
// +spec:display-property:e370af - display:contents generates no box; children promoted to parent
// +spec:display-contents:852a59 - display:contents computes to display:none for replaced elements
// +spec:display-contents:4a524e - display:contents computes to display:none on replaced elements
// +spec:replaced-elements:af1e68 - display:contents on replaced elements has no effect (element renders normally)
// +spec:table-layout:d52e09 - display:table/inline-table cause element to behave like a table element
// +spec:display-contents:34008d - display:none elements generate no boxes; excluded from formatting structure
// +spec:display-property:eb53f7 - display:none suppresses box generation; visibility:hidden boxes still affect layout
// +spec:display-property:d1600a - display:none suppresses box generation; visibility:hidden boxes still affect layout
// +spec:display-property:934c84 - table wrapper box generation: display:table/inline-table generates a principal block container (table wrapper box) that establishes BFC and contains the table box + caption boxes
// +spec:width-calculation:59d456 - table wrapper box is block-level, establishes BFC (CSS 2.2 §17.4)
let child_idx = self.process_node(styled_dom, child_dom_id, Some(node_idx), debug_messages)?;
// +spec:display-property:5572e7 - Anonymous block boxes: wrap inline runs when block container has mixed block/inline children
// +spec:display-property:090043 - Anonymous block box properties inherited from enclosing non-anonymous box; non-inherited props get initial values
// +spec:display-property:7b9f7a - Block-level vs inline-level classification and anonymous block box creation
// +spec:display-property:078fe5 - Anonymous block boxes wrapping inline content in mixed block/inline contexts
// +spec:display-property:8d8ef3 - block container anonymous box generation: wraps inline runs in anonymous block boxes to ensure block containers contain only block-level or only inline-level boxes
// +spec:display-property:1fe2be - inline box construction with anonymous text interspersed with inline elements
// +spec:display-property:be80e3 - Anonymous inline boxes: text in block containers treated as anonymous inlines, whitespace-only runs collapsed
// +spec:display-property:b73c50 - blockify inline content by wrapping in anonymous block containers
// +spec:display-property:7d1570 - whitespace-only text that would be collapsed does not generate anonymous inline boxes
// +spec:white-space-processing:b32f69 - whitespace-only inline runs between blocks don't generate anonymous inline boxes
// +spec:table-layout:6bb84e - Anonymous table object generation (stages 1-3: remove irrelevant boxes, generate missing child wrappers, generate missing parents)
// +spec:display-property:6f8f13 - anonymous table object generation (§17.2.1): suppress table-column/table-column-group children, wrap non-proper children in anonymous rows/cells
// +spec:display-property:52f497 - anonymous inline boxes inherit inheritable properties from block parent; non-inherited properties use initial values (dom_node_id: None + BoxProps::default())
// +spec:display-property:ee83bf - Anonymous box generation: boxes not associated with elements, inheriting through box tree parentage
// +spec:display-property:6ff51a - anonymous block boxes have no styles (box_props default), so parent element properties still apply to its content
// +spec:display-property:e67146 - Anonymous boxes inherit from enclosing non-anonymous box; non-inherited props use initial values
let collected = collect_box_props(styled_dom, dom_id, debug_messages, self.viewport_size);
pub fn clone_node_from_old(&mut self, old_node: &LayoutNode, parent: Option<usize>) -> usize {
// +spec:display-property:697082 - outer display type determines principal box's role in flow layout (block vs inline)
// +spec:display-property:0d251b - Block-level elements: display 'block', 'list-item', 'table' generate block-level boxes
// +spec:display-property:9464be - block-level vs block container distinction: not all block-level boxes are block containers (e.g. replaced elements, flex containers)
// +spec:display-property:23f111 - Inline-level elements: inline, inline-block, inline-table, inline-flex, inline-grid
// +spec:display-property:c2520b - Block containers with only inline-level children establish IFC; mixed content gets anonymous block wrappers
// +spec:display-property:75d642 - block container with only inline-level content establishes IFC
// +spec:display-property:c188d6 - IFC: all inline content within a containing block flows together as continuous text
let own_wm = get_writing_mode(styled_dom, dom_id, &styled_node_state).unwrap_or_default();
let direction = get_direction(styled_dom, dom_id, &styled_node_state).unwrap_or_default();
let text_orientation = get_text_orientation(styled_dom, dom_id, &styled_node_state).unwrap_or_default();
let text_align = get_text_align(styled_dom, dom_id, &styled_node_state).unwrap_or_default();
// +spec:box-model:ec6466 - percentage margins/padding resolve to 0 when containing block is unknown (intrinsic sizing), breaking cyclic dependencies per css-sizing-3 §5.2.1
/// Collects box properties from the styled DOM and returns both unresolved and resolved forms.
// +spec:table-layout:038f9d - padding does not apply to table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column
// Non-cell internal table elements (rows, row groups, columns, column groups) do not have padding.
// CSS 2.2 §8.5.1: "Computed value: absolute length; '0' if the border style is 'none' or 'hidden'"
let style_zeroes_width = |s: BorderStyle| matches!(s, BorderStyle::None | BorderStyle::Hidden);
top: if style_zeroes_width(bs_top) { PixelValue::const_px(0) } else { to_pixel_value(border_top_mv) },
right: if style_zeroes_width(bs_right) { PixelValue::const_px(0) } else { to_pixel_value(border_right_mv) },
bottom: if style_zeroes_width(bs_bottom) { PixelValue::const_px(0) } else { to_pixel_value(border_bottom_mv) },
left: if style_zeroes_width(bs_left) { PixelValue::const_px(0) } else { to_pixel_value(border_left_mv) },
{ let _ = (0xC0_000007u32); } // after border block (incl is_normal/compact_cache fast-path)
// +spec:box-model:1197a5 - height property does not apply to non-replaced inline elements; vertical margins zeroed
"[BOX] node[{}] {:?} pad=[{:.1} {:.1} {:.1} {:.1}] mar=[{:.1} {:.1} {:.1} {:.1}] bor=[{:.1} {:.1} {:.1} {:.1}]",
resolved.padding.top, resolved.padding.right, resolved.padding.bottom, resolved.padding.left,
resolved.margin.top, resolved.margin.right, resolved.margin.bottom, resolved.margin.left,
resolved.border.top, resolved.border.right, resolved.border.bottom, resolved.border.left,
"NodeId {:?} ({:?}): unresolved_margin_top={:?}, resolved_margin_top={:.2}, viewport_size={:?}",
"NodeId {:?} ({:?}): margin_auto: left={}, right={}, top={}, bottom={} | margin_left={:?}",
// according to the 'white-space' property does not generate any anonymous inline boxes (CSS2§9.2.2.1)
/// Returns true if the given display type is a "proper table child" of a table/inline-table box.
// +spec:display-contents:95faa5 - blockification has no effect on none/contents (other => other)
// +spec:display-property:f68848 - Automatic box type transformations: blockification of computed display values
/// +spec:display-property:d50f70 - blockification affects computed values, determining principal box type only
// +spec:display-property:ee2d65 - blockification of inline-level display types (CSS Display 3 §2.7)
// +spec:display-property:285fe7 - block box establishing a BFC (block-level block container with new BFC)
fn establishes_new_block_formatting_context(styled_dom: &StyledDom, node_id: NodeId) -> bool {
LayoutDisplay::InlineBlock | LayoutDisplay::TableCell | LayoutDisplay::TableCaption | LayoutDisplay::FlowRoot
// CSS Writing Modes 4 § 3.2: block container with different writing-mode than parent establishes BFC
let parent_state = &styled_dom.styled_nodes.as_container()[parent_dom_id].styled_node_state;
let child_wm = get_writing_mode(styled_dom, node_id, &styled_node.styled_node_state).unwrap_or_default();
let parent_wm = get_writing_mode(styled_dom, parent_dom_id, parent_state).unwrap_or_default();
// +spec:replaced-elements:4f494d - replaced elements always establish an independent formatting context
// +spec:display-property:0d93f1 - maps display value to box generation (principal box, none, or contents)
/// Like `determine_formatting_context`, but uses an explicit (possibly blockified) display type
// +spec:display-property:80f43f - inner display type defines formatting context for non-replaced elements
// +spec:display-property:46e71c - Maps outer display (block/inline) and inner display (flow/flow-root/table/flex/grid) to FormattingContext
// +spec:display-property:aa582d - maps display types to formatting contexts (inline-level, block-level, atomic inline, block container)
// +spec:display-property:30a935 - outer display without inner defaults to flow (block/inline both use flow context)
// +spec:block-formatting-context:97b03b - flow-root always establishes a new BFC; block/list-item may establish one based on other conditions
// +spec:display-property:0bac26 - list-item limited to flow layout inner types (block/flow-root)
// +spec:display-property:7c49c1 - block container with only inline children establishes an IFC
// +spec:display-property:723fe8 - CSS 2.2 §17.2 table model: display types map to formatting contexts, table-column/column-group not rendered, anonymous table objects generated
// +spec:table-layout:023714 - map display values to table formatting contexts per CSS 2.2 §17.2
// +spec:table-layout:6c5039 - row-primary table model: rows/cells/captions/columns mapped here
// +spec:table-layout:75eea9 - display property values for table elements (table, tr, td, etc.)
// +spec:display-property:da3fc7 - display:none/contents generate no boxes (no inner/outer display types)
// +spec:display-contents:584072 - no special behavior for legend/HTML elements; contents handled normally
// +spec:display-property:b89b80 - run-in box falls back to block (merging into next block not implemented)
// +spec:display-property:7d77f5 - run-in treated as block (run-in sequencing fixup not yet implemented)
// +spec:display-property:0c30c4 - run-in boxes fall back to block (run-in reparenting not implemented, matches browser behavior)
// +spec:display-property:2f5c52 - run-in treated as block (full run-in merging not implemented)
fn determine_formatting_context(styled_dom: &StyledDom, node_id: NodeId) -> FormattingContext {