layout: Unify layout logic for replaced and non-replaced grid items (#37985)

Laying out a grid item will now use the same logic regardless of whether
it's replaced or not.
This reduces the amount of code, and should have no observable effect
(but hard to say since and I don't understand Taffy).

Testing: Unneeded (no behavior change)
This part of #37942

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2025-07-11 15:47:03 +02:00 committed by GitHub
parent 55fd7b862f
commit b7133478e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 82 additions and 238 deletions

View file

@ -29,9 +29,9 @@ use crate::dom::NodeExt;
use crate::fragment_tree::{
BaseFragmentInfo, CollapsedBlockMargins, Fragment, IFrameFragment, ImageFragment,
};
use crate::geom::{LazySize, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize, Size, Sizes};
use crate::geom::{LazySize, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize};
use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::sizing::{ComputeInlineContentSizes, InlineContentSizesResult};
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, LayoutStyle};
use crate::{ConstraintSpace, ContainingBlock, SizeConstraint};
@ -474,103 +474,6 @@ impl ReplacedContents {
.unwrap_or_else(|| Self::default_block_size(writing_mode))
}
/// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-width>
/// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-height>
///
/// Also used in other cases, for example
/// <https://drafts.csswg.org/css2/visudet.html#block-replaced-width>
///
/// The logic differs from CSS2 in order to properly handle `aspect-ratio` and keyword sizes.
/// Each axis can have preferred, min and max sizing constraints, plus constraints transferred
/// from the other axis if there is an aspect ratio, plus a natural and default size.
/// In case of conflict, the order of precedence (from highest to lowest) is:
/// 1. Non-transferred min constraint
/// 2. Non-transferred max constraint
/// 3. Non-transferred preferred constraint
/// 4. Transferred min constraint
/// 5. Transferred max constraint
/// 6. Transferred preferred constraint
/// 7. Natural size
/// 8. Default object size
///
/// <https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers>
/// <https://github.com/w3c/csswg-drafts/issues/6071#issuecomment-2243986313>
pub(crate) fn used_size_as_if_inline_element_from_content_box_sizes(
&self,
containing_block: &ContainingBlock,
style: &ComputedValues,
preferred_aspect_ratio: Option<AspectRatio>,
sizes: LogicalVec2<&Sizes>,
automatic_size: LogicalVec2<Size<Au>>,
pbm_sums: LogicalVec2<Au>,
) -> LogicalVec2<Au> {
// <https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing>
let inline_stretch_size = Au::zero().max(containing_block.size.inline - pbm_sums.inline);
let block_stretch_size = containing_block
.size
.block
.to_definite()
.map(|block_size| Au::zero().max(block_size - pbm_sums.block));
let writing_mode = style.writing_mode;
let resolve_inline_size = |get_block_size: &dyn Fn() -> SizeConstraint| {
let get_inline_content_size = || {
self.content_size(
Direction::Inline,
preferred_aspect_ratio,
get_block_size,
&|| self.fallback_inline_size(writing_mode),
)
.into()
};
sizes.inline.resolve(
Direction::Inline,
automatic_size.inline,
Au::zero,
Some(inline_stretch_size),
get_inline_content_size,
false, /* is_table */
)
};
let resolve_block_size = |get_inline_size: &dyn Fn() -> SizeConstraint| {
let get_block_content_size = || -> ContentSizes {
self.content_size(
Direction::Block,
preferred_aspect_ratio,
get_inline_size,
&|| self.fallback_block_size(writing_mode),
)
.into()
};
sizes.block.resolve(
Direction::Block,
automatic_size.block,
Au::zero,
block_stretch_size,
get_block_content_size,
false, /* is_table */
)
};
// First, compute the inline size. Intrinsic values depend on the block sizing properties
// through the aspect ratio, but these can also be intrinsic and depend on the inline size.
// Therefore, when there is an aspect ratio, we may need to:
// 1. Tentatively resolve the inline size, ignoring sizing properties in both axes
// (i.e. resulting in the inline fallback size).
// 2. Tentatively resolve the block size, resolving intrinsic keywords by transferring (1).
// 3. Resolve the final inline size, resolving intrinsic keywords by transferring (2).
// 4. Resolve the final block size, resolving intrinsic keywords by transferring (3).
let inline_size = resolve_inline_size(&|| {
SizeConstraint::Definite(resolve_block_size(&|| {
SizeConstraint::Definite(self.fallback_inline_size(writing_mode))
}))
});
LogicalVec2 {
inline: inline_size,
block: resolve_block_size(&|| SizeConstraint::Definite(inline_size)),
}
}
#[inline]
pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
LayoutStyle::Default(&base.style)

View file

@ -16,15 +16,12 @@ use super::{
};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::formatting_contexts::{
Baselines, IndependentFormattingContext, IndependentFormattingContextContents,
};
use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
};
use crate::geom::{
LazySize, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size,
SizeConstraint, Sizes,
LazySize, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, SizeConstraint,
};
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
@ -123,13 +120,6 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
let mut child = (*self.source_child_nodes[usize::from(node_id)]).borrow_mut();
let child = &mut *child;
fn option_f32_to_size(input: Option<f32>) -> Size<Au> {
match input {
None => Size::Initial,
Some(length) => Size::Numeric(Au::from_f32_px(length)),
}
}
with_independant_formatting_context(
&mut child.taffy_level_box,
|independent_context| -> taffy::LayoutOutput {
@ -158,137 +148,88 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
let preferred_aspect_ratio =
independent_context.preferred_aspect_ratio(&pbm.padding_border_sums);
// TODO: unify the replaced and non-replaced logic.
if let IndependentFormattingContextContents::Replaced(replaced) =
&independent_context.contents
{
let content_box_size = replaced
.used_size_as_if_inline_element_from_content_box_sizes(
containing_block,
style,
preferred_aspect_ratio,
LogicalVec2 {
block: &Sizes::new(
option_f32_to_size(content_box_known_dimensions.height),
Size::Initial,
Size::Initial,
),
inline: &Sizes::new(
option_f32_to_size(content_box_known_dimensions.width),
Size::Initial,
Size::Initial,
),
},
Size::FitContent.into(),
pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum(),
)
.to_physical_size(writing_mode);
// TODO: pass min- and max- size
let tentative_block_size = content_box_known_dimensions
.height
.map(Au::from_f32_px)
.map_or_else(SizeConstraint::default, SizeConstraint::Definite);
// Create fragments if the RunMode if PerformLayout
// If the RunMode is ComputeSize then only the returned size will be used
if inputs.run_mode == RunMode::PerformLayout {
child.child_fragments =
replaced.make_fragments(self.layout_context, style, content_box_size);
}
let computed_size =
taffy::Size {
width: inputs.known_dimensions.width.unwrap_or_else(|| {
content_box_size.width.to_f32_px() + pb_sum.inline
}),
height: inputs.known_dimensions.height.unwrap_or_else(|| {
content_box_size.height.to_f32_px() + pb_sum.block
}),
};
let size = inputs.known_dimensions.unwrap_or(computed_size);
taffy::LayoutOutput {
size,
..taffy::LayoutOutput::DEFAULT
}
} else {
// TODO: pass min- and max- size
let tentative_block_size = content_box_known_dimensions
.height
.map(Au::from_f32_px)
.map_or_else(SizeConstraint::default, SizeConstraint::Definite);
// Compute inline size
let inline_size = content_box_known_dimensions.width.unwrap_or_else(|| {
let constraint_space = ConstraintSpace {
block_size: tentative_block_size,
writing_mode,
preferred_aspect_ratio,
};
// TODO: pass min- and max- size
let result = independent_context
.inline_content_sizes(self.layout_context, &constraint_space);
let adjusted_available_space = inputs
.available_space
.width
.map_definite_value(|width| width - content_box_inset.inline);
resolve_content_size(adjusted_available_space, result.sizes)
});
// Return early if only inline content sizes are requested
if inputs.run_mode == RunMode::ComputeSize &&
inputs.axis == RequestedAxis::Horizontal
{
return taffy::LayoutOutput::from_outer_size(taffy::Size {
width: inline_size + pb_sum.inline,
// If RequestedAxis is Horizontal then height will be ignored.
height: 0.0,
});
}
let content_box_size_override = ContainingBlock {
size: ContainingBlockSize {
inline: Au::from_f32_px(inline_size),
block: tentative_block_size,
},
style,
};
let lazy_block_size = match content_box_known_dimensions.height {
// FIXME: use the correct min/max sizes.
None => LazySize::intrinsic(),
Some(height) => Au::from_f32_px(height).into(),
};
child.positioning_context = PositioningContext::default();
let layout = independent_context.layout_without_caching(
self.layout_context,
&mut child.positioning_context,
&content_box_size_override,
containing_block,
// Compute inline size
let inline_size = content_box_known_dimensions.width.unwrap_or_else(|| {
let constraint_space = ConstraintSpace {
block_size: tentative_block_size,
writing_mode,
preferred_aspect_ratio,
false, /* depends_on_block_constraints */
&lazy_block_size,
);
child.child_fragments = layout.fragments;
self.child_specific_layout_infos[usize::from(node_id)] =
layout.specific_layout_info;
let block_size = lazy_block_size
.resolve(|| layout.content_block_size)
.to_f32_px();
let computed_size = taffy::Size {
width: inline_size + pb_sum.inline,
height: block_size + pb_sum.block,
};
let size = inputs.known_dimensions.unwrap_or(computed_size);
taffy::LayoutOutput {
size,
first_baselines: taffy::Point {
x: None,
y: layout.baselines.first.map(|au| au.to_f32_px()),
},
..taffy::LayoutOutput::DEFAULT
}
// TODO: pass min- and max- size
let result = independent_context
.inline_content_sizes(self.layout_context, &constraint_space);
let adjusted_available_space = inputs
.available_space
.width
.map_definite_value(|width| width - content_box_inset.inline);
resolve_content_size(adjusted_available_space, result.sizes)
});
// Return early if only inline content sizes are requested
if inputs.run_mode == RunMode::ComputeSize &&
inputs.axis == RequestedAxis::Horizontal
{
return taffy::LayoutOutput::from_outer_size(taffy::Size {
width: inline_size + pb_sum.inline,
// If RequestedAxis is Horizontal then height will be ignored.
height: 0.0,
});
}
let content_box_size_override = ContainingBlock {
size: ContainingBlockSize {
inline: Au::from_f32_px(inline_size),
block: tentative_block_size,
},
style,
};
let lazy_block_size = match content_box_known_dimensions.height {
// FIXME: use the correct min/max sizes.
None => LazySize::intrinsic(),
Some(height) => Au::from_f32_px(height).into(),
};
child.positioning_context = PositioningContext::default();
let layout = independent_context.layout_without_caching(
self.layout_context,
&mut child.positioning_context,
&content_box_size_override,
containing_block,
preferred_aspect_ratio,
false, /* depends_on_block_constraints */
&lazy_block_size,
);
child.child_fragments = layout.fragments;
self.child_specific_layout_infos[usize::from(node_id)] =
layout.specific_layout_info;
let block_size = lazy_block_size
.resolve(|| layout.content_block_size)
.to_f32_px();
let computed_size = taffy::Size {
width: inline_size + pb_sum.inline,
height: block_size + pb_sum.block,
};
let size = inputs.known_dimensions.unwrap_or(computed_size);
taffy::LayoutOutput {
size,
first_baselines: taffy::Point {
x: None,
y: layout.baselines.first.map(|au| au.to_f32_px()),
},
..taffy::LayoutOutput::DEFAULT
}
},
)