diff --git a/crates/obscura-render/src/dom.rs b/crates/obscura-render/src/dom.rs index 0223777..a4d124f 100644 --- a/crates/obscura-render/src/dom.rs +++ b/crates/obscura-render/src/dom.rs @@ -5374,6 +5374,12 @@ enum ContainerAutoInlineSize { StretchedGridItem, } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum ContainerAutoBlockSize { + Intrinsic, + StretchedGridItem, +} + /// Classify how an auto inline-size is resolved. Stretched grid items need /// their intrinsic contribution contained without replacing the final auto /// size that stretch alignment consumes. @@ -5455,6 +5461,55 @@ fn container_auto_inline_size( } } +/// Classify an auto block-size for size containment. A stretched grid item +/// must keep its auto size so final alignment can fill a definite grid area; +/// only its intrinsic track contribution is contained. +fn container_auto_block_size( + tree: &DomTree, + id: NodeId, + style: &crate::LayoutStyle, + styles: &HashMap, +) -> ContainerAutoBlockSize { + // Absolutely positioned descendants are not grid items. Preserve the + // existing contained intrinsic-height behavior until their independent + // inset-based fill sizing is modeled explicitly. + if matches!(style.position, Some(taffy::Position::Absolute)) { + return ContainerAutoBlockSize::Intrinsic; + } + + let mut parent = tree.get_node(id).and_then(|node| node.parent); + let parent_style = loop { + let Some(parent_id) = parent else { + return ContainerAutoBlockSize::Intrinsic; + }; + let Some(parent_style) = styles.get(&parent_id) else { + return ContainerAutoBlockSize::Intrinsic; + }; + if parent_style.display_contents { + parent = tree.get_node(parent_id).and_then(|node| node.parent); + continue; + } + break parent_style; + }; + + if parent_style.display == crate::Display::Grid + && style + .align_self + .unwrap_or( + parent_style + .align_items + .unwrap_or(taffy::AlignItems::STRETCH), + ) + == taffy::AlignSelf::STRETCH + && !style.margin_auto[0] + && !style.margin_auto[2] + { + ContainerAutoBlockSize::StretchedGridItem + } else { + ContainerAutoBlockSize::Intrinsic + } +} + /// Apply the used-size part of `container-type`'s implicit size containment. /// /// The default contain-intrinsic-size is zero. Fill-available block boxes keep @@ -5499,7 +5554,14 @@ fn apply_container_size_containment( let ratio_transfers_block_size = style.aspect_ratio.is_some() && !matches!(style.width, crate::Dimension::Auto); if matches!(style.height, crate::Dimension::Auto) && !ratio_transfers_block_size { - taffy_style.size.height = taffy::Dimension::length(0.0); + match container_auto_block_size(tree, id, style, styles) { + ContainerAutoBlockSize::Intrinsic => { + taffy_style.size.height = taffy::Dimension::length(0.0); + } + ContainerAutoBlockSize::StretchedGridItem => { + taffy_style.intrinsic_size_containment.height = true; + } + } } } } diff --git a/crates/obscura-render/tests/layout_test.rs b/crates/obscura-render/tests/layout_test.rs index cc0f32a..6a527f5 100644 --- a/crates/obscura-render/tests/layout_test.rs +++ b/crates/obscura-render/tests/layout_test.rs @@ -1147,6 +1147,63 @@ fn stretched_inline_size_container_has_zero_intrinsic_grid_contribution() { ); } +#[test] +fn stretched_size_container_has_zero_intrinsic_grid_row_contribution() { + // Chromium 140: block-axis size containment removes the tall child's + // intrinsic row contribution, but leaves the grid item's auto height + // available for final stretch alignment. Padding and border remain inside + // the fixed row's stretched 100px border box. + let tree = parse_html( + r#" + +
+
+
+
+
+
+
+
+
+
+ "#, + ); + let layout = layout_dom(&tree, (800.0, 600.0)); + let rect = |id| layout.rects[&tree.get_element_by_id(id).unwrap()]; + let shrink_grid = rect("shrink-grid"); + let shrink_item = rect("shrink-item"); + let shrink_child = rect("shrink-child"); + let fixed_grid = rect("fixed-grid"); + let fixed_item = rect("fixed-item"); + let fixed_child = rect("fixed-child"); + assert!( + shrink_grid.height.abs() < 0.01 + && shrink_item.height.abs() < 0.01 + && (shrink_child.height - 240.0).abs() < 0.01 + && (fixed_grid.height - 100.0).abs() < 0.01 + && (fixed_item.height - 100.0).abs() < 0.01 + && (fixed_child.y - fixed_item.y - 15.0).abs() < 0.01 + && (fixed_child.height - 240.0).abs() < 0.01, + "contained intrinsic contribution leaked into the grid row: \ + shrink=({shrink_grid:?}, {shrink_item:?}, {shrink_child:?}) \ + fixed=({fixed_grid:?}, {fixed_item:?}, {fixed_child:?})" + ); +} + #[test] fn non_stretched_auto_grid_item_shrink_wraps_inside_grid_area() { let tree = parse_html(