mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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:
parent
72b1331949
commit
4ee7a34f32
8 changed files with 257 additions and 252 deletions
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
26
tests/wpt/meta/MANIFEST.json
vendored
26
tests/wpt/meta/MANIFEST.json
vendored
|
@ -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": [
|
||||
|
|
23
tests/wpt/tests/css/css-sizing/stretch/replaced-next-to-float-1.html
vendored
Normal file
23
tests/wpt/tests/css/css-sizing/stretch/replaced-next-to-float-1.html
vendored
Normal 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>
|
41
tests/wpt/tests/css/css-sizing/stretch/replaced-next-to-float-2.html
vendored
Normal file
41
tests/wpt/tests/css/css-sizing/stretch/replaced-next-to-float-2.html
vendored
Normal 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>
|
Loading…
Add table
Add a link
Reference in a new issue