Fix various issues with replaced elements in flex layout (#33263)

In particular, this takes into account that flex items may be stretched,
and if they have an aspect ratio, we ma6y need to convert the stretched
size through the ratio.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Oriol Brufau 2024-08-31 01:39:18 +02:00 committed by GitHub
parent 4ae2610c24
commit 3acc9edd82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 334 additions and 481 deletions

View file

@ -14,6 +14,7 @@ use style::properties::longhands::align_items::computed_value::T as AlignItems;
use style::properties::longhands::align_self::computed_value::T as AlignSelf;
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
use style::properties::ComputedValues;
use style::values::computed::length::Size;
use style::values::computed::Length;
use style::values::generics::flex::GenericFlexBasis as FlexBasis;
@ -21,9 +22,7 @@ use style::values::generics::length::{GenericLengthPercentageOrAuto, LengthPerce
use style::values::specified::align::AlignFlags;
use style::Zero;
use super::geom::{
FlexAxis, FlexRelativeRect, FlexRelativeSides, FlexRelativeVec2, MainStartCrossStart,
};
use super::geom::{FlexAxis, FlexRelativeRect, FlexRelativeSides, FlexRelativeVec2};
use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
@ -244,6 +243,32 @@ struct FinalFlexLineLayout {
all_baselines: Baselines,
}
impl FlexContainerConfig {
fn align_for(&self, align_self: AlignSelf) -> AlignItems {
let value = align_self.0 .0.value();
let mapped_value = match value {
AlignFlags::AUTO | AlignFlags::NORMAL => self.align_items.0,
_ => value,
};
AlignItems(mapped_value)
}
/// Whether an item with an `auto` preferred cross size needs to be stretched
/// to fill the flex container.
/// <https://drafts.csswg.org/css-flexbox/#stretched>
fn item_with_auto_cross_size_stretches_to_container_size(
&self,
item_style: &ComputedValues,
item_margin: &FlexRelativeSides<AuOrAuto>,
) -> bool {
self.container_is_single_line &&
item_with_auto_cross_size_stretches_to_line_size(
self.align_for(item_style.clone_align_self()),
item_margin,
)
}
}
impl FlexContext<'_> {
fn vec2_to_flex_relative<T>(&self, x: LogicalVec2<T>) -> FlexRelativeVec2<T> {
self.config.flex_axis.vec2_to_flex_relative(x)
@ -275,12 +300,7 @@ impl FlexContext<'_> {
}
fn align_for(&self, align_self: AlignSelf) -> AlignItems {
let value = align_self.0 .0.value();
let mapped_value = match value {
AlignFlags::AUTO | AlignFlags::NORMAL => self.config.align_items.0,
_ => value,
};
AlignItems(mapped_value)
self.config.align_for(align_self)
}
}
@ -388,8 +408,7 @@ impl FlexContainer {
layout_context,
containing_block_for_children,
container_is_horizontal,
self.config.flex_axis,
self.config.main_start_cross_start_sides_are,
&self.config,
&flex_context_getter,
);
@ -950,6 +969,20 @@ impl<'a> FlexItem<'a> {
.content_min_box_size(containing_block, &pbm)
.map(|v| v.map(Au::from));
let margin_auto_is_zero = flex_context.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
let padding = flex_context.sides_to_flex_relative(pbm.padding);
let border = flex_context.sides_to_flex_relative(pbm.border);
let margin = flex_context.sides_to_flex_relative(pbm.margin);
let padding_border = padding.sum_by_axis() + border.sum_by_axis();
let pbm_auto_is_zero = FlexRelativeVec2 {
main: padding_border.main,
cross: padding_border.cross,
} + margin_auto_is_zero.sum_by_axis();
let item_with_auto_cross_size_stretches_to_container_size = flex_context
.config
.item_with_auto_cross_size_stretches_to_container_size(&box_.style(), &margin);
let flex_relative_content_box_size = flex_context.vec2_to_flex_relative(content_box_size);
let flex_relative_content_max_size = flex_context.vec2_to_flex_relative(max_size);
let flex_relative_content_min_size = flex_context.vec2_to_flex_relative(min_size);
@ -962,6 +995,8 @@ impl<'a> FlexItem<'a> {
flex_relative_content_box_size,
flex_relative_content_min_size,
flex_relative_content_max_size,
&pbm_auto_is_zero,
item_with_auto_cross_size_stretches_to_container_size,
|item| {
let min_size_auto_is_zero = min_size.auto_is(Au::zero);
@ -971,6 +1006,7 @@ impl<'a> FlexItem<'a> {
content_box_size,
min_size_auto_is_zero,
max_size,
item_with_auto_cross_size_stretches_to_container_size,
IntrinsicSizingMode::Size,
)
},
@ -979,15 +1015,6 @@ impl<'a> FlexItem<'a> {
cross: flex_relative_content_min_size.cross.auto_is(Au::zero),
};
let margin_auto_is_zero = flex_context.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
let padding = flex_context.sides_to_flex_relative(pbm.padding);
let border = flex_context.sides_to_flex_relative(pbm.border);
let padding_border = padding.sum_by_axis() + border.sum_by_axis();
let pbm_auto_is_zero = FlexRelativeVec2 {
main: padding_border.main,
cross: padding_border.cross,
} + margin_auto_is_zero.sum_by_axis();
let align_self = flex_context.align_for(box_.style().clone_align_self());
let (flex_base_size, flex_base_size_is_definite) = box_.flex_base_size(
@ -999,6 +1026,8 @@ impl<'a> FlexItem<'a> {
flex_relative_content_min_size,
flex_relative_content_max_size,
padding_border,
&pbm_auto_is_zero,
item_with_auto_cross_size_stretches_to_container_size,
|item| {
let min_size = flex_context
.config
@ -1010,6 +1039,7 @@ impl<'a> FlexItem<'a> {
content_box_size,
min_size,
max_size,
item_with_auto_cross_size_stretches_to_container_size,
IntrinsicSizingMode::Size,
)
},
@ -1036,6 +1066,11 @@ impl<'a> FlexItem<'a> {
align_self,
}
}
fn stretches(&self) -> bool {
self.content_box_size.cross.is_auto() &&
item_with_auto_cross_size_stretches_to_line_size(self.align_self, &self.margin)
}
}
fn cross_axis_is_item_block_axis(
@ -1049,6 +1084,17 @@ fn cross_axis_is_item_block_axis(
container_is_row ^ item_is_orthogonal
}
/// Whether an item with an `auto` preferred cross size will stretched to fill the cross size of its flex line.
/// <https://drafts.csswg.org/css-flexbox/#stretched>
fn item_with_auto_cross_size_stretches_to_line_size(
align_self: AlignItems,
margin: &FlexRelativeSides<AuOrAuto>,
) -> bool {
align_self.0.value() == AlignFlags::STRETCH &&
!margin.cross_start.is_auto() &&
!margin.cross_end.is_auto()
}
// “Collect flex items into flex lines”
// https://drafts.csswg.org/css-flexbox/#algo-line-break
fn do_initial_flex_line_layout<'items>(
@ -1411,11 +1457,8 @@ impl InitialFlexLineLayout<'_> {
self.item_layout_results.iter_mut(),
self.item_used_main_sizes.iter(),
) {
let has_stretch = item.align_self.0.value() == AlignFlags::STRETCH;
let used_cross_size = if has_stretch &&
item.content_box_size.cross.is_auto() &&
!(item.margin.cross_start.is_auto() || item.margin.cross_end.is_auto())
{
let stretches = item.stretches();
let used_cross_size = if stretches {
(final_line_cross_size - item.pbm_auto_is_zero.cross).clamp_between_extremums(
item.content_min_size.cross,
item.content_max_size.cross,
@ -1425,7 +1468,7 @@ impl InitialFlexLineLayout<'_> {
};
item_used_cross_sizes.push(used_cross_size);
if has_stretch {
if stretches {
// “If the flex item has `align-self: stretch`, redo layout for its contents,
// treating this used size as its definite cross size
// so that percentage-sized children can be resolved.”
@ -1585,7 +1628,7 @@ impl FlexItem<'_> {
flex_context: &FlexContext,
used_cross_size_override: Option<Au>,
) -> FlexItemLayoutResult {
let containing_block = &flex_context.containing_block;
let containing_block = flex_context.containing_block;
let mut positioning_context = PositioningContext::new_for_style(self.box_.style())
.unwrap_or_else(|| {
PositioningContext::new_for_subtree(
@ -1604,24 +1647,83 @@ impl FlexItem<'_> {
);
// … and also the items inline axis.
let cross_size = match used_cross_size_override {
Some(s) => AuOrAuto::LengthPercentage(s),
None => self.content_box_size.cross.map(|cross_size| {
cross_size.clamp_between_extremums(
self.content_min_size.cross,
self.content_max_size.cross,
)
}),
};
let ifc = &mut self.box_.independent_formatting_context;
let item_writing_mode = ifc.style().effective_writing_mode();
let item_is_horizontal = item_writing_mode.is_horizontal();
let flex_axis = flex_context.config.flex_axis;
match &mut self.box_.independent_formatting_context {
let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
containing_block
.style
.effective_writing_mode()
.is_horizontal(),
item_is_horizontal,
flex_axis,
);
let (inline_size, block_size) = if cross_axis_is_item_block_axis {
(used_main_size, cross_size)
} else {
(
cross_size.auto_is(|| {
let style = ifc.style().clone();
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(
&style,
AuOrAuto::LengthPercentage(used_main_size),
);
let content_contributions = ifc
.inline_content_sizes(
flex_context.layout_context,
&containing_block_for_children,
&containing_block.into(),
)
.map(|size| {
size.clamp_between_extremums(
self.content_min_size.cross,
self.content_max_size.cross,
)
});
(containing_block.inline_size - self.pbm_auto_is_zero.cross)
.min(content_contributions.max_content)
.max(content_contributions.min_content)
}),
// The main size of a flex item is considered to be definite if its flex basis is definite
// or the flex container has a definite main size.
// <https://drafts.csswg.org/css-flexbox-1/#definite-sizes>
if self.flex_base_size_is_definite ||
flex_context.container_definite_inner_size.main.is_some()
{
AuOrAuto::LengthPercentage(used_main_size)
} else {
AuOrAuto::Auto
},
)
};
match ifc {
IndependentFormattingContext::Replaced(replaced) => {
let pbm = replaced.style.padding_border_margin(containing_block);
let box_size = used_cross_size_override.map(|size| LogicalVec2 {
inline: replaced
.style
.content_box_size(containing_block, &pbm)
.inline
.map(Au::from),
block: AuOrAuto::LengthPercentage(size),
});
let size = replaced.contents.used_size_as_if_inline_element(
containing_block,
&replaced.style,
box_size,
&pbm,
);
let size = replaced
.contents
.used_size_as_if_inline_element_from_content_box_sizes(
containing_block,
&replaced.style,
LogicalVec2 {
inline: AuOrAuto::LengthPercentage(inline_size),
block: block_size,
},
flex_axis.vec2_to_flow_relative(self.content_min_size),
flex_axis.vec2_to_flow_relative(self.content_max_size),
);
let cross_size = flex_axis.vec2_to_flex_relative(size).cross;
let fragments = replaced.contents.make_fragments(
&replaced.style,
@ -1640,66 +1742,6 @@ impl FlexItem<'_> {
}
},
IndependentFormattingContext::NonReplaced(non_replaced) => {
let cross_size = match used_cross_size_override {
Some(s) => AuOrAuto::LengthPercentage(s),
None => self.content_box_size.cross.map(|cross_size| {
cross_size.clamp_between_extremums(
self.content_min_size.cross,
self.content_max_size.cross,
)
}),
};
let item_writing_mode = non_replaced.style.effective_writing_mode();
let item_is_horizontal = item_writing_mode.is_horizontal();
let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
containing_block
.style
.effective_writing_mode()
.is_horizontal(),
item_is_horizontal,
flex_axis,
);
let (inline_size, block_size) = if cross_axis_is_item_block_axis {
(used_main_size, cross_size)
} else {
(
cross_size.auto_is(|| {
let style = non_replaced.style.clone();
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(
&style,
AuOrAuto::LengthPercentage(used_main_size),
);
let content_contributions = non_replaced
.inline_content_sizes(
flex_context.layout_context,
&containing_block_for_children,
)
.map(|size| {
size.clamp_between_extremums(
self.content_min_size.cross,
self.content_max_size.cross,
)
});
(containing_block.inline_size - self.pbm_auto_is_zero.cross)
.min(content_contributions.max_content)
.max(content_contributions.min_content)
}),
// The main size of a flex item is considered to be definite if its flex basis is definite
// or the flex container has a definite main size.
// <https://drafts.csswg.org/css-flexbox-1/#definite-sizes>
if self.flex_base_size_is_definite ||
flex_context.container_definite_inner_size.main.is_some()
{
AuOrAuto::LengthPercentage(used_main_size)
} else {
AuOrAuto::Auto
},
)
};
let item_as_containing_block = ContainingBlock {
inline_size,
block_size,
@ -1903,10 +1945,11 @@ impl FlexItemBox {
layout_context: &LayoutContext,
containing_block: &IndefiniteContainingBlock,
container_is_horizontal: bool,
flex_axis: FlexAxis,
main_start_cross_start: MainStartCrossStart,
config: &FlexContainerConfig,
flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
) -> FlexItemBoxInlineContentSizesInfo {
let flex_axis = config.flex_axis;
let main_start_cross_start = config.main_start_cross_start_sides_are;
let style = self.style().clone();
let item_writing_mode = style.effective_writing_mode();
let item_is_horizontal = item_writing_mode.is_horizontal();
@ -1915,6 +1958,19 @@ impl FlexItemBox {
let (content_box_size, content_min_size, content_max_size, pbm) =
style.content_box_sizes_and_padding_border_margin(containing_block);
let padding = main_start_cross_start.sides_to_flex_relative(pbm.padding);
let border = main_start_cross_start.sides_to_flex_relative(pbm.border);
let margin = main_start_cross_start.sides_to_flex_relative(pbm.margin);
let padding_border = padding.sum_by_axis() + border.sum_by_axis();
let margin_auto_is_zero = pbm.margin.auto_is(Au::zero);
let margin_auto_is_zero =
main_start_cross_start.sides_to_flex_relative(margin_auto_is_zero);
let pbm_auto_is_zero = FlexRelativeVec2 {
main: padding_border.main,
cross: padding_border.cross,
} + margin_auto_is_zero.sum_by_axis();
let item_with_auto_cross_size_stretches_to_container_size =
config.item_with_auto_cross_size_stretches_to_container_size(&style, &margin);
let automatic_min_size = self.automatic_min_size(
layout_context,
containing_block,
@ -1922,6 +1978,8 @@ impl FlexItemBox {
flex_axis.vec2_to_flex_relative(content_box_size),
flex_axis.vec2_to_flex_relative(content_min_size),
flex_axis.vec2_to_flex_relative(content_max_size),
&pbm_auto_is_zero,
item_with_auto_cross_size_stretches_to_container_size,
|item| {
item.layout_for_block_content_size(
flex_context_getter(),
@ -1929,6 +1987,7 @@ impl FlexItemBox {
content_box_size,
content_min_size.map(|v| v.auto_is(Au::zero)),
content_max_size,
item_with_auto_cross_size_stretches_to_container_size,
IntrinsicSizingMode::Size,
)
},
@ -1951,6 +2010,7 @@ impl FlexItemBox {
content_box_size,
content_min_size_no_auto,
content_max_size,
item_with_auto_cross_size_stretches_to_container_size,
IntrinsicSizingMode::Size,
)
};
@ -1964,6 +2024,7 @@ impl FlexItemBox {
layout_context,
containing_block,
&content_min_size_no_auto,
item_with_auto_cross_size_stretches_to_container_size,
),
FlexAxis::Column => self
.layout_for_block_content_size(
@ -1972,6 +2033,7 @@ impl FlexItemBox {
content_box_size,
content_min_size_no_auto,
content_max_size,
item_with_auto_cross_size_stretches_to_container_size,
IntrinsicSizingMode::Contribution,
)
.into(),
@ -1981,31 +2043,21 @@ impl FlexItemBox {
let content_min_size_no_auto = flex_axis.vec2_to_flex_relative(content_min_size_no_auto);
let content_max_size = flex_axis.vec2_to_flex_relative(content_max_size);
let padding = main_start_cross_start.sides_to_flex_relative(pbm.padding);
let border = main_start_cross_start.sides_to_flex_relative(pbm.border);
let padding_border = padding.sum_by_axis() + border.sum_by_axis();
let margin_auto_is_zero = pbm.margin.auto_is(Au::zero);
let margin_auto_is_zero =
main_start_cross_start.sides_to_flex_relative(margin_auto_is_zero);
let pbm_auto_is_zero = FlexRelativeVec2 {
main: padding_border.main,
cross: padding_border.cross,
} + margin_auto_is_zero.sum_by_axis();
// TODO: when laying out a column container with an indefinite main size,
// we compute the base sizes of the items twice. We should consider caching.
let (flex_base_size, _) = self.flex_base_size(
layout_context,
containing_block,
FlexRelativeVec2 {
main: None,
cross: None,
},
config
.flex_axis
.vec2_to_flex_relative(containing_block.size.map(|v| v.non_auto())),
cross_axis_is_item_block_axis,
content_box_size,
content_min_size_no_auto,
content_max_size,
padding_border,
&pbm_auto_is_zero,
item_with_auto_cross_size_stretches_to_container_size,
block_content_size_callback,
);
@ -2109,6 +2161,8 @@ impl FlexItemBox {
content_box_size: FlexRelativeVec2<AuOrAuto>,
min_size: FlexRelativeVec2<GenericLengthPercentageOrAuto<Au>>,
max_size: FlexRelativeVec2<Option<Au>>,
pbm_auto_is_zero: &FlexRelativeVec2<Au>,
auto_cross_size_stretches_to_container_size: bool,
block_content_size_callback: impl FnOnce(&mut FlexItemBox) -> Au,
) -> Au {
// FIXME(stshine): Consider more situations when auto min size is not needed.
@ -2137,14 +2191,25 @@ impl FlexItemBox {
Direction::Block
};
let cross_size =
if content_box_size.cross.is_auto() && auto_cross_size_stretches_to_container_size {
if cross_axis_is_item_block_axis {
containing_block.size.block
} else {
containing_block.size.inline
}
.map(|v| v - pbm_auto_is_zero.cross)
} else {
content_box_size.cross
}
.map(|v| v.clamp_between_extremums(min_size.cross.auto_is(Au::zero), max_size.cross));
// > **transferred size suggestion**
// > If the item has a preferred aspect ratio and its preferred cross size is definite, then the
// > transferred size suggestion is that size (clamped by its minimum and maximum cross sizes if they
// > are definite), converted through the aspect ratio. It is otherwise undefined.
let transferred_size_suggestion = match (ratio, content_box_size.cross) {
let transferred_size_suggestion = match (ratio, cross_size) {
(Some(ratio), AuOrAuto::LengthPercentage(cross_size)) => {
let cross_size = cross_size
.clamp_between_extremums(min_size.cross.auto_is(Au::zero), max_size.cross);
Some(ratio.compute_dependent_size(main_axis, cross_size))
},
_ => None,
@ -2155,12 +2220,9 @@ impl FlexItemBox {
// > preferred aspect ratio, by any definite minimum and maximum cross sizes converted through the
// > aspect ratio.
let main_content_size = if cross_axis_is_item_block_axis {
let block_size = content_box_size.cross.map(|v| {
v.clamp_between_extremums(min_size.cross.auto_is(Au::zero), max_size.cross)
});
let style = self.independent_formatting_context.style().clone();
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size);
IndefiniteContainingBlock::new_for_style_and_block_size(&style, cross_size);
self.independent_formatting_context
.inline_content_sizes(
layout_context,
@ -2204,9 +2266,11 @@ impl FlexItemBox {
container_definite_inner_size: FlexRelativeVec2<Option<Au>>,
cross_axis_is_item_block_axis: bool,
content_box_size: FlexRelativeVec2<AuOrAuto>,
min_size: FlexRelativeVec2<Au>,
max_size: FlexRelativeVec2<Option<Au>>,
content_min_box_size: FlexRelativeVec2<Au>,
content_max_box_size: FlexRelativeVec2<Option<Au>>,
padding_border_sums: FlexRelativeVec2<Au>,
pbm_auto_is_zero: &FlexRelativeVec2<Au>,
item_with_auto_cross_size_stretches_to_container_size: bool,
block_content_size_callback: impl FnOnce(&mut FlexItemBox) -> Au,
) -> (Au, bool) {
let flex_item = &mut self.independent_formatting_context;
@ -2268,7 +2332,38 @@ impl FlexItemBox {
(length, true)
},
FlexBasis::Content => {
// FIXME: implement cases B, C, D.
// > B: If the flex item has ...
// > - a preferred aspect ratio,
// > - a used flex basis of content, and
// > - a definite cross size,
// > then the flex base size is calculated from its used cross size and the flex items aspect ratio.
let ratio = flex_item.preferred_aspect_ratio(containing_block);
let main_axis = if cross_axis_is_item_block_axis {
Direction::Inline
} else {
Direction::Block
};
// > If a single-line flex container has a definite cross size, the automatic preferred
// > outer cross size of any stretched flex items is the flex containers inner cross size
// > (clamped to the flex items min and max cross size) and is considered definite.
let cross_size = if content_box_size.cross.is_auto() &&
item_with_auto_cross_size_stretches_to_container_size
{
container_definite_inner_size
.cross
.map(|v| v - pbm_auto_is_zero.cross)
} else {
content_box_size.cross.non_auto()
};
if let (Some(ratio), Some(cross_size)) = (ratio, cross_size) {
let cross_size = cross_size.clamp_between_extremums(
content_min_box_size.cross,
content_max_box_size.cross,
);
return (ratio.compute_dependent_size(main_axis, cross_size), true);
}
// FIXME: implement cases C, D.
// > E. Otherwise, size the item into the available space using its used flex basis in place of
// > its main size, treating a value of content as max-content. If a cross size is needed to
@ -2279,19 +2374,32 @@ impl FlexItemBox {
let flex_basis = if cross_axis_is_item_block_axis {
// The main axis is the inline axis, so we can get the content size from the normal
// preferred widths calculation.
let style = style.clone();
let block_size = content_box_size
.cross
.map(|v| v.clamp_between_extremums(min_size.cross, max_size.cross));
let style = flex_item.style().clone();
let block_size = content_box_size.cross.map(|v| {
v.clamp_between_extremums(
content_min_box_size.cross,
content_max_box_size.cross,
)
});
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size);
flex_item
let max_content = flex_item
.inline_content_sizes(
layout_context,
&containing_block_for_children,
containing_block,
)
.max_content
.max_content;
if let Some(ratio) = ratio {
max_content.clamp_between_extremums(
ratio.compute_dependent_size(main_axis, content_min_box_size.cross),
content_max_box_size
.cross
.map(|v| ratio.compute_dependent_size(main_axis, v)),
)
} else {
max_content
}
} else {
block_content_size_callback(self)
};
@ -2304,9 +2412,10 @@ impl FlexItemBox {
&mut self,
flex_context: &FlexContext,
padding_border_margin: &PaddingBorderMargin,
content_box_size: LogicalVec2<AuOrAuto>,
min_size: LogicalVec2<Au>,
max_size: LogicalVec2<Option<Au>>,
mut content_box_size: LogicalVec2<AuOrAuto>,
mut min_size: LogicalVec2<Au>,
mut max_size: LogicalVec2<Option<Au>>,
item_with_auto_cross_size_stretches_to_container_size: bool,
intrinsic_sizing_mode: IntrinsicSizingMode,
) -> Au {
let mut positioning_context = PositioningContext::new_for_subtree(
@ -2317,14 +2426,22 @@ impl FlexItemBox {
match &mut self.independent_formatting_context {
IndependentFormattingContext::Replaced(replaced) => {
// TODO: handle intrinsic_sizing_mode.
let size = replaced.contents.used_size_as_if_inline_element(
flex_context.containing_block,
&replaced.style,
None,
padding_border_margin,
);
size.block
content_box_size.inline = content_box_size.inline.map(|v| v.max(Au::zero()));
if intrinsic_sizing_mode == IntrinsicSizingMode::Size {
content_box_size.block = AuOrAuto::Auto;
min_size.block = Au::zero();
max_size.block = None;
}
replaced
.contents
.used_size_as_if_inline_element_from_content_box_sizes(
flex_context.containing_block,
&replaced.style,
content_box_size,
min_size,
max_size,
)
.block
},
IndependentFormattingContext::NonReplaced(non_replaced) => {
// TODO: This is wrong if the item writing mode is different from the flex
@ -2332,18 +2449,11 @@ impl FlexItemBox {
let inline_size = content_box_size
.inline
.auto_is(|| {
let will_stretch = flex_context
.align_for(non_replaced.style.clone_align_self())
.0 ==
AlignFlags::STRETCH &&
!padding_border_margin.margin.inline_start.is_auto() &&
!padding_border_margin.margin.inline_end.is_auto();
let containing_block_inline_size_minus_pbm =
flex_context.containing_block.inline_size -
padding_border_margin.padding_border_sums.inline;
if will_stretch {
if item_with_auto_cross_size_stretches_to_container_size {
containing_block_inline_size_minus_pbm
} else {
let style = non_replaced.style.clone();

View file

@ -980,7 +980,6 @@ impl FloatBox {
content_size = replaced.contents.used_size_as_if_inline_element(
containing_block,
&replaced.style,
None,
&pbm,
);
children = replaced.contents.make_fragments(

View file

@ -1917,12 +1917,7 @@ impl IndependentFormattingContext {
IndependentFormattingContext::Replaced(replaced) => {
let size = replaced
.contents
.used_size_as_if_inline_element(
layout.containing_block,
&replaced.style,
None,
&pbm,
)
.used_size_as_if_inline_element(layout.containing_block, &replaced.style, &pbm)
.to_physical_size(container_writing_mode);
let fragments = replaced.contents.make_fragments(&replaced.style, size);
let content_rect = PhysicalRect::new(pbm_physical_origin, size);
@ -2383,6 +2378,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
self.layout_context,
self.containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
);
if !inline_formatting_context

View file

@ -372,6 +372,7 @@ fn calculate_inline_content_size_for_block_level_boxes(
layout_context,
containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
)
.max(ContentSizes::zero());
let style_box = &float_box.contents.style().get_box();
@ -384,6 +385,7 @@ fn calculate_inline_content_size_for_block_level_boxes(
style,
&containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
|containing_block_for_children| {
contents.inline_content_sizes(layout_context, containing_block_for_children)
},
@ -400,6 +402,7 @@ fn calculate_inline_content_size_for_block_level_boxes(
layout_context,
containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
)
.max(ContentSizes::zero());
Some((size, Float::None, independent.style().get_box().clear))
@ -1296,7 +1299,7 @@ fn layout_in_flow_replaced_block_level(
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
) -> BoxFragment {
let pbm = style.padding_border_margin(containing_block);
let content_size = replaced.used_size_as_if_inline_element(containing_block, style, None, &pbm);
let content_size = replaced.used_size_as_if_inline_element(containing_block, style, &pbm);
let margin_inline_start;
let margin_inline_end;

View file

@ -198,12 +198,14 @@ impl IndependentFormattingContext {
layout_context: &LayoutContext,
containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool,
) -> ContentSizes {
match self {
Self::NonReplaced(non_replaced) => sizing::outer_inline(
&non_replaced.style.clone(),
containing_block,
auto_minimum,
auto_block_size_stretches_to_containing_block,
|containing_block_for_children| {
non_replaced.inline_content_sizes(layout_context, containing_block_for_children)
},
@ -212,6 +214,7 @@ impl IndependentFormattingContext {
&replaced.style,
containing_block,
auto_minimum,
auto_block_size_stretches_to_containing_block,
|containing_block_for_children| {
replaced.contents.inline_content_sizes(
layout_context,
@ -222,6 +225,16 @@ impl IndependentFormattingContext {
),
}
}
pub(crate) fn preferred_aspect_ratio(
&self,
containing_block: &IndefiniteContainingBlock,
) -> Option<AspectRatio> {
match self {
Self::NonReplaced(_) => None,
Self::Replaced(replaced) => replaced.preferred_aspect_ratio(containing_block),
}
}
}
impl NonReplacedFormattingContext {

View file

@ -499,7 +499,6 @@ impl HoistedAbsolutelyPositionedBox {
let used_size = replaced.contents.used_size_as_if_inline_element(
&containing_block.into(),
&replaced.style,
None,
&pbm,
);
LogicalVec2 {

View file

@ -360,32 +360,50 @@ impl ReplacedContent {
///
/// Also used in other cases, for example
/// <https://drafts.csswg.org/css2/visudet.html#block-replaced-width>
pub fn used_size_as_if_inline_element(
pub(crate) fn used_size_as_if_inline_element(
&self,
containing_block: &ContainingBlock,
style: &ComputedValues,
box_size: Option<LogicalVec2<AuOrAuto>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Au> {
let mode = style.effective_writing_mode();
let intrinsic_size = self.flow_relative_intrinsic_size(style);
let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style);
let box_size = box_size.unwrap_or(
style
.content_box_size(containing_block, pbm)
// We need to clamp to zero here to obtain the proper aspect
// ratio when box-sizing is border-box and the inner box size
// would otherwise be negative.
.map(|v| v.map(|v| Au::from(v).max(Au::zero()))),
);
let max_box_size = style
.content_max_box_size(containing_block, pbm)
.map(|v| v.map(Au::from));
let box_size = style
.content_box_size(containing_block, pbm)
// We need to clamp to zero here to obtain the proper aspect
// ratio when box-sizing is border-box and the inner box size
// would otherwise be negative.
.map(|v| v.map(|v| Au::from(v).max(Au::zero())));
let min_box_size = style
.content_min_box_size(containing_block, pbm)
.map(|v| v.map(Au::from))
.auto_is(Au::zero);
let max_box_size = style
.content_max_box_size(containing_block, pbm)
.map(|v| v.map(Au::from));
self.used_size_as_if_inline_element_from_content_box_sizes(
containing_block,
style,
box_size,
min_box_size,
max_box_size,
)
}
/// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-width>
/// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-height>
///
/// Also used in other cases, for example
/// <https://drafts.csswg.org/css2/visudet.html#block-replaced-width>
pub(crate) fn used_size_as_if_inline_element_from_content_box_sizes(
&self,
containing_block: &ContainingBlock,
style: &ComputedValues,
box_size: LogicalVec2<AuOrAuto>,
min_box_size: LogicalVec2<Au>,
max_box_size: LogicalVec2<Option<Au>>,
) -> LogicalVec2<Au> {
let mode = style.effective_writing_mode();
let intrinsic_size = self.flow_relative_intrinsic_size(style);
let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style);
let default_object_size = || {
// FIXME:

View file

@ -111,6 +111,7 @@ pub(crate) fn outer_inline(
style: &ComputedValues,
containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool,
get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> ContentSizes,
) -> ContentSizes {
let (content_box_size, content_min_size, content_max_size, pbm) =
@ -119,18 +120,23 @@ pub(crate) fn outer_inline(
inline: content_min_size.inline.auto_is(|| auto_minimum.inline),
block: content_min_size.block.auto_is(|| auto_minimum.block),
};
let pbm_inline_sum = pbm.padding_border_sums.inline +
pbm.margin.inline_start.auto_is(Au::zero) +
pbm.margin.inline_end.auto_is(Au::zero);
let margin = pbm.margin.map(|v| v.auto_is(Au::zero));
let pbm_inline_sum = pbm.padding_border_sums.inline + margin.inline_sum();
let adjust = |v: Au| {
v.clamp_between_extremums(content_min_size.inline, content_max_size.inline) + pbm_inline_sum
};
match content_box_size.inline {
AuOrAuto::LengthPercentage(inline_size) => adjust(inline_size).into(),
AuOrAuto::Auto => {
let block_size = content_box_size
.block
.map(|v| v.clamp_between_extremums(content_min_size.block, content_max_size.block));
let block_size = if content_box_size.block.is_auto() &&
auto_block_size_stretches_to_containing_block
{
let outer_block_size = containing_block.size.block;
outer_block_size.map(|v| v - pbm.padding_border_sums.block - margin.block_sum())
} else {
content_box_size.block
}
.map(|v| v.clamp_between_extremums(content_min_size.block, content_max_size.block));
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(style, block_size);
get_content_size(&containing_block_for_children).map(adjust)

View file

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[aspect-ratio-intrinsic-size-006.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[css-flexbox-img-expand-evenly.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flex-aspect-ratio-img-column-010.html]
expected: FAIL

View file

@ -1,9 +1,3 @@
[flex-aspect-ratio-img-column-011.html]
[.flexbox 7]
expected: FAIL
[.flexbox 1]
expected: FAIL
[.flexbox 3]
expected: FAIL

View file

@ -1,3 +0,0 @@
[flex-aspect-ratio-img-row-005.html]
[img 3]
expected: FAIL

View file

@ -1,15 +0,0 @@
[flex-aspect-ratio-img-row-013.html]
[img 1]
expected: FAIL
[img 2]
expected: FAIL
[img 3]
expected: FAIL
[img 4]
expected: FAIL
[img 5]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flex-minimum-height-flex-items-020.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flex-minimum-height-flex-items-021.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flex-minimum-width-flex-items-013.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-flex-basis-content-002a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-flex-basis-content-002b.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-min-width-auto-002a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-min-width-auto-002b.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox-min-width-auto-002c.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flexbox_align-items-stretch-4.html]
expected: FAIL

View file

@ -1,4 +0,0 @@
[flexitem-stretch-image.html]
[.flexbox 1]
expected: FAIL

View file

@ -1,15 +0,0 @@
[image-as-flexitem-size-001.html]
[.flexbox > img 4]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 10]
expected: FAIL
[.flexbox > img 11]
expected: FAIL

View file

@ -1,15 +0,0 @@
[image-as-flexitem-size-001v.html]
[.flexbox > img 4]
expected: FAIL
[.flexbox > img 11]
expected: FAIL
[.flexbox > img 10]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 6]
expected: FAIL

View file

@ -1,3 +0,0 @@
[image-as-flexitem-size-002.html]
[.flexbox > img 4]
expected: FAIL

View file

@ -1,3 +0,0 @@
[image-as-flexitem-size-002v.html]
[.flexbox > img 4]
expected: FAIL

View file

@ -1,21 +0,0 @@
[image-as-flexitem-size-003.html]
[.flexbox > img 1]
expected: FAIL
[.flexbox > img 5]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 8]
expected: FAIL
[.flexbox > img 11]
expected: FAIL
[.flexbox > img 10]
expected: FAIL

View file

@ -1,21 +0,0 @@
[image-as-flexitem-size-003v.html]
[.flexbox > img 1]
expected: FAIL
[.flexbox > img 5]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 8]
expected: FAIL
[.flexbox > img 11]
expected: FAIL
[.flexbox > img 10]
expected: FAIL

View file

@ -1,15 +0,0 @@
[image-as-flexitem-size-004.html]
[.flexbox > img 8]
expected: FAIL
[.flexbox > img 5]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 1]
expected: FAIL

View file

@ -1,15 +0,0 @@
[image-as-flexitem-size-004v.html]
[.flexbox > img 8]
expected: FAIL
[.flexbox > img 5]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 1]
expected: FAIL

View file

@ -1,24 +0,0 @@
[image-as-flexitem-size-005.html]
[.flexbox > img 18]
expected: FAIL
[.flexbox > img 8]
expected: FAIL
[.flexbox > img 5]
expected: FAIL
[.flexbox > img 4]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 1]
expected: FAIL
[.flexbox > img 2]
expected: FAIL

View file

@ -1,24 +0,0 @@
[image-as-flexitem-size-005v.html]
[.flexbox > img 18]
expected: FAIL
[.flexbox > img 8]
expected: FAIL
[.flexbox > img 5]
expected: FAIL
[.flexbox > img 4]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 1]
expected: FAIL
[.flexbox > img 2]
expected: FAIL

View file

@ -1,24 +0,0 @@
[image-as-flexitem-size-006.html]
[.flexbox > img 15]
expected: FAIL
[.flexbox > img 8]
expected: FAIL
[.flexbox > img 5]
expected: FAIL
[.flexbox > img 4]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 1]
expected: FAIL
[.flexbox > img 3]
expected: FAIL

View file

@ -1,24 +0,0 @@
[image-as-flexitem-size-006v.html]
[.flexbox > img 15]
expected: FAIL
[.flexbox > img 8]
expected: FAIL
[.flexbox > img 5]
expected: FAIL
[.flexbox > img 4]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 1]
expected: FAIL
[.flexbox > img 3]
expected: FAIL

View file

@ -1,15 +0,0 @@
[image-as-flexitem-size-007.html]
[.flexbox > img 4]
expected: FAIL
[.flexbox > img 6]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 10]
expected: FAIL
[.flexbox > img 11]
expected: FAIL

View file

@ -1,15 +0,0 @@
[image-as-flexitem-size-007v.html]
[.flexbox > img 4]
expected: FAIL
[.flexbox > img 11]
expected: FAIL
[.flexbox > img 10]
expected: FAIL
[.flexbox > img 7]
expected: FAIL
[.flexbox > img 6]
expected: FAIL

View file

@ -0,0 +1,3 @@
[row-use-cases-001.html]
[same heights]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flex-aspect-ratio-023.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[flex-aspect-ratio-024.html]
expected: FAIL