Use formatting-context atomic baselines

This commit is contained in:
ldm0
2026-08-11 19:33:01 +08:00
committed by Donough Liu
parent d785c20317
commit 8a175047a8
2 changed files with 99 additions and 9 deletions
+19 -8
View File
@@ -1972,15 +1972,26 @@ where
fn atomic_inline_baseline(&self, id: LayoutBoxId, output: LayoutOutput) -> Option<f32> {
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(
+80 -1
View File
@@ -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| {