layout: Unify logic for laying out replaced and non-replaced in a BFC (#37864)

The logic for laying out block-level replaced elements wasn't taking
floats into account when resolving a `stretch` inline size. By handling
them with the same logic as non-replaced elements, we fix that problem,
and reduce the amount of code.

Testing: Adding new tests
Fixes: #37861

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2025-07-04 19:47:40 +02:00 committed by GitHub
parent 72b1331949
commit 4ee7a34f32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 257 additions and 252 deletions

View file

@ -2147,7 +2147,7 @@ impl FlexItem<'_> {
#[inline]
fn is_table(&self) -> bool {
self.box_.is_table()
self.box_.independent_formatting_context.is_table()
}
}
@ -2225,7 +2225,7 @@ impl FlexItemBox {
.map(|v| Au::zero().max(v - pbm_auto_is_zero.cross)),
};
let is_table = self.is_table();
let is_table = self.independent_formatting_context.is_table();
let tentative_cross_content_size = if cross_axis_is_item_block_axis {
self.independent_formatting_context
.tentative_block_content_size(preferred_aspect_ratio)
@ -2718,12 +2718,4 @@ impl FlexItemBox {
},
}
}
#[inline]
fn is_table(&self) -> bool {
match &self.independent_formatting_context.contents {
IndependentFormattingContextContents::NonReplaced(content) => content.is_table(),
IndependentFormattingContextContents::Replaced(_) => false,
}
}
}

View file

