layout: Add an indefinite containing block for intrinsic sizing (#33204)

When computing the min-content or max-content size of an element we
need to ignore `inline-size`, `min-inline-size` and `max-inline-size`.

However, we should take the block-axis sizing properties into account.
That's because the contents could have percentages depending on them,
which can then affect their inline size via an aspect ratio.

Therefore, this patch adds `IndefiniteContainingBlock`, which is similar
to `ContainingBlock`, but it allows an indefinite inline-size. This
struct is then passed arround during intrinsic sizing.

More refinement will be needed in follow-up patches in order to fully
address the problem.

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-29 16:10:46 +02:00 committed by GitHub
parent 46dbe4ce32
commit 93abdf7cb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 600 additions and 332 deletions

View file

@ -9,7 +9,6 @@ use app_units::Au;
use atomic_refcell::AtomicRefMut; use atomic_refcell::AtomicRefMut;
use itertools::izip; use itertools::izip;
use style::computed_values::position::T as Position; use style::computed_values::position::T as Position;
use style::logical_geometry::WritingMode;
use style::properties::longhands::align_items::computed_value::T as AlignItems; 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::align_self::computed_value::T as AlignSelf;
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing; use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
@ -35,7 +34,7 @@ use crate::positioned::{
}; };
use crate::sizing::{ContentSizes, IntrinsicSizingMode}; use crate::sizing::{ContentSizes, IntrinsicSizingMode};
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::ContainingBlock; use crate::{ContainingBlock, IndefiniteContainingBlock};
// FIMXE: “Flex items […] `z-index` values other than `auto` create a stacking context // FIMXE: “Flex items […] `z-index` values other than `auto` create a stacking context
// even if `position` is `static` (behaving exactly as if `position` were `relative`).” // even if `position` is `static` (behaving exactly as if `position` were `relative`).”
@ -305,19 +304,27 @@ impl FlexContainer {
pub fn inline_content_sizes( pub fn inline_content_sizes(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
writing_mode: WritingMode, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> ContentSizes {
match self.config.flex_axis { match self.config.flex_axis {
FlexAxis::Row => self.main_content_sizes(layout_context, writing_mode, || { FlexAxis::Row => {
unreachable!( self.main_content_sizes(layout_context, containing_block_for_children, || {
"Unexpected FlexContext query during row flex intrinsic size calculation." unreachable!(
) "Unexpected FlexContext query during row flex intrinsic size calculation."
}), )
FlexAxis::Column => self.cross_content_sizes(layout_context), })
},
FlexAxis::Column => {
self.cross_content_sizes(layout_context, containing_block_for_children)
},
} }
} }
fn cross_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes { fn cross_content_sizes(
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
// <https://drafts.csswg.org/css-flexbox/#intrinsic-cross-sizes> // <https://drafts.csswg.org/css-flexbox/#intrinsic-cross-sizes>
assert_eq!( assert_eq!(
self.config.flex_axis, self.config.flex_axis,
@ -331,10 +338,15 @@ impl FlexContainer {
FlexLevelBox::FlexItem(item) => { FlexLevelBox::FlexItem(item) => {
// TODO: For the max-content size we should distribute items into // TODO: For the max-content size we should distribute items into
// columns, and sum the column sizes and gaps. // columns, and sum the column sizes and gaps.
content_sizes.max_assign( // TODO: Use the proper automatic minimum size.
item.independent_formatting_context let ifc = &mut item.independent_formatting_context;
.inline_content_sizes(layout_context), content_sizes.max_assign(ifc.inline_content_sizes(
); layout_context,
&containing_block_for_children.new_for_intrinsic_inline_size_of_child(
&ifc.style().clone(),
&LogicalVec2::zero(),
),
));
}, },
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {}, FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
} }
@ -345,7 +357,7 @@ impl FlexContainer {
fn main_content_sizes<'a>( fn main_content_sizes<'a>(
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
writing_mode: WritingMode, containing_block_for_children: &IndefiniteContainingBlock,
flex_context_getter: impl Fn() -> &'a FlexContext<'a>, flex_context_getter: impl Fn() -> &'a FlexContext<'a>,
) -> ContentSizes { ) -> ContentSizes {
// - TODO: calculate intrinsic cross sizes when container is a column // - TODO: calculate intrinsic cross sizes when container is a column
@ -372,7 +384,7 @@ impl FlexContainer {
let info = item.main_content_size_info( let info = item.main_content_size_info(
layout_context, layout_context,
writing_mode, containing_block_for_children,
container_is_horizontal, container_is_horizontal,
self.config.flex_axis, self.config.flex_axis,
self.config.main_start_cross_start_sides_are, self.config.main_start_cross_start_sides_are,
@ -514,7 +526,7 @@ impl FlexContainer {
let container_main_size = match self.config.flex_axis { let container_main_size = match self.config.flex_axis {
FlexAxis::Row => containing_block.inline_size, FlexAxis::Row => containing_block.inline_size,
FlexAxis::Column => containing_block.block_size.auto_is(|| { FlexAxis::Column => containing_block.block_size.auto_is(|| {
self.main_content_sizes(layout_context, self.style.writing_mode, || &flex_context) self.main_content_sizes(layout_context, &containing_block.into(), || &flex_context)
.max_content .max_content
}), }),
}; };
@ -905,16 +917,17 @@ fn allocate_free_cross_space_for_flex_line(
impl<'a> FlexItem<'a> { impl<'a> FlexItem<'a> {
fn new(flex_context: &FlexContext, box_: &'a mut FlexItemBox) -> Self { fn new(flex_context: &FlexContext, box_: &'a mut FlexItemBox) -> Self {
let containing_block = flex_context.containing_block; let containing_block = flex_context.containing_block;
let parent_writing_mode = containing_block.effective_writing_mode();
let item_writing_mode = box_.style().effective_writing_mode();
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
assert_eq!( assert_eq!(
containing_block.effective_writing_mode(), parent_writing_mode, item_writing_mode,
box_.style().effective_writing_mode(),
"Mixed writing modes are not supported yet" "Mixed writing modes are not supported yet"
); );
let container_is_horizontal = containing_block.effective_writing_mode().is_horizontal(); let container_is_horizontal = parent_writing_mode.is_horizontal();
let item_is_horizontal = box_.style().effective_writing_mode().is_horizontal(); let item_is_horizontal = item_writing_mode.is_horizontal();
let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis( let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
container_is_horizontal, container_is_horizontal,
item_is_horizontal, item_is_horizontal,
@ -979,6 +992,8 @@ impl<'a> FlexItem<'a> {
flex_context.container_definite_inner_size, flex_context.container_definite_inner_size,
cross_axis_is_item_block_axis, cross_axis_is_item_block_axis,
flex_relative_content_box_size, flex_relative_content_box_size,
flex_relative_content_min_size,
flex_relative_content_max_size,
padding_border, padding_border,
|item| { |item| {
let min_size = flex_context let min_size = flex_context
@ -1585,6 +1600,7 @@ impl FlexItem<'_> {
); );
// … and also the items inline axis. // … and also the items inline axis.
let flex_axis = flex_context.config.flex_axis;
match &mut self.box_.independent_formatting_context { match &mut self.box_.independent_formatting_context {
IndependentFormattingContext::Replaced(replaced) => { IndependentFormattingContext::Replaced(replaced) => {
let pbm = replaced.style.padding_border_margin(containing_block); let pbm = replaced.style.padding_border_margin(containing_block);
@ -1602,11 +1618,7 @@ impl FlexItem<'_> {
box_size, box_size,
&pbm, &pbm,
); );
let cross_size = flex_context let cross_size = flex_axis.vec2_to_flex_relative(size).cross;
.config
.flex_axis
.vec2_to_flex_relative(size)
.cross;
let fragments = replaced.contents.make_fragments( let fragments = replaced.contents.make_fragments(
&replaced.style, &replaced.style,
size.to_physical_size(container_writing_mode), size.to_physical_size(container_writing_mode),
@ -1642,23 +1654,34 @@ impl FlexItem<'_> {
.effective_writing_mode() .effective_writing_mode()
.is_horizontal(), .is_horizontal(),
item_is_horizontal, item_is_horizontal,
flex_context.config.flex_axis, flex_axis,
); );
let (inline_size, block_size) = if cross_axis_is_item_block_axis { let (inline_size, block_size) = if cross_axis_is_item_block_axis {
(used_main_size, cross_size) (used_main_size, cross_size)
} else { } else {
( (
cross_size.auto_is(|| { cross_size.auto_is(|| {
let content_contributions = non_replaced.outer_inline_content_sizes( let style = non_replaced.style.clone();
flex_context.layout_context, let containing_block_for_children =
container_writing_mode, IndefiniteContainingBlock::new_for_style_and_block_size(
Au::zero, &style,
); AuOrAuto::LengthPercentage(used_main_size),
containing_block );
.inline_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) .min(content_contributions.max_content)
.max(content_contributions.min_content) - .max(content_contributions.min_content)
self.pbm_auto_is_zero.cross
}), }),
// The main size of a flex item is considered to be definite if its flex basis is definite // 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. // or the flex container has a definite main size.
@ -1874,7 +1897,7 @@ impl FlexItemBox {
fn main_content_size_info<'a>( fn main_content_size_info<'a>(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
container_writing_mode: WritingMode, containing_block: &IndefiniteContainingBlock,
container_is_horizontal: bool, container_is_horizontal: bool,
flex_axis: FlexAxis, flex_axis: FlexAxis,
main_start_cross_start: MainStartCrossStart, main_start_cross_start: MainStartCrossStart,
@ -1886,25 +1909,8 @@ impl FlexItemBox {
let cross_axis_is_item_block_axis = let cross_axis_is_item_block_axis =
cross_axis_is_item_block_axis(container_is_horizontal, item_is_horizontal, flex_axis); cross_axis_is_item_block_axis(container_is_horizontal, item_is_horizontal, flex_axis);
let pbm = style.padding_border_margin_for_intrinsic_size(item_writing_mode); let (content_box_size, content_min_size, content_max_size, pbm) =
let box_size = style style.content_box_sizes_and_padding_border_margin(containing_block);
.box_size(item_writing_mode)
.map(|v| v.percentage_relative_to(Length::zero()));
let content_box_size = style
.content_box_size_for_box_size(box_size, &pbm)
.map(|v| v.map(Au::from));
let min_size = style
.min_box_size(item_writing_mode)
.map(|v| v.percentage_relative_to(Length::zero()));
let content_min_size = style
.content_min_box_size_for_min_size(min_size, &pbm)
.map(|v| v.map(Au::from));
let max_size = style
.max_box_size(item_writing_mode)
.map(|v| v.map(|v| v.percentage_relative_to(Length::zero())));
let content_max_size = style
.content_max_box_size_for_max_size(max_size, &pbm)
.map(|v| v.map(Au::from));
let automatic_min_size = self.automatic_min_size( let automatic_min_size = self.automatic_min_size(
layout_context, layout_context,
cross_axis_is_item_block_axis, cross_axis_is_item_block_axis,
@ -1942,9 +1948,11 @@ impl FlexItemBox {
let content_contribution_sizes = match flex_axis { let content_contribution_sizes = match flex_axis {
FlexAxis::Row => self FlexAxis::Row => self
.independent_formatting_context .independent_formatting_context
.outer_inline_content_sizes(layout_context, container_writing_mode, || { .outer_inline_content_sizes(
automatic_min_size layout_context,
}), containing_block,
&content_min_size_no_auto,
),
FlexAxis::Column => self FlexAxis::Column => self
.layout_for_block_content_size( .layout_for_block_content_size(
flex_context_getter(), flex_context_getter(),
@ -1982,6 +1990,8 @@ impl FlexItemBox {
}, },
cross_axis_is_item_block_axis, cross_axis_is_item_block_axis,
content_box_size, content_box_size,
content_min_size_no_auto,
content_max_size,
padding_border, padding_border,
block_content_size_callback, block_content_size_callback,
); );
@ -2142,8 +2152,14 @@ impl FlexItemBox {
// > preferred aspect ratio, by any definite minimum and maximum cross sizes converted through the // > preferred aspect ratio, by any definite minimum and maximum cross sizes converted through the
// > aspect ratio. // > aspect ratio.
let main_content_size = if cross_axis_is_item_block_axis { 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);
self.independent_formatting_context self.independent_formatting_context
.inline_content_sizes(layout_context) .inline_content_sizes(layout_context, &containing_block_for_children)
.min_content .min_content
} else { } else {
block_content_size_callback(self) block_content_size_callback(self)
@ -2178,16 +2194,19 @@ impl FlexItemBox {
container_definite_inner_size: FlexRelativeVec2<Option<Au>>, container_definite_inner_size: FlexRelativeVec2<Option<Au>>,
cross_axis_is_item_block_axis: bool, cross_axis_is_item_block_axis: bool,
content_box_size: FlexRelativeVec2<AuOrAuto>, content_box_size: FlexRelativeVec2<AuOrAuto>,
min_size: FlexRelativeVec2<Au>,
max_size: FlexRelativeVec2<Option<Au>>,
padding_border_sums: FlexRelativeVec2<Au>, padding_border_sums: FlexRelativeVec2<Au>,
block_content_size_callback: impl FnOnce(&mut FlexItemBox) -> Au, block_content_size_callback: impl FnOnce(&mut FlexItemBox) -> Au,
) -> (Au, bool) { ) -> (Au, bool) {
let flex_item = &mut self.independent_formatting_context; let flex_item = &mut self.independent_formatting_context;
let style = flex_item.style();
let used_flex_basis = match &flex_item.style().get_position().flex_basis { let used_flex_basis = match &style.get_position().flex_basis {
FlexBasis::Content => FlexBasis::Content, FlexBasis::Content => FlexBasis::Content,
FlexBasis::Size(Size::LengthPercentage(length_percentage)) => { FlexBasis::Size(Size::LengthPercentage(length_percentage)) => {
let apply_box_sizing = |length: Au| { let apply_box_sizing = |length: Au| {
match flex_item.style().get_position().box_sizing { match style.get_position().box_sizing {
BoxSizing::ContentBox => length, BoxSizing::ContentBox => length,
BoxSizing::BorderBox => { BoxSizing::BorderBox => {
// This may make `length` negative, // This may make `length` negative,
@ -2250,7 +2269,15 @@ impl FlexItemBox {
let flex_basis = if cross_axis_is_item_block_axis { 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 // The main axis is the inline axis, so we can get the content size from the normal
// preferred widths calculation. // preferred widths calculation.
flex_item.inline_content_sizes(layout_context).max_content let style = style.clone();
let block_size = content_box_size
.cross
.map(|v| v.clamp_between_extremums(min_size.cross, max_size.cross));
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size);
flex_item
.inline_content_sizes(layout_context, &containing_block_for_children)
.max_content
} else { } else {
block_content_size_callback(self) block_content_size_callback(self)
}; };
@ -2305,8 +2332,14 @@ impl FlexItemBox {
if will_stretch { if will_stretch {
containing_block_inline_size_minus_pbm containing_block_inline_size_minus_pbm
} else { } else {
let inline_content_sizes = let style = non_replaced.style.clone();
non_replaced.inline_content_sizes(flex_context.layout_context); let containing_block_for_children =
IndefiniteContainingBlock::new_for_style(&style);
let inline_content_sizes = non_replaced.inline_content_sizes(
flex_context.layout_context,
&containing_block_for_children,
);
containing_block_inline_size_minus_pbm containing_block_inline_size_minus_pbm
.min(inline_content_sizes.max_content) .min(inline_content_sizes.max_content)
.max(inline_content_sizes.min_content) .max(inline_content_sizes.min_content)

View file

@ -18,7 +18,7 @@ use servo_arc::Arc;
use style::computed_values::float::T as FloatProperty; use style::computed_values::float::T as FloatProperty;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Clear, Length}; use style::values::computed::Clear;
use style::values::specified::text::TextDecorationLine; use style::values::specified::text::TextDecorationLine;
use crate::context::LayoutContext; use crate::context::LayoutContext;
@ -28,8 +28,8 @@ use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin}; use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin};
use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
use crate::positioned::PositioningContext; use crate::positioned::PositioningContext;
use crate::style_ext::{ComputedValuesExt, DisplayInside, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside, PaddingBorderMargin};
use crate::ContainingBlock; use crate::{ContainingBlock, IndefiniteContainingBlock};
/// A floating box. /// A floating box.
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -905,35 +905,44 @@ impl FloatBox {
IndependentFormattingContext::NonReplaced(ref mut non_replaced) => { IndependentFormattingContext::NonReplaced(ref mut non_replaced) => {
// Calculate inline size. // Calculate inline size.
// https://drafts.csswg.org/css2/#float-width // https://drafts.csswg.org/css2/#float-width
let box_size = non_replaced.style.content_box_size(containing_block, &pbm); let style = non_replaced.style.clone();
let max_box_size = non_replaced let box_size = style
.style .content_box_size(containing_block, &pbm)
.content_max_box_size(containing_block, &pbm); .map(|v| v.map(Au::from));
let min_box_size = non_replaced let max_box_size = style
.style .content_max_box_size(containing_block, &pbm)
.map(|v| v.map(Au::from));
let min_box_size = style
.content_min_box_size(containing_block, &pbm) .content_min_box_size(containing_block, &pbm)
.auto_is(Length::zero); .map(|v| v.map(Au::from))
.auto_is(Au::zero);
let tentative_inline_size = box_size.inline.auto_is(|| {
let available_size =
containing_block.inline_size - pbm_sums.inline_sum();
non_replaced
.inline_content_sizes(layout_context)
.shrink_to_fit(available_size)
.into()
});
let inline_size = tentative_inline_size
.clamp_between_extremums(min_box_size.inline, max_box_size.inline);
let block_size = box_size.block.map(|size| { let block_size = box_size.block.map(|size| {
size.clamp_between_extremums(min_box_size.block, max_box_size.block) size.clamp_between_extremums(min_box_size.block, max_box_size.block)
}); });
let tentative_inline_size = box_size.inline.auto_is(|| {
let available_size =
containing_block.inline_size - pbm_sums.inline_sum();
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(
&style, block_size,
);
non_replaced
.inline_content_sizes(
layout_context,
&containing_block_for_children,
)
.shrink_to_fit(available_size)
});
let inline_size = tentative_inline_size
.clamp_between_extremums(min_box_size.inline, max_box_size.inline);
// Calculate block size. // Calculate block size.
// https://drafts.csswg.org/css2/#block-root-margin // https://drafts.csswg.org/css2/#block-root-margin
// FIXME(pcwalton): Is a tree rank of zero correct here? // FIXME(pcwalton): Is a tree rank of zero correct here?
let containing_block_for_children = ContainingBlock { let containing_block_for_children = ContainingBlock {
inline_size: inline_size.into(), inline_size,
block_size: block_size.map(|t| t.into()), block_size,
style: &non_replaced.style, style: &non_replaced.style,
}; };
let independent_layout = non_replaced.layout( let independent_layout = non_replaced.layout(
@ -944,13 +953,13 @@ impl FloatBox {
); );
let (block_size, inline_size) = let (block_size, inline_size) =
match independent_layout.content_inline_size_for_table { match independent_layout.content_inline_size_for_table {
Some(inline_size) => ( Some(inline_size) => {
independent_layout.content_block_size.into(), (independent_layout.content_block_size, inline_size)
inline_size.into(), },
),
None => ( None => (
block_size.auto_is(|| { block_size.auto_is(|| {
Length::from(independent_layout.content_block_size) independent_layout
.content_block_size
.clamp_between_extremums( .clamp_between_extremums(
min_box_size.block, min_box_size.block,
max_box_size.block, max_box_size.block,
@ -960,8 +969,8 @@ impl FloatBox {
), ),
}; };
content_size = LogicalVec2 { content_size = LogicalVec2 {
inline: inline_size.into(), inline: inline_size,
block: block_size.into(), block: block_size,
}; };
children = independent_layout.fragments; children = independent_layout.fragments;
}, },

View file

@ -94,7 +94,6 @@ use style::computed_values::text_wrap_mode::T as TextWrapMode;
use style::computed_values::vertical_align::T as VerticalAlign; use style::computed_values::vertical_align::T as VerticalAlign;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
use style::context::QuirksMode; use style::context::QuirksMode;
use style::logical_geometry::WritingMode;
use style::properties::style_structs::InheritedText; use style::properties::style_structs::InheritedText;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Clear, Length}; use style::values::computed::{Clear, Length};
@ -128,7 +127,7 @@ use crate::geom::{LogicalRect, LogicalVec2, PhysicalRect, ToLogical};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::ContainingBlock; use crate::{ContainingBlock, IndefiniteContainingBlock};
// From gfxFontConstants.h in Firefox. // From gfxFontConstants.h in Firefox.
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
@ -1557,9 +1556,9 @@ impl InlineFormattingContext {
pub(super) fn inline_content_sizes( pub(super) fn inline_content_sizes(
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_writing_mode: WritingMode, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> ContentSizes {
ContentSizesComputation::compute(self, layout_context, containing_block_writing_mode) ContentSizesComputation::compute(self, layout_context, containing_block_for_children)
} }
pub(super) fn layout( pub(super) fn layout(
@ -1954,13 +1953,19 @@ impl IndependentFormattingContext {
.content_min_box_size(layout.containing_block, &pbm) .content_min_box_size(layout.containing_block, &pbm)
.map(|v| v.map(Au::from)) .map(|v| v.map(Au::from))
.auto_is(Au::zero); .auto_is(Au::zero);
let block_size = box_size
.block
.map(|v| v.clamp_between_extremums(min_box_size.block, max_box_size.block));
// https://drafts.csswg.org/css2/visudet.html#inlineblock-width // https://drafts.csswg.org/css2/visudet.html#inlineblock-width
let tentative_inline_size = box_size.inline.auto_is(|| { let tentative_inline_size = box_size.inline.auto_is(|| {
let style = non_replaced.style.clone();
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size);
let available_size = let available_size =
layout.containing_block.inline_size - pbm_sums.inline_sum(); layout.containing_block.inline_size - pbm_sums.inline_sum();
non_replaced non_replaced
.inline_content_sizes(layout.layout_context) .inline_content_sizes(layout.layout_context, &containing_block_for_children)
.shrink_to_fit(available_size) .shrink_to_fit(available_size)
}); });
@ -1969,9 +1974,6 @@ impl IndependentFormattingContext {
// always results in that size. // always results in that size.
let inline_size = tentative_inline_size let inline_size = tentative_inline_size
.clamp_between_extremums(min_box_size.inline, max_box_size.inline); .clamp_between_extremums(min_box_size.inline, max_box_size.inline);
let block_size = box_size
.block
.map(|v| v.clamp_between_extremums(min_box_size.block, max_box_size.block));
let containing_block_for_children = ContainingBlock { let containing_block_for_children = ContainingBlock {
inline_size, inline_size,
@ -2259,7 +2261,7 @@ fn inline_container_needs_strut(
/// A struct which takes care of computing [`ContentSizes`] for an [`InlineFormattingContext`]. /// A struct which takes care of computing [`ContentSizes`] for an [`InlineFormattingContext`].
struct ContentSizesComputation<'layout_data> { struct ContentSizesComputation<'layout_data> {
layout_context: &'layout_data LayoutContext<'layout_data>, layout_context: &'layout_data LayoutContext<'layout_data>,
containing_block_writing_mode: WritingMode, containing_block: &'layout_data IndefiniteContainingBlock<'layout_data>,
paragraph: ContentSizes, paragraph: ContentSizes,
current_line: ContentSizes, current_line: ContentSizes,
/// Size for whitepsace pending to be added to this line. /// Size for whitepsace pending to be added to this line.
@ -2297,14 +2299,14 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
let zero = Length::zero(); let zero = Length::zero();
let padding = inline_box let padding = inline_box
.style .style
.padding(self.containing_block_writing_mode) .padding(self.containing_block.style.writing_mode)
.percentages_relative_to(zero); .percentages_relative_to(zero);
let border = inline_box let border = inline_box
.style .style
.border_width(self.containing_block_writing_mode); .border_width(self.containing_block.style.writing_mode);
let margin = inline_box let margin = inline_box
.style .style
.margin(self.containing_block_writing_mode) .margin(self.containing_block.style.writing_mode)
.percentages_relative_to(zero) .percentages_relative_to(zero)
.auto_is(Length::zero); .auto_is(Length::zero);
@ -2379,8 +2381,8 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
let outer = atomic.outer_inline_content_sizes( let outer = atomic.outer_inline_content_sizes(
self.layout_context, self.layout_context,
self.containing_block_writing_mode, self.containing_block,
Au::zero, &LogicalVec2::zero(),
); );
if !inline_formatting_context if !inline_formatting_context
@ -2428,11 +2430,11 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
fn compute( fn compute(
inline_formatting_context: &InlineFormattingContext, inline_formatting_context: &InlineFormattingContext,
layout_context: &'layout_data LayoutContext, layout_context: &'layout_data LayoutContext,
containing_block_writing_mode: WritingMode, containing_block: &'layout_data IndefiniteContainingBlock,
) -> ContentSizes { ) -> ContentSizes {
Self { Self {
layout_context, layout_context,
containing_block_writing_mode, containing_block,
paragraph: ContentSizes::zero(), paragraph: ContentSizes::zero(),
current_line: ContentSizes::zero(), current_line: ContentSizes::zero(),
pending_whitespace: Au::zero(), pending_whitespace: Au::zero(),

View file

@ -36,7 +36,7 @@ use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, Positioning
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use crate::sizing::{self, ContentSizes}; use crate::sizing::{self, ContentSizes};
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::ContainingBlock; use crate::{ContainingBlock, IndefiniteContainingBlock};
mod construct; mod construct;
pub mod float; pub mod float;
@ -227,10 +227,10 @@ impl OutsideMarker {
sequential_layout_state: Option<&mut SequentialLayoutState>, sequential_layout_state: Option<&mut SequentialLayoutState>,
collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>, collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
) -> Fragment { ) -> Fragment {
let containing_block_writing_mode = containing_block.effective_writing_mode(); let content_sizes = self.block_container.inline_content_sizes(
let content_sizes = self layout_context,
.block_container &IndefiniteContainingBlock::new_for_style(&self.marker_style),
.inline_content_sizes(layout_context, containing_block_writing_mode); );
let containing_block_for_children = ContainingBlock { let containing_block_for_children = ContainingBlock {
inline_size: content_sizes.max_content, inline_size: content_sizes.max_content,
block_size: AuOrAuto::auto(), block_size: AuOrAuto::auto(),
@ -244,6 +244,8 @@ impl OutsideMarker {
sequential_layout_state, sequential_layout_state,
collapsible_with_parent_start_margin.unwrap_or(CollapsibleWithParentStartMargin(false)), collapsible_with_parent_start_margin.unwrap_or(CollapsibleWithParentStartMargin(false)),
); );
let containing_block_writing_mode = containing_block.effective_writing_mode();
let max_inline_size = let max_inline_size =
flow_layout flow_layout
.fragments .fragments
@ -357,7 +359,7 @@ impl BlockFormattingContext {
fn calculate_inline_content_size_for_block_level_boxes( fn calculate_inline_content_size_for_block_level_boxes(
boxes: &[ArcRefCell<BlockLevelBox>], boxes: &[ArcRefCell<BlockLevelBox>],
layout_context: &LayoutContext, layout_context: &LayoutContext,
writing_mode: WritingMode, containing_block: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> ContentSizes {
let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| { let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
match &mut *box_.borrow_mut() { match &mut *box_.borrow_mut() {
@ -366,7 +368,11 @@ fn calculate_inline_content_size_for_block_level_boxes(
BlockLevelBox::OutOfFlowFloatBox(ref mut float_box) => { BlockLevelBox::OutOfFlowFloatBox(ref mut float_box) => {
let size = float_box let size = float_box
.contents .contents
.outer_inline_content_sizes(layout_context, writing_mode, Au::zero) .outer_inline_content_sizes(
layout_context,
containing_block,
&LogicalVec2::zero(),
)
.max(ContentSizes::zero()); .max(ContentSizes::zero());
let style_box = &float_box.contents.style().get_box(); let style_box = &float_box.contents.style().get_box();
Some((size, style_box.float, style_box.clear)) Some((size, style_box.float, style_box.clear))
@ -376,12 +382,11 @@ fn calculate_inline_content_size_for_block_level_boxes(
} => { } => {
let size = sizing::outer_inline( let size = sizing::outer_inline(
style, style,
writing_mode, &containing_block,
|| { &LogicalVec2::zero(),
contents |containing_block_for_children| {
.inline_content_sizes(layout_context, style.effective_writing_mode()) contents.inline_content_sizes(layout_context, containing_block_for_children)
}, },
Au::zero,
) )
.max(ContentSizes::zero()); .max(ContentSizes::zero());
// A block in the same BFC can overlap floats, it's not moved next to them, // A block in the same BFC can overlap floats, it's not moved next to them,
@ -391,7 +396,11 @@ fn calculate_inline_content_size_for_block_level_boxes(
}, },
BlockLevelBox::Independent(ref mut independent) => { BlockLevelBox::Independent(ref mut independent) => {
let size = independent let size = independent
.outer_inline_content_sizes(layout_context, writing_mode, Au::zero) .outer_inline_content_sizes(
layout_context,
containing_block,
&LogicalVec2::zero(),
)
.max(ContentSizes::zero()); .max(ContentSizes::zero());
Some((size, Float::None, independent.style().get_box().clear)) Some((size, Float::None, independent.style().get_box().clear))
}, },
@ -498,16 +507,16 @@ impl BlockContainer {
pub(super) fn inline_content_sizes( pub(super) fn inline_content_sizes(
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
writing_mode: WritingMode, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> ContentSizes {
match &self { match &self {
Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes( Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes(
boxes, boxes,
layout_context, layout_context,
writing_mode, containing_block_for_children,
), ),
Self::InlineFormattingContext(context) => { Self::InlineFormattingContext(context) => {
context.inline_content_sizes(layout_context, writing_mode) context.inline_content_sizes(layout_context, containing_block_for_children)
}, },
} }
} }

View file

@ -5,7 +5,7 @@
use app_units::Au; use app_units::Au;
use serde::Serialize; use serde::Serialize;
use servo_arc::Arc; use servo_arc::Arc;
use style::logical_geometry::WritingMode; use style::logical_geometry::Direction;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::selector_parser::PseudoElement; use style::selector_parser::PseudoElement;
use style::values::specified::text::TextDecorationLine; use style::values::specified::text::TextDecorationLine;
@ -19,9 +19,9 @@ use crate::fragment_tree::{BaseFragmentInfo, Fragment, FragmentFlags};
use crate::positioned::PositioningContext; use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use crate::sizing::{self, ContentSizes}; use crate::sizing::{self, ContentSizes};
use crate::style_ext::{ComputedValuesExt, DisplayInside}; use crate::style_ext::{AspectRatio, DisplayInside};
use crate::table::Table; use crate::table::Table;
use crate::ContainingBlock; use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock, LogicalVec2};
/// <https://drafts.csswg.org/css-display/#independent-formatting-context> /// <https://drafts.csswg.org/css-display/#independent-formatting-context>
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -36,7 +36,7 @@ pub(crate) struct NonReplacedFormattingContext {
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
/// If it was requested during construction /// If it was requested during construction
pub content_sizes: Option<ContentSizes>, pub content_sizes: Option<(AuOrAuto, ContentSizes)>,
pub contents: NonReplacedFormattingContextContents, pub contents: NonReplacedFormattingContextContents,
} }
@ -176,30 +176,56 @@ impl IndependentFormattingContext {
} }
} }
pub fn inline_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes { pub(crate) fn inline_content_sizes(
&mut self,
layout_context: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
match self { match self {
Self::NonReplaced(inner) => inner.inline_content_sizes(layout_context), Self::NonReplaced(inner) => {
Self::Replaced(inner) => inner.contents.inline_content_sizes(&inner.style), inner.inline_content_sizes(layout_context, containing_block_for_children)
},
Self::Replaced(inner) => inner
.contents
.inline_content_sizes(layout_context, containing_block_for_children),
} }
} }
pub fn outer_inline_content_sizes( pub(crate) fn outer_inline_content_sizes(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_writing_mode: WritingMode, containing_block: &IndefiniteContainingBlock,
get_auto_minimum: impl FnOnce() -> Au, auto_minimum: &LogicalVec2<Au>,
) -> ContentSizes { ) -> ContentSizes {
match self { match self {
Self::NonReplaced(non_replaced) => non_replaced.outer_inline_content_sizes( Self::NonReplaced(non_replaced) => sizing::outer_inline(
layout_context, &non_replaced.style.clone(),
containing_block_writing_mode, containing_block,
get_auto_minimum, auto_minimum,
|containing_block_for_children| {
non_replaced.inline_content_sizes(layout_context, containing_block_for_children)
},
), ),
Self::Replaced(replaced) => sizing::outer_inline( Self::Replaced(replaced) => sizing::outer_inline(
&replaced.style, &replaced.style,
containing_block_writing_mode, containing_block,
|| replaced.contents.inline_content_sizes(&replaced.style), auto_minimum,
get_auto_minimum, |containing_block_for_children| {
match (
containing_block_for_children.size.block,
replaced.preferred_aspect_ratio(containing_block),
) {
(AuOrAuto::LengthPercentage(block_size), Some(ratio)) => {
return ratio
.compute_dependent_size(Direction::Inline, block_size)
.into();
},
_ => {},
}
replaced
.contents
.inline_content_sizes(layout_context, containing_block_for_children)
},
), ),
} }
} }
@ -234,46 +260,59 @@ impl NonReplacedFormattingContext {
} }
} }
pub fn inline_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes { pub(crate) fn inline_content_sizes(
let writing_mode = self.style.effective_writing_mode();
let contents = &mut self.contents;
*self
.content_sizes
.get_or_insert_with(|| contents.inline_content_sizes(layout_context, writing_mode))
}
pub fn outer_inline_content_sizes(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_writing_mode: WritingMode, containing_block_for_children: &IndefiniteContainingBlock,
get_auto_minimum: impl FnOnce() -> Au,
) -> ContentSizes { ) -> ContentSizes {
sizing::outer_inline( assert_eq!(
&self.style, containing_block_for_children.size.inline,
containing_block_writing_mode, AuOrAuto::Auto,
|| { "inline_content_sizes() got non-auto containing block inline-size",
*self.content_sizes.get_or_insert_with(|| { );
self.contents if let Some((previous_cb_block_size, result)) = self.content_sizes {
.inline_content_sizes(layout_context, self.style.effective_writing_mode()) if previous_cb_block_size == containing_block_for_children.size.block {
}) return result;
}, }
get_auto_minimum, // TODO: Should we keep multiple caches for various block sizes?
) }
self.content_sizes
.insert((
containing_block_for_children.size.block,
self.contents
.inline_content_sizes(layout_context, &containing_block_for_children),
))
.1
} }
} }
impl NonReplacedFormattingContextContents { impl NonReplacedFormattingContextContents {
pub fn inline_content_sizes( pub(crate) fn inline_content_sizes(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
writing_mode: WritingMode, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> ContentSizes {
match self { match self {
Self::Flow(inner) => inner Self::Flow(inner) => inner
.contents .contents
.inline_content_sizes(layout_context, writing_mode), .inline_content_sizes(layout_context, containing_block_for_children),
Self::Flex(inner) => inner.inline_content_sizes(layout_context, writing_mode), Self::Flex(inner) => {
Self::Table(table) => table.inline_content_sizes(layout_context, writing_mode), inner.inline_content_sizes(layout_context, containing_block_for_children)
},
Self::Table(table) => {
table.inline_content_sizes(layout_context, containing_block_for_children)
},
} }
} }
} }
impl ReplacedFormattingContext {
pub(crate) fn preferred_aspect_ratio(
&self,
containing_block: &IndefiniteContainingBlock,
) -> Option<AspectRatio> {
self.contents
.preferred_aspect_ratio(containing_block, &self.style)
}
}

View file

@ -24,7 +24,7 @@ pub type AuOrAuto = AutoOr<Au>;
pub type LengthOrAuto = AutoOr<Length>; pub type LengthOrAuto = AutoOr<Length>;
pub type LengthPercentageOrAuto<'a> = AutoOr<&'a LengthPercentage>; pub type LengthPercentageOrAuto<'a> = AutoOr<&'a LengthPercentage>;
#[derive(Clone, Copy, Serialize)] #[derive(Clone, Copy, PartialEq, Serialize)]
pub struct LogicalVec2<T> { pub struct LogicalVec2<T> {
pub inline: T, pub inline: T,
pub block: T, pub block: T,
@ -150,7 +150,7 @@ impl<T: Clone> LogicalVec2<AutoOr<T>> {
} }
impl LogicalVec2<LengthPercentageOrAuto<'_>> { impl LogicalVec2<LengthPercentageOrAuto<'_>> {
pub fn percentages_relative_to( pub(crate) fn percentages_relative_to(
&self, &self,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
) -> LogicalVec2<LengthOrAuto> { ) -> LogicalVec2<LengthOrAuto> {
@ -165,8 +165,32 @@ impl LogicalVec2<LengthPercentageOrAuto<'_>> {
} }
} }
impl LogicalVec2<LengthPercentageOrAuto<'_>> {
pub(crate) fn percentages_relative_to_basis(
&self,
basis: &LogicalVec2<Length>,
) -> LogicalVec2<LengthOrAuto> {
LogicalVec2 {
inline: self.inline.percentage_relative_to(basis.inline),
block: self.block.percentage_relative_to(basis.block),
}
}
}
impl LogicalVec2<LengthPercentageOrAuto<'_>> {
pub(crate) fn maybe_percentages_relative_to_basis(
&self,
basis: &LogicalVec2<Option<Length>>,
) -> LogicalVec2<LengthOrAuto> {
LogicalVec2 {
inline: self.inline.maybe_percentage_relative_to(basis.inline),
block: self.block.maybe_percentage_relative_to(basis.block),
}
}
}
impl LogicalVec2<Option<&'_ LengthPercentage>> { impl LogicalVec2<Option<&'_ LengthPercentage>> {
pub fn percentages_relative_to( pub(crate) fn percentages_relative_to(
&self, &self,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
) -> LogicalVec2<Option<Length>> { ) -> LogicalVec2<Option<Length>> {
@ -183,6 +207,22 @@ impl LogicalVec2<Option<&'_ LengthPercentage>> {
} }
} }
impl LogicalVec2<Option<&'_ LengthPercentage>> {
pub(crate) fn maybe_percentages_relative_to_basis(
&self,
basis: &LogicalVec2<Option<Length>>,
) -> LogicalVec2<Option<Length>> {
LogicalVec2 {
inline: self
.inline
.and_then(|v| v.maybe_percentage_relative_to(basis.inline)),
block: self
.block
.and_then(|v| v.maybe_percentage_relative_to(basis.block)),
}
}
}
impl<T: Zero> LogicalRect<T> { impl<T: Zero> LogicalRect<T> {
pub fn zero() -> Self { pub fn zero() -> Self {
Self { Self {

View file

@ -31,10 +31,76 @@ pub use fragment_tree::FragmentTree;
use geom::AuOrAuto; use geom::AuOrAuto;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style_ext::ComputedValuesExt; use style_ext::{Clamp, ComputedValuesExt};
use crate::geom::LogicalVec2; use crate::geom::LogicalVec2;
/// A containing block useful for calculating inline content sizes, which may
/// have inline sizes that depend on block sizes due to aspect ratio.
pub(crate) struct IndefiniteContainingBlock<'a> {
pub size: LogicalVec2<AuOrAuto>,
pub style: &'a ComputedValues,
}
impl<'a> IndefiniteContainingBlock<'a> {
fn new_for_style(style: &'a ComputedValues) -> Self {
Self::new_for_style_and_block_size(style, 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_style_and_block_size(style: &'a ComputedValues, block_size: AuOrAuto) -> Self {
Self {
size: LogicalVec2 {
inline: AuOrAuto::Auto,
block: block_size,
},
style,
}
}
fn new_for_intrinsic_inline_size_of_child(
&self,
style: &'a ComputedValues,
auto_minimum: &LogicalVec2<Au>,
) -> Self {
let (content_box_size, content_min_size, content_max_size, _) =
style.content_box_sizes_and_padding_border_margin(&self);
let block_size = content_box_size.block.map(|v| {
v.clamp_between_extremums(
content_min_size.block.auto_is(|| auto_minimum.block),
content_max_size.block,
)
});
IndefiniteContainingBlock::new_for_style_and_block_size(style, block_size)
}
}
impl<'a> From<&'_ ContainingBlock<'a>> for IndefiniteContainingBlock<'a> {
fn from(containing_block: &ContainingBlock<'a>) -> Self {
Self {
size: LogicalVec2 {
inline: AuOrAuto::LengthPercentage(containing_block.inline_size),
block: containing_block.block_size,
},
style: containing_block.style,
}
}
}
impl<'a> From<&'_ DefiniteContainingBlock<'a>> for IndefiniteContainingBlock<'a> {
fn from(containing_block: &DefiniteContainingBlock<'a>) -> Self {
Self {
size: containing_block
.size
.map(|v| AuOrAuto::LengthPercentage(*v)),
style: containing_block.style,
}
}
}
pub struct ContainingBlock<'a> { pub struct ContainingBlock<'a> {
inline_size: Au, inline_size: Au,
block_size: AuOrAuto, block_size: AuOrAuto,
@ -47,6 +113,23 @@ impl<'a> ContainingBlock<'a> {
} }
} }
impl<'a> TryFrom<&'_ IndefiniteContainingBlock<'a>> for ContainingBlock<'a> {
type Error = &'static str;
fn try_from(
indefinite_containing_block: &IndefiniteContainingBlock<'a>,
) -> Result<Self, Self::Error> {
match indefinite_containing_block.size.inline {
AuOrAuto::Auto => Err("ContainingBlock doesn't accept auto inline sizes"),
AuOrAuto::LengthPercentage(inline_size) => Ok(ContainingBlock {
inline_size,
block_size: indefinite_containing_block.size.block,
style: indefinite_containing_block.style,
}),
}
}
}
struct DefiniteContainingBlock<'a> { struct DefiniteContainingBlock<'a> {
size: LogicalVec2<Au>, size: LogicalVec2<Au>,
style: &'a ComputedValues, style: &'a ComputedValues,

View file

@ -27,7 +27,7 @@ use crate::geom::{
PhysicalPoint, PhysicalRect, ToLogical, PhysicalPoint, PhysicalRect, ToLogical,
}; };
use crate::style_ext::{ComputedValuesExt, DisplayInside}; use crate::style_ext::{ComputedValuesExt, DisplayInside};
use crate::{ContainingBlock, DefiniteContainingBlock}; use crate::{ContainingBlock, DefiniteContainingBlock, IndefiniteContainingBlock};
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) struct AbsolutelyPositionedBox { pub(crate) struct AbsolutelyPositionedBox {
@ -578,8 +578,16 @@ impl HoistedAbsolutelyPositionedBox {
let margin_sum = inline_axis.margin_start + inline_axis.margin_end; let margin_sum = inline_axis.margin_start + inline_axis.margin_end;
let available_size = let available_size =
cbis - anchor - pbm.padding_border_sums.inline - margin_sum; cbis - anchor - pbm.padding_border_sums.inline - margin_sum;
let style = non_replaced.style.clone();
let containing_block_for_children =
IndefiniteContainingBlock::from(containing_block)
.new_for_intrinsic_inline_size_of_child(
&style,
&LogicalVec2::zero(),
);
non_replaced non_replaced
.inline_content_sizes(layout_context) .inline_content_sizes(layout_context, &containing_block_for_children)
.shrink_to_fit(available_size) .shrink_to_fit(available_size)
}); });

View file

@ -29,8 +29,8 @@ use crate::dom::NodeExt;
use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment}; use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment};
use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize}; use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize};
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::{AuOrAuto, ContainingBlock}; use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock};
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) struct ReplacedContent { pub(crate) struct ReplacedContent {
@ -238,7 +238,7 @@ impl ReplacedContent {
LogicalVec2::from_physical_size(&intrinsic_size, style.effective_writing_mode()) LogicalVec2::from_physical_size(&intrinsic_size, style.effective_writing_mode())
} }
pub fn inline_size_over_block_size_intrinsic_ratio( pub(crate) fn inline_size_over_block_size_intrinsic_ratio(
&self, &self,
style: &ComputedValues, style: &ComputedValues,
) -> Option<CSSFloat> { ) -> Option<CSSFloat> {
@ -251,11 +251,16 @@ impl ReplacedContent {
}) })
} }
pub fn inline_content_sizes(&self, style: &ComputedValues) -> ContentSizes { pub fn inline_content_sizes(
&self,
_: &LayoutContext,
containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes {
// FIXME: min/max-content of replaced elements is not defined in // FIXME: min/max-content of replaced elements is not defined in
// https://dbaron.org/css/intrinsic/ // https://dbaron.org/css/intrinsic/
// This seems sensible? // This seems sensible?
self.flow_relative_intrinsic_size(style)
self.flow_relative_intrinsic_size(containing_block_for_children.style)
.inline .inline
.unwrap_or(Au::zero()) .unwrap_or(Au::zero())
.into() .into()
@ -331,6 +336,18 @@ impl ReplacedContent {
} }
} }
pub(crate) fn preferred_aspect_ratio(
&self,
containing_block: &IndefiniteContainingBlock,
style: &ComputedValues,
) -> Option<AspectRatio> {
style.preferred_aspect_ratio(
self.inline_size_over_block_size_intrinsic_ratio(style),
containing_block.try_into().ok().as_ref(),
containing_block.style.effective_writing_mode(),
)
}
/// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-width> /// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-width>
/// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-height> /// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-height>
/// ///
@ -345,10 +362,7 @@ impl ReplacedContent {
) -> LogicalVec2<Au> { ) -> LogicalVec2<Au> {
let mode = style.effective_writing_mode(); let mode = style.effective_writing_mode();
let intrinsic_size = self.flow_relative_intrinsic_size(style); let intrinsic_size = self.flow_relative_intrinsic_size(style);
let intrinsic_ratio = style.preferred_aspect_ratio( let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style);
self.inline_size_over_block_size_intrinsic_ratio(style),
containing_block,
);
let box_size = box_size.unwrap_or( let box_size = box_size.unwrap_or(
style style

View file

@ -8,13 +8,11 @@ use std::ops::{Add, AddAssign};
use app_units::Au; use app_units::Au;
use serde::Serialize; use serde::Serialize;
use style::logical_geometry::WritingMode;
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::Length;
use style::Zero; use style::Zero;
use crate::style_ext::{Clamp, ComputedValuesExt}; use crate::style_ext::{Clamp, ComputedValuesExt};
use crate::{AuOrAuto, IndefiniteContainingBlock, LogicalVec2};
#[derive(PartialEq)] #[derive(PartialEq)]
pub(crate) enum IntrinsicSizingMode { pub(crate) enum IntrinsicSizingMode {
@ -111,70 +109,31 @@ impl From<Au> for ContentSizes {
pub(crate) fn outer_inline( pub(crate) fn outer_inline(
style: &ComputedValues, style: &ComputedValues,
containing_block_writing_mode: WritingMode, containing_block: &IndefiniteContainingBlock,
get_content_size: impl FnOnce() -> ContentSizes, auto_minimum: &LogicalVec2<Au>,
get_auto_minimum: impl FnOnce() -> Au, get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> ContentSizes,
) -> ContentSizes { ) -> ContentSizes {
let padding = style.padding(containing_block_writing_mode); let (content_box_size, content_min_size, content_max_size, pbm) =
let border = style.border_width(containing_block_writing_mode); style.content_box_sizes_and_padding_border_margin(containing_block);
let margin = style.margin(containing_block_writing_mode); let content_min_size = LogicalVec2 {
inline: content_min_size.inline.auto_is(|| auto_minimum.inline),
// For margins and paddings, a cyclic percentage is resolved against zero block: content_min_size.block.auto_is(|| auto_minimum.block),
// for determining intrinsic size contributions.
// https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution
let zero = Length::zero();
let pb_lengths = Au::from(
border.inline_sum() +
padding.inline_start.percentage_relative_to(zero) +
padding.inline_end.percentage_relative_to(zero),
);
let mut m_lengths = zero;
if let Some(m) = margin.inline_start.non_auto() {
m_lengths += m.percentage_relative_to(zero)
}
if let Some(m) = margin.inline_end.non_auto() {
m_lengths += m.percentage_relative_to(zero)
}
let box_sizing = style.get_position().box_sizing;
let inline_size = style
.box_size(containing_block_writing_mode)
.inline
.non_auto()
// Percentages for 'width' are treated as 'auto'
.and_then(|lp| lp.to_length());
let min_inline_size = style
.min_box_size(containing_block_writing_mode)
.inline
// Percentages for 'min-width' are treated as zero
.percentage_relative_to(zero)
.map(Au::from)
.auto_is(get_auto_minimum);
let max_inline_size = style
.max_box_size(containing_block_writing_mode)
.inline
// Percentages for 'max-width' are treated as 'none'
.and_then(|lp| lp.to_length())
.map(Au::from);
let clamp = |l: Au| l.clamp_between_extremums(min_inline_size, max_inline_size);
let border_box_sizes = match inline_size {
Some(non_auto) => {
let clamped = clamp(non_auto.into());
let border_box_size = match box_sizing {
BoxSizing::ContentBox => clamped + pb_lengths,
BoxSizing::BorderBox => clamped,
};
border_box_size.into()
},
None => get_content_size().map(|content_box_size| {
match box_sizing {
// Clamp to 'min-width' and 'max-width', which are sizing the…
BoxSizing::ContentBox => clamp(content_box_size) + pb_lengths,
BoxSizing::BorderBox => clamp(content_box_size + pb_lengths),
}
}),
}; };
let pbm_inline_sum = pbm.padding_border_sums.inline +
border_box_sizes.map(|s| s + m_lengths.into()) pbm.margin.inline_start.auto_is(Au::zero) +
pbm.margin.inline_end.auto_is(Au::zero);
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 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

@ -30,7 +30,7 @@ use crate::geom::{
AuOrAuto, LengthOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, AuOrAuto, LengthOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides,
PhysicalSize, PhysicalSize,
}; };
use crate::ContainingBlock; use crate::{ContainingBlock, IndefiniteContainingBlock};
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) enum Display { pub(crate) enum Display {
@ -233,11 +233,25 @@ pub(crate) trait ComputedValuesExt {
box_size: LogicalVec2<Option<Length>>, box_size: LogicalVec2<Option<Length>>,
pbm: &PaddingBorderMargin, pbm: &PaddingBorderMargin,
) -> LogicalVec2<Option<Length>>; ) -> LogicalVec2<Option<Length>>;
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<AuOrAuto>,
LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>,
PaddingBorderMargin,
);
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin; fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin;
fn padding_border_margin_for_intrinsic_size( fn padding_border_margin_for_intrinsic_size(
&self, &self,
writing_mode: WritingMode, writing_mode: WritingMode,
) -> PaddingBorderMargin; ) -> PaddingBorderMargin;
fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
&self,
writing_mode: WritingMode,
containing_block_inline_size: Au,
) -> PaddingBorderMargin;
fn padding( fn padding(
&self, &self,
containing_block_writing_mode: WritingMode, containing_block_writing_mode: WritingMode,
@ -263,7 +277,8 @@ pub(crate) trait ComputedValuesExt {
fn preferred_aspect_ratio( fn preferred_aspect_ratio(
&self, &self,
natural_aspect_ratio: Option<CSSFloat>, natural_aspect_ratio: Option<CSSFloat>,
containing_block: &ContainingBlock, containing_block: Option<&ContainingBlock>,
containing_block_writing_mode: WritingMode,
) -> Option<AspectRatio>; ) -> Option<AspectRatio>;
fn background_is_transparent(&self) -> bool; fn background_is_transparent(&self) -> bool;
fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags; fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
@ -431,6 +446,49 @@ impl ComputedValuesExt for ComputedValues {
} }
} }
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<AuOrAuto>,
LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>,
PaddingBorderMargin,
) {
// <https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution>
// If max size properties or preferred size properties are set to a value containing
// indefinite percentages, we treat the entire value as the initial value of the property.
// However, for min size properties, as well as for margins and paddings,
// we instead resolve indefinite percentages against zero.
let containing_block_size = containing_block.size.map(|v| v.non_auto().map(Into::into));
let containing_block_size_auto_is_zero =
containing_block_size.map(|v| v.unwrap_or_else(Length::zero));
let writing_mode = self.writing_mode;
let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
writing_mode,
containing_block.size.inline.auto_is(Au::zero),
);
let box_size = self
.box_size(writing_mode)
.maybe_percentages_relative_to_basis(&containing_block_size);
let content_box_size = self
.content_box_size_for_box_size(box_size, &pbm)
.map(|v| v.map(Au::from));
let min_size = self
.min_box_size(writing_mode)
.percentages_relative_to_basis(&containing_block_size_auto_is_zero);
let content_min_size = self
.content_min_box_size_for_min_size(min_size, &pbm)
.map(|v| v.map(Au::from));
let max_size = self
.max_box_size(writing_mode)
.maybe_percentages_relative_to_basis(&containing_block_size);
let content_max_size = self
.content_max_box_size_for_max_size(max_size, &pbm)
.map(|v| v.map(Au::from));
(content_box_size, content_min_size, content_max_size, pbm)
}
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin { fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin {
let cbis = containing_block.inline_size; let cbis = containing_block.inline_size;
let padding = self let padding = self
@ -473,6 +531,30 @@ impl ComputedValuesExt for ComputedValues {
} }
} }
fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
&self,
writing_mode: WritingMode,
containing_block_inline_size: Au,
) -> PaddingBorderMargin {
let containing_block_inline_size = containing_block_inline_size.into();
let padding = self
.padding(writing_mode)
.percentages_relative_to(containing_block_inline_size);
let border = self.border_width(writing_mode);
let margin = self
.margin(writing_mode)
.percentages_relative_to(containing_block_inline_size);
PaddingBorderMargin {
padding_border_sums: LogicalVec2 {
inline: (padding.inline_sum() + border.inline_sum()).into(),
block: (padding.block_sum() + border.block_sum()).into(),
},
padding: padding.into(),
border: border.into(),
margin: margin.map(|margin_side| margin_side.map(Into::into)),
}
}
fn padding( fn padding(
&self, &self,
containing_block_writing_mode: WritingMode, containing_block_writing_mode: WritingMode,
@ -677,7 +759,8 @@ impl ComputedValuesExt for ComputedValues {
fn preferred_aspect_ratio( fn preferred_aspect_ratio(
&self, &self,
natural_aspect_ratio: Option<CSSFloat>, natural_aspect_ratio: Option<CSSFloat>,
containing_block: &ContainingBlock, containing_block: Option<&ContainingBlock>,
containing_block_writing_mode: WritingMode,
) -> Option<AspectRatio> { ) -> Option<AspectRatio> {
let GenericAspectRatio { let GenericAspectRatio {
auto, auto,
@ -728,8 +811,13 @@ impl ComputedValuesExt for ComputedValues {
let box_sizing_adjustment = match self.clone_box_sizing() { let box_sizing_adjustment = match self.clone_box_sizing() {
BoxSizing::ContentBox => LogicalVec2::zero(), BoxSizing::ContentBox => LogicalVec2::zero(),
BoxSizing::BorderBox => { BoxSizing::BorderBox => {
self.padding_border_margin(containing_block) match containing_block {
.padding_border_sums Some(containing_block) => self.padding_border_margin(containing_block),
None => self.padding_border_margin_for_intrinsic_size(
containing_block_writing_mode,
),
}
.padding_border_sums
}, },
}; };
Some(AspectRatio { Some(AspectRatio {

View file

@ -35,7 +35,7 @@ use crate::positioned::{relative_adjustement, PositioningContext, PositioningCon
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::table::TableSlotCoordinates; use crate::table::TableSlotCoordinates;
use crate::ContainingBlock; use crate::{ContainingBlock, IndefiniteContainingBlock};
/// A result of a final or speculative layout of a single cell in /// 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 /// the table. Note that this is only done for slots that are not
@ -201,10 +201,10 @@ impl<'a> TableLayout<'a> {
let (size, min_size, max_size) = let (size, min_size, max_size) =
get_outer_sizes_from_style(&cell.style, writing_mode, &padding_border_sums); get_outer_sizes_from_style(&cell.style, writing_mode, &padding_border_sums);
let mut inline_content_sizes = cell let mut inline_content_sizes = cell.contents.contents.inline_content_sizes(
.contents layout_context,
.contents &IndefiniteContainingBlock::new_for_style(&cell.style),
.inline_content_sizes(layout_context, writing_mode); );
inline_content_sizes.min_content += padding_border_sums.inline; inline_content_sizes.min_content += padding_border_sums.inline;
inline_content_sizes.max_content += padding_border_sums.inline; inline_content_sizes.max_content += padding_border_sums.inline;
@ -677,52 +677,42 @@ impl<'a> TableLayout<'a> {
.captions .captions
.iter() .iter()
.map(|caption| { .map(|caption| {
let size; let mut context = caption.context.borrow_mut();
let min_size; let padding = context
let max_size; .style
let padding_border_sums; .padding(writing_mode)
let size_is_auto; .percentages_relative_to(Length::zero());
{ let border = context.style.border_width(writing_mode);
let context = caption.context.borrow(); let margin = context
let padding = context .style
.style .margin(writing_mode)
.padding(writing_mode) .percentages_relative_to(Length::zero())
.percentages_relative_to(Length::zero()); .auto_is(Length::zero);
let border = context.style.border_width(writing_mode);
let margin = context
.style
.margin(writing_mode)
.percentages_relative_to(Length::zero())
.auto_is(Length::zero);
padding_border_sums = LogicalVec2 { let padding_border_sums = LogicalVec2 {
inline: (padding.inline_sum() + border.inline_sum() + margin.inline_sum()) inline: (padding.inline_sum() + border.inline_sum() + margin.inline_sum())
.into(), .into(),
block: (padding.block_sum() + border.block_sum() + margin.block_sum()) block: (padding.block_sum() + border.block_sum() + margin.block_sum()).into(),
.into(), };
};
(size, min_size, max_size) = get_outer_sizes_from_style( let (size, min_size, max_size) =
&context.style, get_outer_sizes_from_style(&context.style, writing_mode, &padding_border_sums);
writing_mode, let size_is_auto = context.style.box_size(writing_mode).inline.is_auto();
&padding_border_sums,
);
size_is_auto = context.style.box_size(writing_mode).inline.is_auto();
}
// If an inline size is defined it should serve as the upper limit and lower limit // If an inline size is defined it should serve as the upper limit and lower limit
// of the caption inline size. // of the caption inline size.
let inline_size = if !size_is_auto { if !size_is_auto {
size.inline size.inline
} else { } else {
let inline_content_sizes = caption let style = context.style.clone();
.context let inline_content_sizes = context.inline_content_sizes(
.borrow_mut() layout_context,
.inline_content_sizes(layout_context); &IndefiniteContainingBlock::new_for_style(&style),
);
inline_content_sizes.min_content + padding_border_sums.inline inline_content_sizes.min_content + padding_border_sums.inline
}; }
.min(max_size.inline)
inline_size.min(max_size.inline).max(min_size.inline) .max(min_size.inline)
}) })
.max() .max()
.unwrap_or_default() .unwrap_or_default()
@ -2432,8 +2422,9 @@ impl Table {
pub(crate) fn inline_content_sizes( pub(crate) fn inline_content_sizes(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
writing_mode: WritingMode, containing_block_for_children: &IndefiniteContainingBlock,
) -> ContentSizes { ) -> ContentSizes {
let writing_mode = containing_block_for_children.style.effective_writing_mode();
let mut layout = TableLayout::new(self); let mut layout = TableLayout::new(self);
let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode); let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode);

View file

@ -176439,6 +176439,19 @@
{} {}
] ]
], ],
"percentage-padding-005.html": [
"928286a590407bd5607fe4797b19be3b4ecb14d8",
[
null,
[
[
"/css/reference/ref-filled-green-100px-square-only.html",
"=="
]
],
{}
]
],
"percentage-size-subitems-001.html": [ "percentage-size-subitems-001.html": [
"70f3953052a3a770c6cd15ee169607a00fc452b0", "70f3953052a3a770c6cd15ee169607a00fc452b0",
[ [

View file

@ -1,2 +0,0 @@
[flex-basis-011.html]
expected: FAIL

View file

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

View file

@ -1,2 +0,0 @@
[flexbox-flex-direction-column-percentage-ignored.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[item-with-max-height-and-scrollbar.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[nested-flex-image-loading-invalidates-intrinsic-sizes.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[percentage-heights-014.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[position-fixed-001.html]
expected: FAIL

View file

@ -1,3 +0,0 @@
[aspect-ratio-affects-container-width-when-height-changes.html]
[#container 1]
expected: FAIL

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,2 +0,0 @@
[calc-rounding-002.html]
expected: FAIL

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#item-margins">
<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#intrinsic-main-sizes">
<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="The flex container becomes 100px tall because the padding percentage of the item resolves against its width.">
<p>Test passes if there is a filled green square.</p>
<div style="display: flex; background: green; flex-direction: column; width: 100px;">
<div style="width: 100px; padding-bottom: 100%"></div>
</div>