diff options
author | Oriol Brufau <obrufau@igalia.com> | 2024-12-16 19:17:46 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-16 18:17:46 +0000 |
commit | 3d816d6d24d8e5a2b2d607abbf602dbf33578630 (patch) | |
tree | 6b6b25aac141bd168dd3ec2cf5071b9c823fde56 /components | |
parent | 53740fdd1635d2380bb136a508a511854bbdcdff (diff) | |
download | servo-3d816d6d24d8e5a2b2d607abbf602dbf33578630.tar.gz servo-3d816d6d24d8e5a2b2d607abbf602dbf33578630.zip |
Complete implementation of keyword sizes for block layout (#34641)
Adds support for min-content, max-content, fit-content and stretch,
for the case that was missing from #34568: block-level elements that
establish an independent formatting context, when there are floats.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components')
-rw-r--r-- | components/layout_2020/flow/mod.rs | 153 |
1 files changed, 111 insertions, 42 deletions
diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 47deeb10767..1505eecdb4a 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -41,10 +41,7 @@ use crate::layout_box_base::LayoutBoxBase; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::replaced::ReplacedContents; use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; -use crate::style_ext::{ - Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, ContentBoxSizesAndPBMDeprecated, - PaddingBorderMargin, -}; +use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, PaddingBorderMargin}; use crate::{ ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock, SizeConstraint, @@ -1143,23 +1140,15 @@ impl IndependentNonReplacedContents { containing_block: &ContainingBlock<'_>, sequential_layout_state: &mut SequentialLayoutState, ) -> BoxFragment { + let style = &base.style; let containing_block_writing_mode = containing_block.style.writing_mode; - let ContentBoxSizesAndPBMDeprecated { + let ContentBoxSizesAndPBM { content_box_size, content_min_box_size, content_max_box_size, pbm, depends_on_block_constraints, - } = base - .style - .content_box_sizes_and_padding_border_margin(&containing_block.into()) - .into(); - let content_min_box_size = content_min_box_size.auto_is(Au::zero); - - let block_size = content_box_size.block.map(|block_size| { - block_size - .clamp_between_extremums(content_min_box_size.block, content_max_box_size.block) - }); + } = style.content_box_sizes_and_padding_border_margin(&containing_block.into()); let (margin_block_start, margin_block_end) = solve_block_margins_for_in_flow_block_level(&pbm); @@ -1178,7 +1167,6 @@ impl IndependentNonReplacedContents { let mut content_size; let mut layout; let mut placement_rect; - let style = &base.style; // First compute the clear position required by the 'clear' property. // The code below may then add extra clearance when the element can't fit @@ -1191,16 +1179,96 @@ impl IndependentNonReplacedContents { sequential_layout_state.position_without_clearance(&collapsed_margin_block_start) }); - if let AuOrAuto::LengthPercentage(ref inline_size) = content_box_size.inline { - let inline_size = inline_size - .clamp_between_extremums(content_min_box_size.inline, content_max_box_size.inline); + // Then compute a tentative block size, only taking extrinsic values into account. + let margin = pbm.margin.auto_is(Au::zero); + let pbm_sums = pbm.padding + pbm.border + margin; + let available_block_size = containing_block + .size + .block + .non_auto() + .map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum())); + let preferred_block_size = content_box_size + .block + .maybe_resolve_extrinsic(available_block_size); + let min_block_size = content_min_box_size + .block + .maybe_resolve_extrinsic(available_block_size) + .unwrap_or_default(); + let max_block_size = content_max_box_size + .block + .maybe_resolve_extrinsic(available_block_size); + let tentative_block_size = + SizeConstraint::new(preferred_block_size, min_block_size, max_block_size); + + // With the tentative block size we can compute the inline min/max-content sizes. + let inline_content_sizes = LazyCell::new(|| { + let constraint_space = ConstraintSpace::new( + tentative_block_size, + style.writing_mode, + self.preferred_aspect_ratio(), + ); + base.inline_content_sizes(layout_context, &constraint_space, self) + .sizes + }); + + // The final inline size can depend on the available space, which depends on where + // we are placing the box, since floats reduce the available space. + // TODO: this logic could be refined further, since even if some of the 3 sizes + // depends on the available space, the resulting size might not. For example, + // `min-width: 200px; width: 100px; max-width: stretch`. + let inline_size_depends_on_available_space = matches!( + content_box_size.inline, + Size::Initial | Size::Stretch | Size::FitContent + ) || matches!( + content_min_box_size.inline, + Size::Stretch | Size::FitContent + ) || matches!( + content_max_box_size.inline, + Size::Stretch | Size::FitContent + ); + let compute_inline_size = |stretch_size| { + let preferred_inline_size = + content_box_size + .inline + .resolve(Size::Stretch, stretch_size, &inline_content_sizes); + let min_inline_size = content_min_box_size + .inline + .resolve_non_initial(stretch_size, &inline_content_sizes) + .unwrap_or_default(); + let max_inline_size = content_max_box_size + .inline + .resolve_non_initial(stretch_size, &inline_content_sizes); + preferred_inline_size.clamp_between_extremums(min_inline_size, max_inline_size) + }; + + let compute_block_size = |layout: &IndependentLayout| { + let stretch_size = available_block_size.unwrap_or(layout.content_block_size); + let block_contents_size = LazyCell::new(|| layout.content_block_size.into()); + let min_block_size = content_min_box_size + .block + .resolve_non_initial(stretch_size, &block_contents_size) + .unwrap_or_default(); + let max_block_size = content_max_box_size + .block + .resolve_non_initial(stretch_size, &block_contents_size); + tentative_block_size + .to_definite() + .unwrap_or(layout.content_block_size) + .clamp_between_extremums(min_block_size, max_block_size) + }; + + if !inline_size_depends_on_available_space { + // If the inline size doesn't depend on the available inline space, we can just + // compute it with an available inline space of zero. Then, after layout we can + // compute the block size, and finally place among floats. + let inline_size = compute_inline_size(Au::zero()); layout = self.layout( layout_context, positioning_context, &ContainingBlock { size: ContainingBlockSize { inline: inline_size, - block: block_size, + block: tentative_block_size.to_auto_or(), }, style, }, @@ -1214,12 +1282,7 @@ impl IndependentNonReplacedContents { }; } else { content_size = LogicalVec2 { - block: block_size.auto_is(|| { - layout.content_block_size.clamp_between_extremums( - content_min_box_size.block, - content_max_box_size.block, - ) - }), + block: compute_block_size(&layout), inline: inline_size, }; } @@ -1232,10 +1295,24 @@ impl IndependentNonReplacedContents { ); placement_rect = placement.place(); } else { - // Create a PlacementAmongFloats using the minimum size in all dimensions as the object size. + // If the inline size depends on the available space, then we need to iterate + // the various placement candidates, resolve both the inline and block sizes + // on each one placement area, and then check if the box actually fits it. + // As an optimization, we first compute a lower bound of the final box size, + // and skip placement candidates where not even the lower bound would fit. let minimum_size_of_block = LogicalVec2 { - inline: content_min_box_size.inline, - block: block_size.auto_is(|| content_min_box_size.block), + // For the lower bound of the inline size, simply assume no available space. + // TODO: this won't work for things like `calc-size(stretch, 100px - size)`, + // which should result in a bigger size when the available space gets smaller. + inline: compute_inline_size(Au::zero()), + block: match tentative_block_size { + // If we were able to resolve the preferred and maximum block sizes, + // use the tentative block size (it takes the 3 sizes into account). + SizeConstraint::Definite(size) if max_block_size.is_some() => size, + // Oherwise the preferred or maximum block size might end up being zero, + // so can only rely on the minimum block size. + _ => min_block_size, + }, } + pbm.padding_border_sums; let mut placement = PlacementAmongFloats::new( &sequential_layout_state.floats, @@ -1247,12 +1324,9 @@ impl IndependentNonReplacedContents { loop { // First try to place the block using the minimum size as the object size. placement_rect = placement.place(); - let proposed_inline_size = (placement_rect.size.inline - - pbm.padding_border_sums.inline) - .clamp_between_extremums( - content_min_box_size.inline, - content_max_box_size.inline, - ); + let available_inline_size = + placement_rect.size.inline - pbm.padding_border_sums.inline; + let proposed_inline_size = compute_inline_size(available_inline_size); // Now lay out the block using the inline size we calculated from the placement. // Later we'll check to see if the resulting block size is compatible with the @@ -1264,7 +1338,7 @@ impl IndependentNonReplacedContents { &ContainingBlock { size: ContainingBlockSize { inline: proposed_inline_size, - block: block_size, + block: tentative_block_size.to_auto_or(), }, style, }, @@ -1291,12 +1365,7 @@ impl IndependentNonReplacedContents { }; } else { content_size = LogicalVec2 { - block: block_size.auto_is(|| { - layout.content_block_size.clamp_between_extremums( - content_min_box_size.block, - content_max_box_size.block, - ) - }), + block: compute_block_size(&layout), inline: proposed_inline_size, }; } |