Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ The MSRV for this release is 1.71.

A large number of miscelaneous bug fixes are included in this release:

- Flexbox: clamp the content size suggestion used for the automatic minimum size of an aspect-ratio item by min/max cross sizes transferred through the aspect ratio, per [css-sizing-3 §5.1](https://www.w3.org/TR/css-sizing-3/#min-content-zero); previously only the transferred max size was applied
- Flexbox: re-derive the used cross size of an item whose cross size is transferred from its main size through its aspect ratio from the used (post-flexing) main size, instead of transferring it from the flex base size before flexing
- Flexbox: clamp the cross-axis available space by the item's own cross-axis margins rather than the container's margins when sizing flex items; previously a container margin could inflate a stretched item's cross size beyond the container
- Block/float: floated flex and grid containers with `width: auto` are now shrink-to-fit (fit-content) sized; previously they treated the definite available space as stretch-fit
- Flexbox: clamp the flex base size, automatic minimum size and hypothetical main/cross sizes with min/max sizes transferred through the aspect ratio, instead of baking them into the item's used min/max sizes (#989)
- Flexbox: resolve `justify-content: start`/`end` and `align-self: start`/`end`/`self-start`/`self-end` as writing-mode relative (rather than flex-relative) in the static position of absolutely positioned children; use the flex-relative start for `justify-content: space-between`/`normal`, and a fallback of `start` for `align-self: baseline` (#1072)
- Flexbox: only let `auto` margins on absolutely positioned children absorb free space when the box is inset-constrained in that axis; otherwise they resolve to zero per CSS2 §10.3.7/§10.6.4 (#1072)
Expand Down
30 changes: 28 additions & 2 deletions src/compute/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -951,11 +951,37 @@ fn perform_final_layout_on_in_flow_children(
has_active_floats = true;

// A float with `width: auto` is shrink-to-fit (fit-content) sized: the available
// space clamped between its min-content and max-content sizes.
// space clamped between its min-content and max-content sizes. Block containers
// implement this internally when laid out with definite available space and no
// known width, but other layout modes (e.g. a floated flex or grid container)
// treat definite available space as stretch-fit, so compute the fit-content
// width up front and pass it as a known dimension.
let available_width = container_inner_width - item_non_auto_x_margin_sum;
let item_known_width = item.size.maybe_clamp(item.min_size, item.max_size).width.or_else(|| {
let min_content_width = tree.measure_child_size(
item.node_id,
Size::NONE,
parent_size,
Size { width: AvailableSpace::MinContent, height: AvailableSpace::MaxContent },
SizingMode::InherentSize,
crate::AbsoluteAxis::Horizontal,
Line::FALSE,
);
let max_content_width = tree.measure_child_size(
item.node_id,
Size::NONE,
parent_size,
Size { width: AvailableSpace::MaxContent, height: AvailableSpace::MaxContent },
SizingMode::InherentSize,
crate::AbsoluteAxis::Horizontal,
Line::FALSE,
);
Some(available_width.min(max_content_width).max(min_content_width))
.maybe_clamp(item.min_size.width, item.max_size.width)
});
let item_layout = tree.perform_child_layout(
item.node_id,
Size::NONE,
Size { width: item_known_width, height: None },
parent_size,
Size { width: AvailableSpace::Definite(available_width), height: AvailableSpace::MaxContent },
SizingMode::InherentSize,
Expand Down
54 changes: 45 additions & 9 deletions src/compute/flexbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ struct FlexItem {
max_size: Size<Option<f32>>,
/// The aspect ratio of this item
aspect_ratio: Option<f32>,
/// Whether the item's cross size is transferred from its main size through the aspect ratio
/// (i.e. the cross size property is `auto` and the item has an aspect ratio)
cross_size_is_transferred: bool,
/// The box sizing mode of this item
box_sizing: BoxSizing,
/// The cross-alignment of this item
align_self: AlignSelf,

Expand Down Expand Up @@ -536,14 +541,14 @@ fn generate_anonymous_flex_items(
let pb_sum = (padding + border).sum_axes();
let box_sizing_adjustment =
if child_style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO };
let raw_size =
child_style.size().maybe_resolve(constants.node_inner_size, |val, basis| tree.calc(val, basis));
FlexItem {
node: child,
order: index as u32,
size: child_style
.size()
.maybe_resolve(constants.node_inner_size, |val, basis| tree.calc(val, basis))
.maybe_apply_aspect_ratio(aspect_ratio)
.maybe_add(box_sizing_adjustment),
size: raw_size.maybe_apply_aspect_ratio(aspect_ratio).maybe_add(box_sizing_adjustment),
cross_size_is_transferred: aspect_ratio.is_some() && raw_size.cross(constants.dir).is_none(),
box_sizing: child_style.box_sizing(),
min_size: child_style
.min_size()
.maybe_resolve(constants.node_inner_size, |val, basis| tree.calc(val, basis))
Expand Down Expand Up @@ -680,7 +685,7 @@ fn determine_flex_base_size(
// Available space for child sizing
// Min/max sizes transferred through the aspect ratio are taken into account here
// https://github.com/w3c/csswg-drafts/issues/10997
let cross_axis_margin_sum = constants.margin.cross_axis_sum(dir);
let cross_axis_margin_sum = child.margin.cross_axis_sum(dir);
let transferred_min_size = child.min_size.maybe_apply_aspect_ratio(child.aspect_ratio);
let transferred_max_size = child.max_size.maybe_apply_aspect_ratio(child.aspect_ratio);
let child_min_cross = transferred_min_size.cross(dir).maybe_add(cross_axis_margin_sum);
Expand Down Expand Up @@ -848,8 +853,12 @@ fn determine_flex_base_size(

// 4.5. Automatic Minimum Size of Flex Items
// https://www.w3.org/TR/css-flexbox-1/#min-size-auto
let clamped_min_content_size =
min_content_main_size.maybe_min(child.size.main(dir)).maybe_min(transferred_max_size.main(dir));
// If the item has an aspect ratio, the content size suggestion is clamped by
// min/max cross sizes transferred through the aspect ratio.
let clamped_min_content_size = min_content_main_size
.maybe_clamp(transferred_min_size.main(dir), transferred_max_size.main(dir))
.maybe_min(child.size.main(dir))
.maybe_min(transferred_max_size.main(dir));
clamped_min_content_size.maybe_max(padding_border_axes_sums.main(dir))
});

Expand Down Expand Up @@ -1068,7 +1077,7 @@ fn determine_container_main_size(
let cross_axis_parent_size = constants.node_inner_size.cross(dir);

// Available space for child sizing
let cross_axis_margin_sum = constants.margin.cross_axis_sum(dir);
let cross_axis_margin_sum = item.margin.cross_axis_sum(dir);
let child_min_cross = item.min_size.cross(dir).maybe_add(cross_axis_margin_sum);
let child_max_cross = item.max_size.cross(dir).maybe_add(cross_axis_margin_sum);
let cross_axis_available_space: AvailableSpace = available_space
Expand Down Expand Up @@ -1677,6 +1686,33 @@ fn determine_used_cross_size(
child.min_size.cross(constants.dir),
max_size_ignoring_aspect_ratio.cross(constants.dir),
)
} else if child.cross_size_is_transferred {
// If the cross size is transferred from the main size through the aspect ratio
// then it is re-derived from the used (target) main size rather than taken from
// the hypothetical cross size, which was transferred from the flex base size
// before flexing.
let box_sizing_adjustment = if child.box_sizing == BoxSizing::ContentBox {
(child.padding + child.border).sum_axes()
} else {
Size::ZERO
};
let transferred_min_cross =
child.min_size.maybe_apply_aspect_ratio(child.aspect_ratio).cross(constants.dir);
let transferred_max_cross =
child.max_size.maybe_apply_aspect_ratio(child.aspect_ratio).cross(constants.dir);
let padding_border_sum =
child.padding.cross_axis_sum(constants.dir) + child.border.cross_axis_sum(constants.dir);
Size::NONE
.with_main(
constants.dir,
Some(child.target_size.main(constants.dir) - box_sizing_adjustment.main(constants.dir)),
)
.maybe_apply_aspect_ratio(child.aspect_ratio)
.maybe_add(box_sizing_adjustment)
.cross(constants.dir)
.maybe_clamp(transferred_min_cross, transferred_max_cross)
.unwrap_or(0.0)
.max(padding_border_sum)
} else {
child.hypothetical_inner_size.cross(constants.dir)
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>

<div id="test-root" style="display: flex; flex-direction: column; align-items: start; max-width: 34px; height: 1px;">
<div style="height: 100px; min-width: 30px; aspect-ratio: 1;"></div>
</div>

</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>

<div id="test-root" style="display: block; width: 400px; height: 200px;">
<div style="display: flex; flex-direction: column; align-items: stretch; float: left; max-width: 34px; height: 1px; margin-right: 2px;">
<div style="height: 5px; min-width: 30px;"></div>
</div>
</div>

</body>
</html>
20 changes: 20 additions & 0 deletions test_fixtures/float/float_flex_container_shrink_to_fit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>

<div id="test-root" style="display: block; width: 400px; height: 200px;">
<div style="display: flex; float: left;">
<div style="width: 50px; height: 30px;"></div>
<div style="width: 40px; height: 20px;"></div>
</div>
</div>

</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<test name="aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="ltr" flex-direction="column" align-items="start" height="1px" max-width="34px">
<div direction="ltr" height="100px" min-width="30px" aspect-ratio="1"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="34" height="1">
<node x="0" y="0" width="30" height="30"/>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<test name="aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="rtl" flex-direction="column" align-items="start" height="1px" max-width="34px">
<div direction="rtl" height="100px" min-width="30px" aspect-ratio="1"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="34" height="1">
<node x="4" y="0" width="30" height="30"/>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<test name="aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="ltr" flex-direction="column" align-items="start" height="1px" max-width="34px">
<div box-sizing="content-box" direction="ltr" height="100px" min-width="30px" aspect-ratio="1"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="34" height="1">
<node x="0" y="0" width="30" height="30"/>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<test name="aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="rtl" flex-direction="column" align-items="start" height="1px" max-width="34px">
<div box-sizing="content-box" direction="rtl" height="100px" min-width="30px" aspect-ratio="1"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="34" height="1">
<node x="4" y="0" width="30" height="30"/>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<test name="float_flex_container_max_width_margin_stretch_item__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="block" direction="ltr" width="400px" height="200px">
<div display="flex" direction="ltr" float="left" flex-direction="column" align-items="stretch" height="1px" max-width="34px" margin-right="2px">
<div direction="ltr" height="5px" min-width="30px"/>
</div>
</div>
</input>
<expectations>
<node x="0" y="0" width="400" height="200">
<node x="0" y="0" width="30" height="1">
<node x="0" y="0" width="30" height="1"/>
</node>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<test name="float_flex_container_max_width_margin_stretch_item__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="block" direction="rtl" width="400px" height="200px">
<div display="flex" direction="rtl" float="left" flex-direction="column" align-items="stretch" height="1px" max-width="34px" margin-right="2px">
<div direction="rtl" height="5px" min-width="30px"/>
</div>
</div>
</input>
<expectations>
<node x="0" y="0" width="400" height="200">
<node x="0" y="0" width="30" height="1">
<node x="0" y="0" width="30" height="1"/>
</node>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<test name="float_flex_container_max_width_margin_stretch_item__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="block" box-sizing="content-box" direction="ltr" width="400px" height="200px">
<div display="flex" box-sizing="content-box" direction="ltr" float="left" flex-direction="column" align-items="stretch" height="1px" max-width="34px" margin-right="2px">
<div box-sizing="content-box" direction="ltr" height="5px" min-width="30px"/>
</div>
</div>
</input>
<expectations>
<node x="0" y="0" width="400" height="200">
<node x="0" y="0" width="30" height="1">
<node x="0" y="0" width="30" height="1"/>
</node>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<test name="float_flex_container_max_width_margin_stretch_item__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="block" box-sizing="content-box" direction="rtl" width="400px" height="200px">
<div display="flex" box-sizing="content-box" direction="rtl" float="left" flex-direction="column" align-items="stretch" height="1px" max-width="34px" margin-right="2px">
<div box-sizing="content-box" direction="rtl" height="5px" min-width="30px"/>
</div>
</div>
</input>
<expectations>
<node x="0" y="0" width="400" height="200">
<node x="0" y="0" width="30" height="1">
<node x="0" y="0" width="30" height="1"/>
</node>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<test name="float_flex_container_shrink_to_fit__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="block" direction="ltr" width="400px" height="200px">
<div display="flex" direction="ltr" float="left">
<div direction="ltr" width="50px" height="30px"/>
<div direction="ltr" width="40px" height="20px"/>
</div>
</div>
</input>
<expectations>
<node x="0" y="0" width="400" height="200">
<node x="0" y="0" width="90" height="30">
<node x="0" y="0" width="50" height="30"/>
<node x="50" y="0" width="40" height="20"/>
</node>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<test name="float_flex_container_shrink_to_fit__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="block" direction="rtl" width="400px" height="200px">
<div display="flex" direction="rtl" float="left">
<div direction="rtl" width="50px" height="30px"/>
<div direction="rtl" width="40px" height="20px"/>
</div>
</div>
</input>
<expectations>
<node x="0" y="0" width="400" height="200">
<node x="0" y="0" width="90" height="30">
<node x="40" y="0" width="50" height="30"/>
<node x="0" y="0" width="40" height="20"/>
</node>
</node>
</expectations>
</test>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<test name="float_flex_container_shrink_to_fit__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="block" box-sizing="content-box" direction="ltr" width="400px" height="200px">
<div display="flex" box-sizing="content-box" direction="ltr" float="left">
<div box-sizing="content-box" direction="ltr" width="50px" height="30px"/>
<div box-sizing="content-box" direction="ltr" width="40px" height="20px"/>
</div>
</div>
</input>
<expectations>
<node x="0" y="0" width="400" height="200">
<node x="0" y="0" width="90" height="30">
<node x="0" y="0" width="50" height="30"/>
<node x="50" y="0" width="40" height="20"/>
</node>
</node>
</expectations>
</test>
Loading