From 8a175047a832887f12e4c898dec64fd15cc8731e Mon Sep 17 00:00:00 2001 From: ldm0 Date: Tue, 11 Aug 2026 18:28:17 +0800 Subject: [PATCH] Use formatting-context atomic baselines --- moli-layout/src/taffy_tree.rs | 27 +++++-- moli-layout/tests/phase4_layout_contract.rs | 81 ++++++++++++++++++++- 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/moli-layout/src/taffy_tree.rs b/moli-layout/src/taffy_tree.rs index de9487c69..66d8f8fd6 100644 --- a/moli-layout/src/taffy_tree.rs +++ b/moli-layout/src/taffy_tree.rs @@ -1972,15 +1972,26 @@ where fn atomic_inline_baseline(&self, id: LayoutBoxId, output: LayoutOutput) -> Option { let layout_box = &self.boxes[id.index()]; - if !matches!( - layout_box.style.display(), - crate::LayoutDisplay::InlineBlock | crate::LayoutDisplay::InlineListItem - ) || layout_box.style.taffy.overflow.x != taffy::Overflow::Visible - || layout_box.style.taffy.overflow.y != taffy::Overflow::Visible - { - return None; + match layout_box.style.display() { + // Blink's block layout marks these atomic fragments to use their + // last baseline. A scrolling inline-block instead forces baseline + // synthesis from its margin-box edge. + crate::LayoutDisplay::InlineBlock | crate::LayoutDisplay::InlineListItem => { + (layout_box.style.taffy.overflow.x == taffy::Overflow::Visible + && layout_box.style.taffy.overflow.y == taffy::Overflow::Visible) + .then_some(output.last_baselines.y) + .flatten() + } + // Flex, grid, and table formatting contexts expose their first + // baseline as the automatic inline-level baseline. Do not apply + // the inline-block overflow exception to these fragment types. + crate::LayoutDisplay::InlineFlex + | crate::LayoutDisplay::InlineGrid + | crate::LayoutDisplay::InlineTable => output.first_baselines.y, + // Replaced and other atomic inline-level boxes synthesize their + // baseline at the appropriate box edge in the caller. + _ => None, } - output.last_baselines.y } fn break_inline_lines_with_floats( diff --git a/moli-layout/tests/phase4_layout_contract.rs b/moli-layout/tests/phase4_layout_contract.rs index 72fcc42d3..51cc0aa61 100644 --- a/moli-layout/tests/phase4_layout_contract.rs +++ b/moli-layout/tests/phase4_layout_contract.rs @@ -11,7 +11,9 @@ use moli_layout::{ build_screenshot_snapshot, }; use style::Atom; -use taffy::{BoxSizing, Clear, Dimension, Float, Overflow, Point, Rect, Size, Style}; +use taffy::{ + BoxSizing, Clear, Dimension, FlexDirection, Float, Overflow, Point, Rect, Size, Style, +}; const RED: PaintColor = PaintColor::new(0.9, 0.1, 0.1, 1.0); const GREEN: PaintColor = PaintColor::new(0.1, 0.7, 0.2, 1.0); @@ -1042,6 +1044,83 @@ fn inline_blocks_use_their_internal_last_line_baseline_and_overflow_fallback() { assert_close(rect(&fallback, BLUE).y, 19.0); } +#[test] +fn inline_flex_and_grid_use_their_first_container_baseline() { + for atomic_display in [LayoutDisplay::InlineFlex, LayoutDisplay::InlineGrid] { + let source = Source(vec![ + Node::element( + "root", + "div", + LayoutElementCategory::Generic, + None, + vec![1, 4], + ), + Node::element( + "atomic", + "span", + LayoutElementCategory::Generic, + None, + vec![2, 3], + ), + Node::element( + "first-item", + "span", + LayoutElementCategory::Generic, + None, + Vec::new(), + ), + Node::element( + "last-item", + "span", + LayoutElementCategory::Generic, + None, + Vec::new(), + ), + Node::element( + "tail", + "span", + LayoutElementCategory::Generic, + None, + Vec::new(), + ), + ]); + let mut styles = Styles::default(); + styles.primary.insert( + 0, + style(LayoutDisplay::Block, PaintColor::TRANSPARENT) + .tap_taffy(|taffy| taffy.size.width = Dimension::length(100.0)) + .with_text_metrics(0.0, 0.0), + ); + styles.primary.insert( + 1, + style(atomic_display, YELLOW).tap_taffy(|taffy| { + taffy.size.width = Dimension::length(10.0); + if atomic_display == LayoutDisplay::InlineFlex { + taffy.flex_direction = FlexDirection::Column; + } else { + taffy.grid_template_columns = vec![taffy::style_helpers::length(10.0)]; + } + }), + ); + styles + .primary + .insert(2, sized(LayoutDisplay::Block, 10.0, 10.0, GREEN)); + styles + .primary + .insert(3, sized(LayoutDisplay::Block, 10.0, 10.0, BLUE)); + styles + .primary + .insert(4, sized(LayoutDisplay::InlineBlock, 10.0, 10.0, RED)); + + let snapshot = render(&source, &mut styles, 100, 60); + let atomic = rect(&snapshot, YELLOW); + let tail = rect(&snapshot, RED); + assert_close(atomic.width, 10.0); + assert_close(atomic.height, 20.0); + assert_close(tail.y, atomic.y); + } +} + #[test] fn inline_block_propagates_scroll_block_end_baseline_through_block_children() { let render_variant = |inner_display, inner_overflow, margin_bottom| {