Separate contained grid contributions from stretch

This commit is contained in:
SGavrl committed 2026-07-31 00:34:56 +00:00
1 parent fadf4a7ac8
commit ef4be1b5d9
5 files changed
+148 -26

No files matched your search

+53 -26
View File
@@ -5367,28 +5367,42 @@ fn effective_container_type(style: &crate::LayoutStyle) -> crate::ContainerType
}
}
/// Does this auto inline-size use shrink-to-fit/content sizing rather than
/// filling a definite containing block?
fn container_auto_inline_size_is_intrinsic(
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ContainerAutoInlineSize {
FillAvailable,
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.
fn container_auto_inline_size(
tree: &DomTree,
id: NodeId,
style: &crate::LayoutStyle,
styles: &HashMap<NodeId, crate::LayoutStyle>,
) -> bool {
) -> ContainerAutoInlineSize {
if style.is_inline_block || style.float.is_some() {
return true;
return ContainerAutoInlineSize::Intrinsic;
}
if matches!(style.position, Some(taffy::Position::Absolute)) {
// An absolutely positioned auto width is fill-available only when
// both inline insets are definite.
return style.inset[1].is_none() || style.inset[3].is_none();
return if style.inset[1].is_none() || style.inset[3].is_none() {
ContainerAutoInlineSize::Intrinsic
} else {
ContainerAutoInlineSize::FillAvailable
};
}
let mut parent = tree.get_node(id).and_then(|node| node.parent);
let parent_style = loop {
let Some(parent_id) = parent else { return false };
let Some(parent_id) = parent else {
return ContainerAutoInlineSize::FillAvailable;
};
let Some(parent_style) = styles.get(&parent_id) else {
return false;
return ContainerAutoInlineSize::FillAvailable;
};
if parent_style.display_contents {
parent = tree.get_node(parent_id).and_then(|node| node.parent);
@@ -5406,30 +5420,38 @@ fn container_auto_inline_size_is_intrinsic(
)
);
if row {
true
ContainerAutoInlineSize::Intrinsic
} else if style
.align_self
.unwrap_or(
parent_style
.align_items
.unwrap_or(taffy::AlignItems::STRETCH),
)
== taffy::AlignSelf::STRETCH
{
ContainerAutoInlineSize::FillAvailable
} else {
style
.align_self
.unwrap_or(
parent_style
.align_items
.unwrap_or(taffy::AlignItems::STRETCH),
)
!= taffy::AlignSelf::STRETCH
ContainerAutoInlineSize::Intrinsic
}
}
crate::Display::Grid => {
style
if style
.justify_self
.unwrap_or(
parent_style
.justify_items
.unwrap_or(taffy::JustifyItems::STRETCH),
)
!= taffy::AlignSelf::STRETCH
== taffy::AlignSelf::STRETCH
{
ContainerAutoInlineSize::StretchedGridItem
} else {
ContainerAutoInlineSize::Intrinsic
}
}
crate::Display::Inline => true,
_ => false,
crate::Display::Inline => ContainerAutoInlineSize::Intrinsic,
_ => ContainerAutoInlineSize::FillAvailable,
}
}
@@ -5458,11 +5480,16 @@ fn apply_container_size_containment(
}
let ratio_transfers_inline_size =
style.aspect_ratio.is_some() && !matches!(style.height, crate::Dimension::Auto);
if matches!(style.width, crate::Dimension::Auto)
&& !ratio_transfers_inline_size
&& container_auto_inline_size_is_intrinsic(tree, id, style, styles)
{
taffy_style.size.width = taffy::Dimension::length(0.0);
if matches!(style.width, crate::Dimension::Auto) && !ratio_transfers_inline_size {
match container_auto_inline_size(tree, id, style, styles) {
ContainerAutoInlineSize::Intrinsic => {
taffy_style.size.width = taffy::Dimension::length(0.0);
}
ContainerAutoInlineSize::StretchedGridItem => {
taffy_style.intrinsic_size_containment.width = true;
}
ContainerAutoInlineSize::FillAvailable => {}
}
}
if kind == crate::ContainerType::Size {
@@ -1090,6 +1090,63 @@ fn item_self_alignment_places_flex_and_grid_items() {
);
}
#[test]
fn stretched_inline_size_container_has_zero_intrinsic_grid_contribution() {
// Chromium 140: size containment affects intrinsic track sizing, but not
// the item's later stretch alignment. In the first case the grid therefore
// shrink-wraps to zero. In the definite-width control the contained item
// contributes zero, then stretches to the 50px left after the fixed track.
// Its 240px child remains visible overflow in both cases.
let tree = parse_html(
r#"
<style>
html, body { margin: 0 }
#shrink-host { display: flex; width: 600px }
.grid { display: grid }
.item { container-type: inline-size }
.child { width: 240px; height: 20px }
#fixed-grid {
display: grid;
width: 150px;
grid-template-columns: auto 100px;
}
#fixed { height: 20px }
</style>
<div id="shrink-host">
<div id="shrink-grid" class="grid">
<div id="shrink-item" class="item"><div id="shrink-child" class="child"></div></div>
</div>
</div>
<div id="fixed-grid" class="grid">
<div id="fixed-item" class="item"><div id="fixed-child" class="child"></div></div>
<div id="fixed"></div>
</div>
"#,
);
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");
let fixed = rect("fixed");
assert!(
shrink_grid.width.abs() < 0.01
&& shrink_item.width.abs() < 0.01
&& (shrink_child.width - 240.0).abs() < 0.01
&& (fixed_grid.width - 150.0).abs() < 0.01
&& (fixed_item.width - 50.0).abs() < 0.01
&& (fixed_child.width - 240.0).abs() < 0.01
&& (fixed.x - 50.0).abs() < 0.01
&& (fixed.width - 100.0).abs() < 0.01,
"contained intrinsic contribution leaked into the grid track: \
shrink=({shrink_grid:?}, {shrink_item:?}, {shrink_child:?}) \
fixed=({fixed_grid:?}, {fixed_item:?}, {fixed_child:?}, {fixed:?})"
);
}
#[test]
fn non_stretched_auto_grid_item_shrink_wraps_inside_grid_area() {
let tree = parse_html(