Fix inline content sizes of intrinsic element with indefinite block size (#34152)

To compute the min-content and max-content inline sizes of a replaced
element, we were only using the aspect ratio to transfer definite block
sizes resulting from clamping the preferred block size between the min
and max block sizes.

However, if the preferred block size is indefinite, then we weren't
transfering the min and max through the aspect ratio.

This patch adds a `SizeConstraint` enum that can represent these cases,
and a `ConstraintSpace` struct analogous to `IndefiniteContainingBlock`
but with no inline size, and a `SizeConstraint` block size.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2024-11-11 12:38:19 +01:00 committed by GitHub
parent 72971bd271
commit b28260aa13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 297 additions and 292 deletions

View file

@ -40,7 +40,7 @@ use crate::sizing::{ContentSizes, InlineContentSizesResult, IntrinsicSizingMode}
use crate::style_ext::{
Clamp, ComputedValuesExt, ContentBoxSizesAndPBMDeprecated, PaddingBorderMargin,
};
use crate::{ContainingBlock, IndefiniteContainingBlock};
use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SizeConstraint};
/// Layout parameters and intermediate results about a flex container,
/// grouped to avoid passing around many parameters
@ -401,19 +401,17 @@ impl FlexContainer {
pub fn inline_content_sizes(
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
constraint_space: &ConstraintSpace,
) -> InlineContentSizesResult {
match self.config.flex_axis {
FlexAxis::Row => {
self.main_content_sizes(layout_context, containing_block_for_children, || {
self.main_content_sizes(layout_context, &constraint_space.into(), || {
unreachable!(
"Unexpected FlexContext query during row flex intrinsic size calculation."
)
})
},
FlexAxis::Column => {
self.cross_content_sizes(layout_context, containing_block_for_children)
},
FlexAxis::Column => self.cross_content_sizes(layout_context, &constraint_space.into()),
}
}
@ -1882,17 +1880,16 @@ impl FlexItem<'_> {
} else {
(
cross_size.auto_is(|| {
let containing_block_for_children =
IndefiniteContainingBlock::new_for_writing_mode_and_block_size(
let constraint_space = ConstraintSpace::new(
SizeConstraint::Definite(used_main_size),
item_writing_mode,
AuOrAuto::LengthPercentage(used_main_size),
);
let content_contributions = self
.box_
.independent_formatting_context
.inline_content_sizes(
flex_context.layout_context,
&containing_block_for_children,
&constraint_space,
&containing_block.into(),
)
.sizes
@ -2474,7 +2471,7 @@ impl FlexItemBox {
Direction::Block
};
let cross_size =
let cross_size = SizeConstraint::new(
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
@ -2485,14 +2482,17 @@ impl FlexItemBox {
} else {
content_box_size.cross
}
.map(|v| v.clamp_between_extremums(min_size.cross.auto_is(Au::zero), max_size.cross));
.non_auto(),
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, cross_size) {
(Some(ratio), AuOrAuto::LengthPercentage(cross_size)) => {
(Some(ratio), SizeConstraint::Definite(cross_size)) => {
Some(ratio.compute_dependent_size(main_axis, cross_size))
},
_ => None,
@ -2504,17 +2504,9 @@ impl FlexItemBox {
// > aspect ratio.
let main_content_size = if cross_axis_is_item_block_axis {
let writing_mode = self.independent_formatting_context.style().writing_mode;
let containing_block_for_children =
IndefiniteContainingBlock::new_for_writing_mode_and_block_size(
writing_mode,
cross_size,
);
let constraint_space = ConstraintSpace::new(cross_size, writing_mode);
self.independent_formatting_context
.inline_content_sizes(
layout_context,
&containing_block_for_children,
containing_block,
)
.inline_content_sizes(layout_context, &constraint_space, containing_block)
.sizes
.min_content
} else {
@ -2664,23 +2656,16 @@ impl FlexItemBox {
// The main axis is the inline axis, so we can get the content size from the normal
// preferred widths calculation.
let writing_mode = flex_item.style().writing_mode;
let block_size = content_box_size.cross.map(|v| {
v.clamp_between_extremums(
let constraint_space = ConstraintSpace::new(
SizeConstraint::new(
content_box_size.cross.non_auto(),
content_min_box_size.cross,
content_max_box_size.cross,
)
});
let containing_block_for_children =
IndefiniteContainingBlock::new_for_writing_mode_and_block_size(
),
writing_mode,
block_size,
);
let max_content = flex_item
.inline_content_sizes(
layout_context,
&containing_block_for_children,
containing_block,
)
.inline_content_sizes(layout_context, &constraint_space, containing_block)
.sizes
.max_content;
if let Some(ratio) = ratio {
@ -2764,9 +2749,7 @@ impl FlexItemBox {
containing_block_inline_size_minus_pbm
} else {
let containing_block_for_children =
IndefiniteContainingBlock::new_for_writing_mode(
non_replaced.style.writing_mode,
);
ConstraintSpace::new_for_style(&non_replaced.style);
non_replaced
.inline_content_sizes(
flex_context.layout_context,

View file

@ -128,7 +128,7 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::{ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
use crate::{ContainingBlock, IndefiniteContainingBlock};
use crate::{ConstraintSpace, ContainingBlock};
// From gfxFontConstants.h in Firefox.
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
@ -1574,9 +1574,9 @@ impl InlineFormattingContext {
pub(super) fn inline_content_sizes(
&self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
constraint_space: &ConstraintSpace,
) -> InlineContentSizesResult {
ContentSizesComputation::compute(self, layout_context, containing_block_for_children)
ContentSizesComputation::compute(self, layout_context, constraint_space)
}
pub(super) fn layout(
@ -2188,7 +2188,7 @@ fn inline_container_needs_strut(
/// A struct which takes care of computing [`ContentSizes`] for an [`InlineFormattingContext`].
struct ContentSizesComputation<'layout_data> {
layout_context: &'layout_data LayoutContext<'layout_data>,
containing_block: &'layout_data IndefiniteContainingBlock,
constraint_space: &'layout_data ConstraintSpace,
paragraph: ContentSizes,
current_line: ContentSizes,
/// Size for whitespace pending to be added to this line.
@ -2234,16 +2234,15 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
let inline_box = inline_formatting_context.inline_boxes.get(identifier);
let inline_box = (*inline_box).borrow();
let zero = Au::zero();
let writing_mode = self.constraint_space.writing_mode;
let padding = inline_box
.style
.padding(self.containing_block.writing_mode)
.padding(writing_mode)
.percentages_relative_to(zero);
let border = inline_box
.style
.border_width(self.containing_block.writing_mode);
let border = inline_box.style.border_width(writing_mode);
let margin = inline_box
.style
.margin(self.containing_block.writing_mode)
.margin(writing_mode)
.percentages_relative_to(zero)
.auto_is(Au::zero);
@ -2329,7 +2328,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
depends_on_block_constraints,
} = atomic.outer_inline_content_sizes(
self.layout_context,
self.containing_block,
&self.constraint_space.into(),
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
);
@ -2384,11 +2383,11 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
fn compute(
inline_formatting_context: &InlineFormattingContext,
layout_context: &'layout_data LayoutContext,
containing_block: &'layout_data IndefiniteContainingBlock,
constraint_space: &'layout_data ConstraintSpace,
) -> InlineContentSizesResult {
Self {
layout_context,
containing_block,
constraint_space,
paragraph: ContentSizes::zero(),
current_line: ContentSizes::zero(),
pending_whitespace: ContentSizes::zero(),

View file

@ -41,7 +41,7 @@ use crate::sizing::{self, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{
Clamp, ComputedValuesExt, ContentBoxSizesAndPBMDeprecated, PaddingBorderMargin,
};
use crate::{ContainingBlock, IndefiniteContainingBlock};
use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SizeConstraint};
mod construct;
pub mod float;
@ -235,7 +235,7 @@ impl OutsideMarker {
) -> Fragment {
let content_sizes = self.block_container.inline_content_sizes(
layout_context,
&IndefiniteContainingBlock::new_for_writing_mode(self.marker_style.writing_mode),
&ConstraintSpace::new_for_style(&self.marker_style),
);
let containing_block_for_children = ContainingBlock {
inline_size: content_sizes.sizes.max_content,
@ -393,8 +393,8 @@ fn calculate_inline_content_size_for_block_level_boxes(
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)
|constraint_space| {
contents.inline_content_sizes(layout_context, constraint_space)
},
);
// A block in the same BFC can overlap floats, it's not moved next to them,
@ -528,16 +528,16 @@ impl BlockContainer {
pub(super) fn inline_content_sizes(
&self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
constraint_space: &ConstraintSpace,
) -> InlineContentSizesResult {
match &self {
Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes(
boxes,
layout_context,
containing_block_for_children,
&constraint_space.into(),
),
Self::InlineFormattingContext(context) => {
context.inline_content_sizes(layout_context, containing_block_for_children)
context.inline_content_sizes(layout_context, constraint_space)
},
}
}
@ -2030,18 +2030,17 @@ impl IndependentFormattingContext {
(fragments, content_rect, None)
},
IndependentFormattingContext::NonReplaced(non_replaced) => {
let style = non_replaced.style.clone();
let writing_mode = non_replaced.style.writing_mode;
let available_inline_size =
(containing_block.inline_size - pbm_sums.inline_sum()).max(Au::zero());
let available_block_size = containing_block
.block_size
.non_auto()
.map(|block_size| (block_size - pbm_sums.block_sum()).max(Au::zero()));
let tentative_block_size = content_box_sizes_and_pbm
let preferred_block_size = content_box_sizes_and_pbm
.content_box_size
.block
.maybe_resolve_extrinsic(available_block_size)
.map(|size| {
.maybe_resolve_extrinsic(available_block_size);
let min_block_size = content_box_sizes_and_pbm
.content_min_box_size
.block
@ -2051,18 +2050,13 @@ impl IndependentFormattingContext {
.content_max_box_size
.block
.maybe_resolve_extrinsic(available_block_size);
size.clamp_between_extremums(min_block_size, max_block_size)
})
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage);
let tentative_block_size =
SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
let mut get_content_size = || {
let containing_block_for_children =
IndefiniteContainingBlock::new_for_writing_mode_and_block_size(
style.writing_mode,
tentative_block_size,
);
let constraint_space = ConstraintSpace::new(tentative_block_size, writing_mode);
non_replaced
.inline_content_sizes(layout_context, &containing_block_for_children)
.inline_content_sizes(layout_context, &constraint_space)
.sizes
};
@ -2092,12 +2086,12 @@ impl IndependentFormattingContext {
let containing_block_for_children = ContainingBlock {
inline_size,
block_size: tentative_block_size,
style: &style,
block_size: tentative_block_size.to_auto_or(),
style: &non_replaced.style,
};
assert_eq!(
container_writing_mode.is_horizontal(),
style.writing_mode.is_horizontal(),
writing_mode.is_horizontal(),
"Mixed horizontal and vertical writing modes are not supported yet"
);
@ -2126,7 +2120,8 @@ impl IndependentFormattingContext {
.block
.resolve_non_initial(stretch_size, &mut get_content_size);
let block_size = tentative_block_size
.auto_is(|| independent_layout.content_block_size)
.to_definite()
.unwrap_or(independent_layout.content_block_size)
.clamp_between_extremums(min_block_size, max_block_size);
(inline_size, block_size)
},

View file

@ -21,7 +21,9 @@ use crate::replaced::ReplacedContent;
use crate::sizing::{self, InlineContentSizesResult};
use crate::style_ext::{AspectRatio, DisplayInside};
use crate::table::Table;
use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock, LogicalVec2};
use crate::{
ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, LogicalVec2, SizeConstraint,
};
/// <https://drafts.csswg.org/css-display/#independent-formatting-context>
#[derive(Debug, Serialize)]
@ -36,7 +38,7 @@ pub(crate) struct NonReplacedFormattingContext {
#[serde(skip_serializing)]
pub style: Arc<ComputedValues>,
/// If it was requested during construction
pub content_sizes_result: Option<(AuOrAuto, InlineContentSizesResult)>,
pub content_sizes_result: Option<(SizeConstraint, InlineContentSizesResult)>,
pub contents: NonReplacedFormattingContextContents,
}
@ -188,16 +190,16 @@ impl IndependentFormattingContext {
pub(crate) fn inline_content_sizes(
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
constraint_space: &ConstraintSpace,
containing_block: &IndefiniteContainingBlock,
) -> InlineContentSizesResult {
match self {
Self::NonReplaced(inner) => {
inner.inline_content_sizes(layout_context, containing_block_for_children)
inner.inline_content_sizes(layout_context, constraint_space)
},
Self::Replaced(inner) => inner.contents.inline_content_sizes(
layout_context,
containing_block_for_children,
constraint_space,
inner.preferred_aspect_ratio(containing_block),
),
}
@ -222,10 +224,10 @@ impl IndependentFormattingContext {
containing_block,
auto_minimum,
auto_block_size_stretches_to_containing_block,
|containing_block_for_children| {
|constraint_space| {
replaced.contents.inline_content_sizes(
layout_context,
containing_block_for_children,
constraint_space,
replaced.preferred_aspect_ratio(containing_block),
)
},
@ -276,16 +278,11 @@ impl NonReplacedFormattingContext {
pub(crate) fn inline_content_sizes(
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
constraint_space: &ConstraintSpace,
) -> InlineContentSizesResult {
assert_eq!(
containing_block_for_children.size.inline,
AuOrAuto::Auto,
"inline_content_sizes() got non-auto containing block inline-size",
);
if let Some((previous_cb_block_size, result)) = self.content_sizes_result {
if !result.depends_on_block_constraints ||
previous_cb_block_size == containing_block_for_children.size.block
previous_cb_block_size == constraint_space.block_size
{
return result;
}
@ -294,9 +291,9 @@ impl NonReplacedFormattingContext {
self.content_sizes_result
.insert((
containing_block_for_children.size.block,
constraint_space.block_size,
self.contents
.inline_content_sizes(layout_context, containing_block_for_children),
.inline_content_sizes(layout_context, constraint_space),
))
.1
}
@ -313,9 +310,7 @@ impl NonReplacedFormattingContext {
containing_block,
auto_minimum,
auto_block_size_stretches_to_containing_block,
|containing_block_for_children| {
self.inline_content_sizes(layout_context, containing_block_for_children)
},
|constraint_space| self.inline_content_sizes(layout_context, constraint_space),
)
}
}
@ -324,18 +319,14 @@ impl NonReplacedFormattingContextContents {
pub(crate) fn inline_content_sizes(
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
constraint_space: &ConstraintSpace,
) -> InlineContentSizesResult {
match self {
Self::Flow(inner) => inner
.contents
.inline_content_sizes(layout_context, containing_block_for_children),
Self::Flex(inner) => {
inner.inline_content_sizes(layout_context, containing_block_for_children)
},
Self::Table(table) => {
table.inline_content_sizes(layout_context, containing_block_for_children)
},
.inline_content_sizes(layout_context, constraint_space),
Self::Flex(inner) => inner.inline_content_sizes(layout_context, constraint_space),
Self::Table(table) => table.inline_content_sizes(layout_context, constraint_space),
}
}
}

View file

@ -17,6 +17,7 @@ use style::Zero;
use style_traits::CSSPixel;
use crate::sizing::ContentSizes;
use crate::style_ext::Clamp;
use crate::ContainingBlock;
pub type PhysicalPoint<U> = euclid::Point2D<U, CSSPixel>;
@ -848,3 +849,45 @@ impl Size<Au> {
}
}
}
/// Represents the sizing constraint that the preferred, min and max sizing properties
/// impose on one axis.
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub(crate) enum SizeConstraint {
/// Represents a definite preferred size, clamped by minimum and maximum sizes (if any).
Definite(Au),
/// Represents an indefinite preferred size that allows a range of values between
/// the first argument (minimum size) and the second one (maximum size).
MinMax(Au, Option<Au>),
}
impl Default for SizeConstraint {
#[inline]
fn default() -> Self {
Self::MinMax(Au::default(), None)
}
}
impl SizeConstraint {
#[inline]
pub(crate) fn new(preferred_size: Option<Au>, min_size: Au, max_size: Option<Au>) -> Self {
preferred_size.map_or_else(
|| Self::MinMax(min_size, max_size),
|size| Self::Definite(size.clamp_between_extremums(min_size, max_size)),
)
}
#[inline]
pub(crate) fn to_definite(self) -> Option<Au> {
match self {
Self::Definite(size) => Some(size),
_ => None,
}
}
#[inline]
pub(crate) fn to_auto_or(self) -> AutoOr<Au> {
self.to_definite()
.map_or(AutoOr::Auto, AutoOr::LengthPercentage)
}
}

View file

@ -32,34 +32,44 @@ use geom::AuOrAuto;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use crate::geom::LogicalVec2;
use crate::geom::{LogicalVec2, SizeConstraint};
/// A containing block useful for calculating inline content sizes, which may
/// have inline sizes that depend on block sizes due to aspect ratio.
/// Represents the set of constraints that we use when computing the min-content
/// and max-content inline sizes of an element.
pub(crate) struct ConstraintSpace {
pub block_size: SizeConstraint,
pub writing_mode: WritingMode,
}
impl ConstraintSpace {
fn new(block_size: SizeConstraint, writing_mode: WritingMode) -> Self {
Self {
block_size,
writing_mode,
}
}
fn new_for_style(style: &ComputedValues) -> Self {
Self::new(SizeConstraint::default(), style.writing_mode)
}
}
/// A variant of [`ContainingBlock`] that allows an indefinite inline size.
/// Useful for code that is shared for both layout (where we know the inline size
/// of the containing block) and intrinsic sizing (where we don't know it).
pub(crate) struct IndefiniteContainingBlock {
pub size: LogicalVec2<AuOrAuto>,
pub writing_mode: WritingMode,
}
impl IndefiniteContainingBlock {
fn new_for_writing_mode(writing_mode: WritingMode) -> Self {
Self::new_for_writing_mode_and_block_size(writing_mode, AuOrAuto::Auto)
}
/// Creates an [`IndefiniteContainingBlock`] with the provided style and block size,
/// and the inline size is set to auto.
/// This is useful when finding the min-content or max-content size of an element,
/// since then we ignore its 'inline-size', 'min-inline-size' and 'max-inline-size'.
fn new_for_writing_mode_and_block_size(
writing_mode: WritingMode,
block_size: AuOrAuto,
) -> Self {
impl From<&ConstraintSpace> for IndefiniteContainingBlock {
fn from(constraint_space: &ConstraintSpace) -> Self {
Self {
size: LogicalVec2 {
inline: AuOrAuto::Auto,
block: block_size,
block: constraint_space.block_size.to_auto_or(),
},
writing_mode,
writing_mode: constraint_space.writing_mode,
}
}
}

View file

@ -29,8 +29,8 @@ use crate::geom::{
PhysicalRect, PhysicalVec, Size, ToLogical, ToLogicalWithContainingBlock,
};
use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside};
use crate::{ContainingBlock, DefiniteContainingBlock, IndefiniteContainingBlock};
use crate::style_ext::{ComputedValuesExt, DisplayInside};
use crate::{ConstraintSpace, ContainingBlock, DefiniteContainingBlock, SizeConstraint};
#[derive(Debug, Serialize)]
pub(crate) struct AbsolutelyPositionedBox {
@ -546,17 +546,9 @@ impl HoistedAbsolutelyPositionedBox {
// The inline axis can be fully resolved, computing intrinsic sizes using the
// tentative block size.
let mut inline_axis = inline_axis_solver.solve(Some(|| {
let containing_block_for_children =
IndefiniteContainingBlock::new_for_writing_mode_and_block_size(
style.writing_mode,
block_axis.size,
);
let constraint_space = ConstraintSpace::new(block_axis.size, style.writing_mode);
context
.inline_content_sizes(
layout_context,
&containing_block_for_children,
&containing_block.into(),
)
.inline_content_sizes(layout_context, &constraint_space, &containing_block.into())
.sizes
}));
@ -578,10 +570,10 @@ impl HoistedAbsolutelyPositionedBox {
IndependentFormattingContext::NonReplaced(non_replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
let inline_size = inline_axis.size.non_auto().unwrap();
let inline_size = inline_axis.size.to_definite().unwrap();
let containing_block_for_children = ContainingBlock {
inline_size,
block_size: block_axis.size,
block_size: block_axis.size.to_auto_or(),
style: &style,
};
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
@ -608,7 +600,7 @@ impl HoistedAbsolutelyPositionedBox {
inline_axis = inline_axis_solver.solve_with_size(table_inline_size);
}
let table_block_size = independent_layout.content_block_size;
if block_axis.size != AuOrAuto::LengthPercentage(table_block_size) {
if block_axis.size != SizeConstraint::Definite(table_block_size) {
block_axis = block_axis_solver.solve_with_size(table_block_size);
}
(table_block_size, table_inline_size)
@ -617,7 +609,7 @@ impl HoistedAbsolutelyPositionedBox {
// Now we can properly solve the block size.
block_axis = block_axis_solver
.solve(Some(|| independent_layout.content_block_size.into()));
(block_axis.size.non_auto().unwrap(), inline_size)
(block_axis.size.to_definite().unwrap(), inline_size)
},
};
@ -747,7 +739,7 @@ impl Anchor {
struct AxisResult {
anchor: Anchor,
size: AuOrAuto,
size: SizeConstraint,
margin_start: Au,
margin_end: Au,
}
@ -787,12 +779,15 @@ impl<'a> AbsoluteAxisSolver<'a> {
let content_size = LazyCell::new(get_content_size);
move || *content_size
});
let mut solve_size = |initial_behavior, stretch_size: Au| {
let mut solve_size = |initial_behavior, stretch_size: Au| -> SizeConstraint {
let initial_is_stretch = initial_behavior == Size::Stretch;
let stretch_size = stretch_size.max(Au::zero());
get_content_size
.as_mut()
.map(|mut get_content_size| {
if let Some(mut get_content_size) = get_content_size.as_mut() {
let preferred_size = Some(self.computed_size.resolve(
initial_behavior,
stretch_size,
&mut get_content_size,
));
let min_size = self
.computed_min_size
.resolve_non_initial(stretch_size, &mut get_content_size)
@ -800,15 +795,12 @@ impl<'a> AbsoluteAxisSolver<'a> {
let max_size = self
.computed_max_size
.resolve_non_initial(stretch_size, &mut get_content_size);
self.computed_size
.resolve(initial_behavior, stretch_size, &mut get_content_size)
.clamp_between_extremums(min_size, max_size)
})
.or_else(|| {
self.computed_size
SizeConstraint::new(preferred_size, min_size, max_size)
} else {
let preferred_size = self
.computed_size
.maybe_resolve_extrinsic(Some(stretch_size))
.or(initial_is_stretch.then_some(stretch_size))
.map(|size| {
.or(initial_is_stretch.then_some(stretch_size));
let min_size = self
.computed_min_size
.maybe_resolve_extrinsic(Some(stretch_size))
@ -816,9 +808,8 @@ impl<'a> AbsoluteAxisSolver<'a> {
let max_size = self
.computed_max_size
.maybe_resolve_extrinsic(Some(stretch_size));
size.clamp_between_extremums(min_size, max_size)
})
})
SizeConstraint::new(preferred_size, min_size, max_size)
}
};
let mut solve_for_anchor = |anchor: Anchor| {
let margin_start = self.computed_margin_start.auto_is(Au::zero);
@ -828,8 +819,7 @@ impl<'a> AbsoluteAxisSolver<'a> {
self.padding_border_sum -
margin_start -
margin_end;
let size = solve_size(Size::FitContent, stretch_size)
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage);
let size = solve_size(Size::FitContent, stretch_size);
AxisResult {
anchor,
size,
@ -859,7 +849,9 @@ impl<'a> AbsoluteAxisSolver<'a> {
let stretch_size = free_space -
self.computed_margin_start.auto_is(Au::zero) -
self.computed_margin_end.auto_is(Au::zero);
let used_size = solve_size(Size::Stretch, stretch_size).unwrap();
let used_size = solve_size(Size::Stretch, stretch_size)
.to_definite()
.unwrap();
free_space -= used_size;
let (margin_start, margin_end) =
match (self.computed_margin_start, self.computed_margin_end) {
@ -883,7 +875,7 @@ impl<'a> AbsoluteAxisSolver<'a> {
};
AxisResult {
anchor: Anchor::Start(start),
size: AuOrAuto::LengthPercentage(used_size),
size: SizeConstraint::Definite(used_size),
margin_start,
margin_end,
}

View file

@ -31,7 +31,7 @@ use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFrag
use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize, Size};
use crate::sizing::InlineContentSizesResult;
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM};
use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock};
use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SizeConstraint};
#[derive(Debug, Serialize)]
pub(crate) struct ReplacedContent {
@ -259,36 +259,46 @@ impl ReplacedContent {
})
}
#[inline]
fn content_size(
&self,
axis: Direction,
preferred_aspect_ratio: Option<AspectRatio>,
get_size_in_opposite_axis: &dyn Fn() -> SizeConstraint,
get_fallback_size: &dyn Fn() -> Au,
) -> Au {
let Some(ratio) = preferred_aspect_ratio else {
return get_fallback_size();
};
let transfer = |size| ratio.compute_dependent_size(axis, size);
match get_size_in_opposite_axis() {
SizeConstraint::Definite(size) => transfer(size),
SizeConstraint::MinMax(min_size, max_size) => get_fallback_size()
.clamp_between_extremums(transfer(min_size), max_size.map(transfer)),
}
}
pub fn inline_content_sizes(
&self,
_: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
constraint_space: &ConstraintSpace,
preferred_aspect_ratio: Option<AspectRatio>,
) -> InlineContentSizesResult {
// FIXME: min/max-content of replaced elements is not defined in
// https://dbaron.org/css/intrinsic/
// This seems sensible?
let block_size = containing_block_for_children.size.block;
match (block_size, preferred_aspect_ratio) {
(AuOrAuto::LengthPercentage(block_size), Some(ratio)) => InlineContentSizesResult {
sizes: ratio
.compute_dependent_size(Direction::Inline, block_size)
.into(),
depends_on_block_constraints: true,
},
_ => {
let writing_mode = containing_block_for_children.writing_mode;
InlineContentSizesResult {
sizes: self
.flow_relative_natural_size(writing_mode)
let get_inline_fallback_size = || {
let writing_mode = constraint_space.writing_mode;
self.flow_relative_natural_size(writing_mode)
.inline
.unwrap_or_else(|| {
Self::flow_relative_default_object_size(writing_mode).inline
})
.into(),
depends_on_block_constraints: false,
}
},
.unwrap_or_else(|| Self::flow_relative_default_object_size(writing_mode).inline)
};
let inline_content_size = self.content_size(
Direction::Inline,
preferred_aspect_ratio,
&|| constraint_space.block_size,
&get_inline_fallback_size,
);
InlineContentSizesResult {
sizes: inline_content_size.into(),
depends_on_block_constraints: preferred_aspect_ratio.is_some(),
}
}
@ -529,51 +539,51 @@ impl ReplacedContent {
.map(|block_size| Au::zero().max(block_size - pbm_sums.block));
// <https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes>
// FIXME: Use ReplacedContent::inline_content_sizes() once it's fixed to correctly handle
// min and max constraints.
let inline_content_size = LazyCell::new(|| {
let Some(ratio) = ratio else {
return get_inline_fallback_size();
};
let get_block_size = || {
let block_stretch_size = block_stretch_size.unwrap_or_else(get_block_fallback_size);
let transfer = |size| ratio.compute_dependent_size(Direction::Inline, size);
let min = transfer(
SizeConstraint::new(
box_size
.block
.maybe_resolve_extrinsic(Some(block_stretch_size)),
min_box_size
.block
.maybe_resolve_extrinsic(Some(block_stretch_size))
.unwrap_or_default(),
);
let max = max_box_size
max_box_size
.block
.maybe_resolve_extrinsic(Some(block_stretch_size))
.map(transfer);
box_size
.block
.maybe_resolve_extrinsic(Some(block_stretch_size))
.map_or_else(get_inline_fallback_size, transfer)
.clamp_between_extremums(min, max)
.maybe_resolve_extrinsic(Some(block_stretch_size)),
)
};
self.content_size(
Direction::Inline,
ratio,
&get_block_size,
&get_inline_fallback_size,
)
});
let block_content_size = LazyCell::new(|| {
let Some(ratio) = ratio else {
return get_block_fallback_size();
};
let get_inline_size = || {
let mut get_inline_content_size = || (*inline_content_size).into();
let transfer = |size| ratio.compute_dependent_size(Direction::Block, size);
let min = transfer(
SizeConstraint::new(
box_size
.inline
.maybe_resolve_extrinsic(Some(inline_stretch_size)),
min_box_size
.inline
.resolve_non_initial(inline_stretch_size, &mut get_inline_content_size)
.unwrap_or_default(),
);
let max = max_box_size
max_box_size
.inline
.resolve_non_initial(inline_stretch_size, &mut get_inline_content_size)
.map(transfer);
box_size
.inline
.maybe_resolve_extrinsic(Some(inline_stretch_size))
.map_or_else(get_block_fallback_size, transfer)
.clamp_between_extremums(min, max)
.resolve_non_initial(inline_stretch_size, &mut get_inline_content_size),
)
};
self.content_size(
Direction::Block,
ratio,
&get_inline_size,
&get_block_fallback_size,
)
});
let mut get_inline_content_size = || (*inline_content_size).into();
let mut get_block_content_size = || (*block_content_size).into();

View file

@ -14,7 +14,7 @@ use style::Zero;
use crate::geom::Size;
use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM};
use crate::{AuOrAuto, IndefiniteContainingBlock, LogicalVec2};
use crate::{ConstraintSpace, IndefiniteContainingBlock, LogicalVec2, SizeConstraint};
#[derive(PartialEq)]
pub(crate) enum IntrinsicSizingMode {
@ -120,7 +120,7 @@ pub(crate) fn outer_inline(
containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool,
get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> InlineContentSizesResult,
get_content_size: impl FnOnce(&ConstraintSpace) -> InlineContentSizesResult,
) -> InlineContentSizesResult {
let ContentBoxSizesAndPBM {
content_box_size,
@ -140,7 +140,7 @@ pub(crate) fn outer_inline(
.block
.non_auto()
.map(|v| Au::zero().max(v - pbm_sums.block));
let block_size = if content_box_size.block.is_initial() &&
let preferred_block_size = if content_box_size.block.is_initial() &&
auto_block_size_stretches_to_containing_block
{
depends_on_block_constraints = true;
@ -149,8 +149,7 @@ pub(crate) fn outer_inline(
content_box_size
.block
.maybe_resolve_extrinsic(available_block_size)
}
.map(|block_size| {
};
let min_block_size = content_min_box_size
.block
.maybe_resolve_extrinsic(available_block_size)
@ -158,15 +157,10 @@ pub(crate) fn outer_inline(
let max_block_size = content_max_box_size
.block
.maybe_resolve_extrinsic(available_block_size);
block_size.clamp_between_extremums(min_block_size, max_block_size)
})
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage);
let containing_block_for_children =
IndefiniteContainingBlock::new_for_writing_mode_and_block_size(
get_content_size(&ConstraintSpace::new(
SizeConstraint::new(preferred_block_size, min_block_size, max_block_size),
style.writing_mode,
block_size,
);
get_content_size(&containing_block_for_children)
))
});
let resolve_non_initial = |inline_size| {
Some(match inline_size {

View file

@ -16,7 +16,6 @@ use style::computed_values::empty_cells::T as EmptyCells;
use style::computed_values::position::T as Position;
use style::computed_values::table_layout::T as TableLayoutMode;
use style::computed_values::visibility::T as Visibility;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::values::computed::{
BorderStyle, LengthPercentage as ComputedLengthPercentage, Percentage,
@ -40,7 +39,7 @@ use crate::positioned::{relative_adjustement, PositioningContext, PositioningCon
use crate::sizing::{ContentSizes, InlineContentSizesResult};
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::table::TableSlotCoordinates;
use crate::{ContainingBlock, IndefiniteContainingBlock};
use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, WritingMode};
/// A result of a final or speculative layout of a single cell in
/// the table. Note that this is only done for slots that are not
@ -301,9 +300,7 @@ impl<'a> TableLayout<'a> {
.contents
.inline_content_sizes(
layout_context,
&IndefiniteContainingBlock::new_for_writing_mode(
cell.style.writing_mode,
),
&ConstraintSpace::new_for_style(&cell.style),
)
.sizes
};
@ -776,8 +773,13 @@ impl<'a> TableLayout<'a> {
/// Compute CAPMIN: <https://drafts.csswg.org/css-tables/#capmin>
fn compute_caption_minimum_inline_size(&mut self, layout_context: &LayoutContext) -> Au {
let containing_block =
IndefiniteContainingBlock::new_for_writing_mode(self.table.style.writing_mode);
let containing_block = IndefiniteContainingBlock {
size: LogicalVec2 {
inline: AuOrAuto::Auto,
block: AuOrAuto::Auto,
},
writing_mode: self.table.style.writing_mode,
};
self.table
.captions
.iter()
@ -2630,9 +2632,9 @@ impl Table {
pub(crate) fn inline_content_sizes(
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
constraint_space: &ConstraintSpace,
) -> InlineContentSizesResult {
let writing_mode = containing_block_for_children.writing_mode;
let writing_mode = constraint_space.writing_mode;
let mut layout = TableLayout::new(self);
let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode);

View file

@ -1,2 +0,0 @@
[box-sizing-replaced-001.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[box-sizing-replaced-002.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[box-sizing-replaced-003.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[intrinsic-percent-replaced-017.html]
expected: FAIL

View file

@ -1,6 +0,0 @@
[intrinsic-size-fallback-video.html]
[.wrapper 3]
expected: FAIL
[.wrapper 4]
expected: FAIL