@ -1066,36 +1066,6 @@ impl SequentialLayoutState {
.map(|offset| offset - self.position_with_zero_clearance(block_start_margin))
}
/// A block that is replaced or establishes an independent formatting context can't overlap floats,
/// it has to be placed next to them, and may get some clearance if there isn't enough space.
/// Given such a block with the provided 'clear', 'block_start_margin', 'pbm' and 'object_size',
/// this method finds an area that is big enough and doesn't overlap floats.
/// It returns a tuple with:
/// - The clearance amount (if any), which includes both the effect of 'clear'
/// and the extra space to avoid floats.
/// - The LogicalRect in which the block can be placed without overlapping floats.
pub(crate) fn calculate_clearance_and_inline_adjustment(
&self,
clear: Clear,
block_start_margin: &CollapsedMargin,
pbm: &PaddingBorderMargin,
object_size: LogicalVec2<Au>,
) -> (Option<Au>, LogicalRect<Au>) {
// First compute the clear position required by the 'clear' property.
// The code below may then add extra clearance when the element can't fit
// next to floats not covered by 'clear'.
let clear_position = self.calculate_clear_position(clear, block_start_margin);
let ceiling =
clear_position.unwrap_or_else(|| self.position_without_clearance(block_start_margin));
let mut placement = PlacementAmongFloats::new(&self.floats, ceiling, object_size, pbm);
let placement_rect = placement.place();
let position = &placement_rect.start_corner;
let has_clearance = clear_position.is_some() || position.block > ceiling;
let clearance = has_clearance
.then(|| position.block - self.position_with_zero_clearance(block_start_margin));
(clearance, placement_rect)
}
/// Adds a new adjoining margin.
pub(crate) fn adjoin_assign(&mut self, margin: &CollapsedMargin) {
self.current_margin.adjoin_assign(margin)

View file

@ -30,7 +30,6 @@ use crate::flow::float::{
};
use crate::formatting_contexts::{
Baselines, IndependentFormattingContext, IndependentFormattingContextContents,
IndependentNonReplacedContents,
};
use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags,
@ -41,9 +40,8 @@ use crate::geom::{
};
use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::replaced::ReplacedContents;
use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ContentBoxSizesAndPBM, LayoutStyle, PaddingBorderMargin};
use crate::style_ext::{AspectRatio, ContentBoxSizesAndPBM, LayoutStyle, PaddingBorderMargin};
use crate::{
ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock,
SizeConstraint,
@ -956,11 +954,13 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
depends_on_block_constraints,
available_block_size,
justify_self,
..
} = solve_containing_block_padding_and_border_for_in_flow_box(
containing_block,
&layout_style,
get_inline_content_sizes,
ignore_block_margins_for_stretch,
None,
);
let ResolvedMargins {
margin,
@ -1178,15 +1178,16 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
.with_block_margins_collapsed_with_children(block_margins_collapsed_with_children)
}
impl IndependentNonReplacedContents {
/// Lay out a normal in flow non-replaced block that establishes an independent
impl IndependentFormattingContext {
/// Lay out an in-flow block-level box that establishes an independent
/// formatting context in its containing formatting context.
///
/// - <https://drafts.csswg.org/css2/visudet.html#blockwidth>
/// - <https://drafts.csswg.org/css2/visudet.html#block-replaced-width>
/// - <https://drafts.csswg.org/css2/visudet.html#normal-block>
/// - <https://drafts.csswg.org/css2/visudet.html#inline-replaced-height>
pub(crate) fn layout_in_flow_block_level(
&self,
base: &LayoutBoxBase,
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
@ -1195,7 +1196,6 @@ impl IndependentNonReplacedContents {
) -> BoxFragment {
if let Some(sequential_layout_state) = sequential_layout_state {
return self.layout_in_flow_block_level_sequentially(
base,
layout_context,
positioning_context,
containing_block,
@ -1205,10 +1205,10 @@ impl IndependentNonReplacedContents {
}
let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
base.inline_content_sizes(layout_context, constraint_space, self)
self.inline_content_sizes(layout_context, constraint_space)
.sizes
};
let layout_style = self.layout_style(base);
let layout_style = self.layout_style();
let ContainingBlockPaddingAndBorder {
containing_block: containing_block_for_children,
pbm,
@ -1216,11 +1216,13 @@ impl IndependentNonReplacedContents {
depends_on_block_constraints,
available_block_size,
justify_self,
preferred_aspect_ratio,
} = solve_containing_block_padding_and_border_for_in_flow_box(
containing_block,
&layout_style,
get_inline_content_sizes,
ignore_block_margins_for_stretch,
Some(self),
);
let lazy_block_size = LazySize::new(
@ -1237,7 +1239,7 @@ impl IndependentNonReplacedContents {
positioning_context,
&containing_block_for_children,
containing_block,
base,
preferred_aspect_ratio,
false, /* depends_on_block_constraints */
&lazy_block_size,
);
@ -1268,7 +1270,7 @@ impl IndependentNonReplacedContents {
let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
let containing_block_writing_mode = containing_block.style.writing_mode;
let mut base_fragment_info = base.base_fragment_info;
let mut base_fragment_info = self.base.base_fragment_info;
if depends_on_block_constraints {
base_fragment_info.flags.insert(
FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
@ -1276,7 +1278,7 @@ impl IndependentNonReplacedContents {
}
BoxFragment::new(
base_fragment_info,
base.style.clone(),
self.base.style.clone(),
layout.fragments,
content_rect.as_physical(Some(containing_block)),
pbm.padding.to_physical(containing_block_writing_mode),
@ -1294,14 +1296,13 @@ impl IndependentNonReplacedContents {
/// layout concerns, such clearing and placing the content next to floats.
fn layout_in_flow_block_level_sequentially(
&self,
base: &LayoutBoxBase,
layout_context: &LayoutContext<'_>,
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock<'_>,
sequential_layout_state: &mut SequentialLayoutState,
ignore_block_margins_for_stretch: LogicalSides1D<bool>,
) -> BoxFragment {
let style = &base.style;
let style = &self.base.style;
let containing_block_writing_mode = containing_block.style.writing_mode;
let ContentBoxSizesAndPBM {
content_box_sizes,
@ -1309,7 +1310,7 @@ impl IndependentNonReplacedContents {
depends_on_block_constraints,
..
} = self
.layout_style(base)
.layout_style()
.content_box_sizes_and_padding_border_margin(&containing_block.into());
let (margin_block_start, margin_block_end) =
@ -1341,16 +1342,34 @@ impl IndependentNonReplacedContents {
sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
});
// Then compute a tentative block size, only taking extrinsic values into account.
// Then compute a tentative block size.
let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
let available_block_size = containing_block
.size
.block
.to_definite()
.map(|block_size| Au::zero().max(block_size - pbm_sums.block));
let (preferred_block_size, min_block_size, max_block_size) = content_box_sizes
.block
.resolve_each_extrinsic(Size::FitContent, Au::zero(), available_block_size);
let is_table = self.is_table();
let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
let tentative_block_content_size =
self.tentative_block_content_size(preferred_aspect_ratio);
let (preferred_block_size, min_block_size, max_block_size) =
if let Some(block_content_size) = tentative_block_content_size {
let (preferred, min, max) = content_box_sizes.block.resolve_each(
Size::FitContent,
Au::zero,
available_block_size,
|| block_content_size,
is_table,
);
(Some(preferred), min, max)
} else {
content_box_sizes.block.resolve_each_extrinsic(
Size::FitContent,
Au::zero(),
available_block_size,
)
};
let tentative_block_size =
SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
@ -1359,18 +1378,18 @@ impl IndependentNonReplacedContents {
let constraint_space = ConstraintSpace::new(
tentative_block_size,
style.writing_mode,
self.preferred_aspect_ratio(),
preferred_aspect_ratio,
);
base.inline_content_sizes(layout_context, &constraint_space, self)
self.inline_content_sizes(layout_context, &constraint_space)
.sizes
};
let justify_self = resolve_justify_self(style, containing_block.style);
let is_table = self.is_table();
let is_replaced = self.is_replaced();
let compute_inline_size = |stretch_size| {
content_box_sizes.inline.resolve(
Direction::Inline,
automatic_inline_size(justify_self, is_table),
automatic_inline_size(justify_self, is_table, is_replaced),
Au::zero,
Some(stretch_size),
get_inline_content_sizes,
@ -1412,7 +1431,7 @@ impl IndependentNonReplacedContents {
style,
},
containing_block,
base,
preferred_aspect_ratio,
false, /* depends_on_block_constraints */
&lazy_block_size,
);
@ -1478,7 +1497,7 @@ impl IndependentNonReplacedContents {
style,
},
containing_block,
base,
preferred_aspect_ratio,
false, /* depends_on_block_constraints */
&lazy_block_size,
);
@ -1568,7 +1587,7 @@ impl IndependentNonReplacedContents {
size: content_size,
};
let mut base_fragment_info = base.base_fragment_info;
let mut base_fragment_info = self.base.base_fragment_info;
if depends_on_block_constraints {
base_fragment_info.flags.insert(
FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
@ -1591,144 +1610,6 @@ impl IndependentNonReplacedContents {
}
}
impl ReplacedContents {
/// <https://drafts.csswg.org/css2/visudet.html#block-replaced-width>
/// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-width>
/// <https://drafts.csswg.org/css2/visudet.html#inline-replaced-height>
fn layout_in_flow_block_level(
&self,
base: &LayoutBoxBase,
layout_context: &LayoutContext,
containing_block: &ContainingBlock,
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
ignore_block_margins_for_stretch: LogicalSides1D<bool>,
) -> BoxFragment {
let content_box_sizes_and_pbm = self
.layout_style(base)
.content_box_sizes_and_padding_border_margin(&containing_block.into());
let pbm = &content_box_sizes_and_pbm.pbm;
let content_size = self.used_size_as_if_inline_element(
containing_block,
&base.style,
&content_box_sizes_and_pbm,
ignore_block_margins_for_stretch,
);
let margin_inline_start;
let margin_inline_end;
let effective_margin_inline_start;
let (margin_block_start, margin_block_end) =
solve_block_margins_for_in_flow_block_level(pbm);
let justify_self = resolve_justify_self(&base.style, containing_block.style);
let containing_block_writing_mode = containing_block.style.writing_mode;
let physical_content_size = content_size.to_physical_size(containing_block_writing_mode);
let fragments = self.make_fragments(layout_context, &base.style, physical_content_size);
let clearance;
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
// From https://drafts.csswg.org/css2/#floats:
// "The border box of a table, a block-level replaced element, or an element in
// the normal flow that establishes a new block formatting context (such as an
// element with overflow other than visible) must not overlap the margin box of
// any floats in the same block formatting context as the element itself. If
// necessary, implementations should clear the said element by placing it below
// any preceding floats, but may place it adjacent to such floats if there is
// sufficient space. They may even make the border box of said element narrower
// than defined by section 10.3.3. CSS 2 does not define when a UA may put said
// element next to the float or by how much said element may become narrower."
let collapsed_margin_block_start = CollapsedMargin::new(margin_block_start);
let size = content_size + pbm.padding_border_sums;
let placement_rect;
(clearance, placement_rect) = sequential_layout_state
.calculate_clearance_and_inline_adjustment(
Clear::from_style_and_container_writing_mode(
&base.style,
containing_block.style.writing_mode,
),
&collapsed_margin_block_start,
pbm,
size,
);
(
(margin_inline_start, margin_inline_end),
effective_margin_inline_start,
) = solve_inline_margins_avoiding_floats(
sequential_layout_state,
containing_block,
pbm,
size.inline,
placement_rect,
justify_self,
);
// Clearance prevents margin collapse between this block and previous ones,
// so in that case collapse margins before adjoining them below.
if clearance.is_some() {
sequential_layout_state.collapse_margins();
}
sequential_layout_state.adjoin_assign(&collapsed_margin_block_start);
// Margins can never collapse into replaced elements.
sequential_layout_state.collapse_margins();
sequential_layout_state
.advance_block_position(size.block + clearance.unwrap_or_else(Au::zero));
sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin_block_end));
} else {
clearance = None;
(
(margin_inline_start, margin_inline_end),
effective_margin_inline_start,
) = solve_inline_margins_for_in_flow_block_level(
containing_block,
pbm,
content_size.inline,
justify_self,
);
};
let margin = LogicalSides {
inline_start: margin_inline_start,
inline_end: margin_inline_end,
block_start: margin_block_start,
block_end: margin_block_end,
};
let start_corner = LogicalVec2 {
block: pbm.padding.block_start +
pbm.border.block_start +
clearance.unwrap_or_else(Au::zero),
inline: pbm.padding.inline_start +
pbm.border.inline_start +
effective_margin_inline_start,
};
let content_rect = LogicalRect {
start_corner,
size: content_size,
}
.as_physical(Some(containing_block));
let mut base_fragment_info = base.base_fragment_info;
if content_box_sizes_and_pbm.depends_on_block_constraints {
base_fragment_info.flags.insert(
FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
);
}
BoxFragment::new(
base_fragment_info,
base.style.clone(),
fragments,
content_rect,
pbm.padding.to_physical(containing_block_writing_mode),
pbm.border.to_physical(containing_block_writing_mode),
margin.to_physical(containing_block_writing_mode),
clearance,
)
.with_block_margins_collapsed_with_children(CollapsedBlockMargins::from_margin(&margin))
}
}
struct ContainingBlockPaddingAndBorder<'a> {
containing_block: ContainingBlock<'a>,
pbm: PaddingBorderMargin,
@ -1736,6 +1617,7 @@ struct ContainingBlockPaddingAndBorder<'a> {
depends_on_block_constraints: bool,
available_block_size: Option<Au>,
justify_self: AlignFlags,
preferred_aspect_ratio: Option<AspectRatio>,
}
struct ResolvedMargins {
@ -1761,6 +1643,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
layout_style: &'a LayoutStyle,
get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
ignore_block_margins_for_stretch: LogicalSides1D<bool>,
context: Option<&IndependentFormattingContext>,
) -> ContainingBlockPaddingAndBorder<'a> {
let style = layout_style.style();
if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
@ -1787,6 +1670,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
// The initial `justify-self` is `auto`, but use `normal` (behaving as `stretch`).
// This is being discussed in <https://github.com/w3c/csswg-drafts/issues/11461>.
justify_self: AlignFlags::NORMAL,
preferred_aspect_ratio: None,
};
}
@ -1806,13 +1690,32 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
.to_definite()
.map(|block_size| Au::zero().max(block_size - pbm_sums.block));
// TODO: support preferred aspect ratios on boxes that don't establish an independent
// formatting context.
let preferred_aspect_ratio =
context.and_then(|context| context.preferred_aspect_ratio(&pbm.padding_border_sums));
let is_table = layout_style.is_table();
// https://drafts.csswg.org/css2/#the-height-property
// https://drafts.csswg.org/css2/visudet.html#min-max-heights
let tentative_block_size = content_box_sizes.block.resolve_extrinsic(
Size::FitContent,
Au::zero(),
available_block_size,
);
let tentative_block_content_size =
context.and_then(|context| context.tentative_block_content_size(preferred_aspect_ratio));
let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
SizeConstraint::Definite(content_box_sizes.block.resolve(
Direction::Block,
Size::FitContent,
Au::zero,
available_block_size,
|| block_content_size,
is_table,
))
} else {
content_box_sizes.block.resolve_extrinsic(
Size::FitContent,
Au::zero(),
available_block_size,
)
};
// https://drafts.csswg.org/css2/#the-width-property
// https://drafts.csswg.org/css2/visudet.html#min-max-widths
@ -1820,14 +1723,14 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
get_inline_content_sizes(&ConstraintSpace::new(
tentative_block_size,
writing_mode,
None, /* TODO: support preferred aspect ratios on non-replaced boxes */
preferred_aspect_ratio,
))
};
let justify_self = resolve_justify_self(style, containing_block.style);
let is_table = layout_style.is_table();
let is_replaced = context.is_some_and(|context| context.is_replaced());
let inline_size = content_box_sizes.inline.resolve(
Direction::Inline,
automatic_inline_size(justify_self, is_table),
automatic_inline_size(justify_self, is_table, is_replaced),
Au::zero,
Some(available_inline_size),
get_inline_content_sizes,
@ -1857,6 +1760,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
depends_on_block_constraints,
available_block_size,
justify_self,
preferred_aspect_ratio,
}
}
@ -1923,10 +1827,14 @@ fn resolve_justify_self(style: &ComputedValues, parent_style: &ComputedValues) -
/// Determines the automatic size for the inline axis of a block-level box.
/// <https://drafts.csswg.org/css-sizing-3/#automatic-size>
#[inline]
fn automatic_inline_size<T>(justify_self: AlignFlags, is_table: bool) -> Size<T> {
fn automatic_inline_size<T>(
justify_self: AlignFlags,
is_table: bool,
is_replaced: bool,
) -> Size<T> {
match justify_self {
AlignFlags::STRETCH => Size::Stretch,
AlignFlags::NORMAL if !is_table => Size::Stretch,
AlignFlags::NORMAL if !is_table && !is_replaced => Size::Stretch,
_ => Size::FitContent,
}
}
@ -2318,34 +2226,6 @@ pub(crate) struct IndependentFloatOrAtomicLayoutResult {
}
impl IndependentFormattingContext {
pub(crate) fn layout_in_flow_block_level(
&self,
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
sequential_layout_state: Option<&mut SequentialLayoutState>,
ignore_block_margins_for_stretch: LogicalSides1D<bool>,
) -> BoxFragment {
match &self.contents {
IndependentFormattingContextContents::NonReplaced(contents) => contents
.layout_in_flow_block_level(
&self.base,
layout_context,
positioning_context,
containing_block,
sequential_layout_state,
ignore_block_margins_for_stretch,
),
IndependentFormattingContextContents::Replaced(contents) => contents
.layout_in_flow_block_level(
&self.base,
layout_context,
containing_block,
sequential_layout_state,
ignore_block_margins_for_stretch,
),
}
}
pub(crate) fn layout_float_or_atomic_inline(
&self,
layout_context: &LayoutContext,

View file

@ -265,6 +265,48 @@ impl IndependentFormattingContext {
IndependentFormattingContextContents::Replaced(..) => {},
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn layout(
&self,
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
containing_block_for_children: &ContainingBlock,
containing_block: &ContainingBlock,
preferred_aspect_ratio: Option<AspectRatio>,
depends_on_block_constraints: bool,
lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
match &self.contents {
IndependentFormattingContextContents::NonReplaced(content) => content.layout(
layout_context,
positioning_context,
containing_block_for_children,
containing_block,
&self.base,
depends_on_block_constraints,
lazy_block_size,
),
IndependentFormattingContextContents::Replaced(content) => content.layout(
layout_context,
containing_block_for_children,
preferred_aspect_ratio,
&self.base,
depends_on_block_constraints,
lazy_block_size,
),
}
}
#[inline]
pub(crate) fn is_table(&self) -> bool {
matches!(
&self.contents,
IndependentFormattingContextContents::NonReplaced(
IndependentNonReplacedContents::Table(_)
)
)
}
}
impl IndependentNonReplacedContents {
@ -312,7 +354,7 @@ impl IndependentNonReplacedContents {
skip_all
)]
#[allow(clippy::too_many_arguments)]
pub fn layout(
pub(crate) fn layout(
&self,
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
@ -378,11 +420,6 @@ impl IndependentNonReplacedContents {
None
}
#[inline]
pub(crate) fn is_table(&self) -> bool {
matches!(self, Self::Table(_))
}
fn repair_style(
&mut self,
context: &SharedStyleContext,

View file

@ -26,11 +26,13 @@ use webrender_api::ImageKey;
use crate::cell::ArcRefCell;
use crate::context::{LayoutContext, LayoutImageCacheResult};
use crate::dom::NodeExt;
use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment};
use crate::geom::{
LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize, Size, Sizes,
use crate::fragment_tree::{
BaseFragmentInfo, CollapsedBlockMargins, Fragment, IFrameFragment, ImageFragment,
};
use crate::layout_box_base::LayoutBoxBase;
use crate::geom::{
LazySize, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize, Size, Sizes,
};
use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, LayoutStyle};
use crate::{ConstraintSpace, ContainingBlock, SizeConstraint};
@ -598,6 +600,40 @@ impl ReplacedContents {
pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
LayoutStyle::Default(&base.style)
}
pub(crate) fn layout(
&self,
layout_context: &LayoutContext,
containing_block_for_children: &ContainingBlock,
preferred_aspect_ratio: Option<AspectRatio>,
base: &LayoutBoxBase,
depends_on_block_constraints: bool,
lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
// TODO: consider caching the result in LayoutBoxBase like we do for non-replaced.
let writing_mode = base.style.writing_mode;
let inline_size = containing_block_for_children.size.inline;
let content_block_size = self.content_size(
Direction::Block,
preferred_aspect_ratio,
&|| SizeConstraint::Definite(inline_size),
&|| self.fallback_block_size(writing_mode),
);
let size = LogicalVec2 {
inline: inline_size,
block: lazy_block_size.resolve(|| content_block_size),
}
.to_physical_size(writing_mode);
CacheableLayoutResult {
baselines: Default::default(),
collapsible_margins_in_children: CollapsedBlockMargins::zero(),
content_block_size,
content_inline_size_for_table: None,
depends_on_block_constraints,
fragments: self.make_fragments(layout_context, &base.style, size),
specific_layout_info: None,
}
}
}
impl ComputeInlineContentSizes for ReplacedContents {

View file

@ -254076,6 +254076,32 @@
],
{}
]
],
"replaced-next-to-float-1.html": [
"b97f2ddba445dfdad6285691a509b3f2f7042b44",
[
null,
[
[
"/css/reference/ref-filled-green-100px-square.xht",
"=="
]
],
{}
]
],
"replaced-next-to-float-2.html": [
"1a00846074ba376528d82836a644952773ce0edf",
[
null,
[
[
"/css/reference/ref-filled-green-100px-square.xht",
"=="
]
],
{}
]
]
},
"svg-intrinsic-size-005.html": [

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/4028">
<link rel="help" href="https://github.com/servo/servo/issues/37861">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
<style>
#reference-overlapped-red {
position: absolute;
background-color: red;
width: 100px;
height: 100px;
z-index: -1;
}
</style>
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div id="reference-overlapped-red"></div>
<div style="width:200px; margin-left: -100px;">
<div style="float: left; width: 100px; height: 100px;"></div>
<canvas width="1" height="1" style="display: block; width: stretch; background: green"></canvas>
</div>

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/4028">
<link rel="help" href="https://github.com/servo/servo/issues/37861">
<link rel="help" href="https://drafts.csswg.org/css2/#floats">
<meta name="assert" content="The border box of a block-level replaced element
can't overlap the margin box of any float in the same block formatting context.
The stretch size needs to respect that.
">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
<style>
#reference-overlapped-red {
position: absolute;
background-color: red;
width: 100px;
height: 100px;
z-index: -1;
}
.stretch {
display: flow-root;
height: 25px;
background: green;
}
</style>
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div id="reference-overlapped-red"></div>
<div style="width:200px; margin-left: -100px;">
<div style="float: left; width: 100px; height: 75px;"></div>
<canvas class="stretch" style="width: 0; min-width: stretch"></canvas>
<canvas class="stretch" style="width: 1000px; max-width: stretch"></canvas>
<canvas class="stretch" style="width: stretch; padding: 0 10px; border: solid green; border-width: 0 10px; margin-left: 10px"></canvas>
</div>
<div style="width:250px; margin-left: -150px;">
<div style="float: left; clear: left; width: 100px; height: 1px;"></div>
<div style="float: left; clear: left; width: 150px; height: 1px;"></div>
<div style="float: left; clear: left; width: 125px; height: 1px;"></div>
<canvas class="stretch" style="width: stretch"></canvas>
</div>