aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2023-07-03 09:41:03 +0200
committerMartin Robinson <mrobinson@igalia.com>2023-07-03 16:11:44 +0200
commitfa40c22122f50af14ca24081d5480bd1a65eefb8 (patch)
tree5b572f8f2c94fb6b9a56e59fb7c69f068ad7bf48
parentc19eb800de71bf06736781ca1cf9d57620d66f8d (diff)
downloadservo-fa40c22122f50af14ca24081d5480bd1a65eefb8.tar.gz
servo-fa40c22122f50af14ca24081d5480bd1a65eefb8.zip
Split layout_in_flow_non_replaced_block_level
This function is already quite big and the changes necessary for properly laying out independent formatting contexts next to floats will make it even more unwieldy. Split the function in two and add a helper for calculating the containing block for children and the margin. As time goes on the independent formatting case is probably going to be more like the replaced case anyway. Co-authored-by: Oriol Brufau <obrufau@igalia.com>
-rw-r--r--components/layout_2020/flow/mod.rs374
1 files changed, 226 insertions, 148 deletions
diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs
index e881ed61347..2d1da5a570b 100644
--- a/components/layout_2020/flow/mod.rs
+++ b/components/layout_2020/flow/mod.rs
@@ -488,13 +488,13 @@ impl BlockLevelBox {
containing_block,
style,
|positioning_context| {
- layout_in_flow_non_replaced_block_level(
+ layout_in_flow_non_replaced_block_level_same_formatting_context(
layout_context,
positioning_context,
containing_block,
*tag,
style,
- NonReplacedContents::SameFormattingContextBlock(contents),
+ contents,
sequential_layout_state,
collapsible_with_parent_start_margin,
)
@@ -523,17 +523,14 @@ impl BlockLevelBox {
containing_block,
&non_replaced.style,
|positioning_context| {
- layout_in_flow_non_replaced_block_level(
+ layout_in_flow_non_replaced_block_level_independent_formatting_context(
layout_context,
positioning_context,
containing_block,
non_replaced.base_fragment_info,
&non_replaced.style,
- NonReplacedContents::EstablishesAnIndependentFormattingContext(
- non_replaced,
- ),
+ non_replaced,
sequential_layout_state,
- collapsible_with_parent_start_margin,
)
},
))
@@ -581,93 +578,33 @@ impl BlockLevelBox {
}
}
-enum NonReplacedContents<'a> {
- SameFormattingContextBlock(&'a BlockContainer),
- EstablishesAnIndependentFormattingContext(&'a NonReplacedFormattingContext),
-}
-
-/// https://drafts.csswg.org/css2/visudet.html#blockwidth
-/// https://drafts.csswg.org/css2/visudet.html#normal-block
-fn layout_in_flow_non_replaced_block_level(
+/// Lay out a normal flow non-replaced block that does not establish a new formatting
+/// context.
+///
+/// - https://drafts.csswg.org/css2/visudet.html#blockwidth
+/// - https://drafts.csswg.org/css2/visudet.html#normal-block
+fn layout_in_flow_non_replaced_block_level_same_formatting_context(
layout_context: &LayoutContext,
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
base_fragment_info: BaseFragmentInfo,
style: &Arc<ComputedValues>,
- block_level_kind: NonReplacedContents,
+ contents: &BlockContainer,
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
) -> BoxFragment {
- let pbm = style.padding_border_margin(containing_block);
- let box_size = style.content_box_size(containing_block, &pbm);
- let max_box_size = style.content_max_box_size(containing_block, &pbm);
- let min_box_size = style
- .content_min_box_size(containing_block, &pbm)
- .auto_is(Length::zero);
-
- // https://drafts.csswg.org/css2/visudet.html#min-max-widths
- let solve_inline_margins = |inline_size| {
- solve_inline_margins_for_in_flow_block_level(containing_block, &pbm, inline_size)
- };
- let (mut inline_size, mut inline_margins) =
- if let Some(inline_size) = box_size.inline.non_auto() {
- (inline_size, solve_inline_margins(inline_size))
- } else {
- let margin_inline_start = pbm.margin.inline_start.auto_is(Length::zero);
- let margin_inline_end = pbm.margin.inline_end.auto_is(Length::zero);
- let inline_size = containing_block.inline_size -
- pbm.padding_border_sums.inline -
- margin_inline_start -
- margin_inline_end;
- (inline_size, (margin_inline_start, margin_inline_end))
- };
- if let Some(max_inline_size) = max_box_size.inline {
- if inline_size > max_inline_size {
- inline_size = max_inline_size;
- inline_margins = solve_inline_margins(inline_size);
- }
- }
- if inline_size < min_box_size.inline {
- inline_size = min_box_size.inline;
- inline_margins = solve_inline_margins(inline_size);
- }
-
- let margin = Sides {
- inline_start: inline_margins.0,
- inline_end: inline_margins.1,
- block_start: pbm.margin.block_start.auto_is(Length::zero),
- block_end: pbm.margin.block_end.auto_is(Length::zero),
- };
-
- // https://drafts.csswg.org/css2/visudet.html#min-max-heights
- let mut block_size = box_size.block;
- if let LengthOrAuto::LengthPercentage(ref mut block_size) = block_size {
- *block_size = block_size.clamp_between_extremums(min_box_size.block, max_box_size.block);
- }
-
- let containing_block_for_children = ContainingBlock {
- inline_size,
- block_size,
- style,
- };
-
- // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
- assert_eq!(
- containing_block.style.writing_mode, containing_block_for_children.style.writing_mode,
- "Mixed writing modes are not supported yet"
- );
-
- let block_is_same_formatting_context = match block_level_kind {
- NonReplacedContents::SameFormattingContextBlock(_) => true,
- NonReplacedContents::EstablishesAnIndependentFormattingContext(_) => false,
- };
+ let ContainingBlockPaddingBorderAndMargin {
+ containing_block: containing_block_for_children,
+ pbm,
+ min_box_size,
+ max_box_size,
+ margin,
+ } = solve_containing_block_padding_border_and_margin_for_in_flow_box(containing_block, style);
let computed_block_size = style.content_block_size();
- let start_margin_can_collapse_with_children = block_is_same_formatting_context &&
- pbm.padding.block_start == Length::zero() &&
- pbm.border.block_start == Length::zero();
- let end_margin_can_collapse_with_children = block_is_same_formatting_context &&
- pbm.padding.block_end == Length::zero() &&
+ let start_margin_can_collapse_with_children =
+ pbm.padding.block_start == Length::zero() && pbm.border.block_start == Length::zero();
+ let end_margin_can_collapse_with_children = pbm.padding.block_end == Length::zero() &&
pbm.border.block_end == Length::zero() &&
computed_block_size.is_auto();
@@ -694,12 +631,9 @@ fn layout_in_flow_non_replaced_block_level(
when laying out sequentially",
).0 && style.get_box().clear == Clear::None;
if !collapsible_with_parent_start_margin && start_margin_can_collapse_with_children {
- if let NonReplacedContents::SameFormattingContextBlock(
- BlockContainer::BlockLevelBoxes(child_boxes),
- ) = block_level_kind
- {
+ if let BlockContainer::BlockLevelBoxes(child_boxes) = contents {
BlockLevelBox::find_block_margin_collapsing_with_parent_from_slice(
- child_boxes,
+ &child_boxes,
&mut block_start_margin,
containing_block,
);
@@ -737,7 +671,7 @@ fn layout_in_flow_non_replaced_block_level(
block_start: sequential_layout_state.bfc_relative_block_position,
block_start_margins_not_collapsed: sequential_layout_state.current_margin,
inline_start,
- inline_end: inline_start + inline_size,
+ inline_end: inline_start + containing_block_for_children.inline_size,
};
parent_containing_block_position_info = Some(
sequential_layout_state.replace_containing_block_position_info(new_cb_offsets),
@@ -745,65 +679,47 @@ fn layout_in_flow_non_replaced_block_level(
},
};
+ let flow_layout = contents.layout(
+ layout_context,
+ positioning_context,
+ &containing_block_for_children,
+ sequential_layout_state.as_mut().map(|x| &mut **x),
+ CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
+ );
+ let mut content_block_size = flow_layout.content_block_size;
+
+ // Update margins.
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
+ let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
+ if start_margin_can_collapse_with_children {
+ block_margins_collapsed_with_children
+ .start
+ .adjoin_assign(&collapsible_margins_in_children.start);
+ if collapsible_margins_in_children.collapsed_through {
+ block_margins_collapsed_with_children
+ .start
+ .adjoin_assign(&std::mem::replace(
+ &mut collapsible_margins_in_children.end,
+ CollapsedMargin::zero(),
+ ));
+ }
+ }
+ if end_margin_can_collapse_with_children {
+ block_margins_collapsed_with_children
+ .end
+ .adjoin_assign(&collapsible_margins_in_children.end);
+ } else {
+ content_block_size += collapsible_margins_in_children.end.solve();
+ }
- let fragments;
- let mut content_block_size;
- match block_level_kind {
- NonReplacedContents::SameFormattingContextBlock(contents) => {
- let flow_layout = contents.layout(
- layout_context,
- positioning_context,
- &containing_block_for_children,
- sequential_layout_state.as_mut().map(|x| &mut **x),
- CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
- );
+ let computed_min_block_size = style.min_block_size();
+ block_margins_collapsed_with_children.collapsed_through = collapsible_margins_in_children
+ .collapsed_through &&
+ pbm.padding_border_sums.block == Length::zero() &&
+ (computed_block_size.is_definitely_zero() || computed_block_size.is_auto()) &&
+ (computed_min_block_size.is_definitely_zero() || computed_min_block_size.is_auto());
- fragments = flow_layout.fragments;
- content_block_size = flow_layout.content_block_size;
-
- // Update margins.
- let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
- if start_margin_can_collapse_with_children {
- block_margins_collapsed_with_children
- .start
- .adjoin_assign(&collapsible_margins_in_children.start);
- if collapsible_margins_in_children.collapsed_through {
- block_margins_collapsed_with_children
- .start
- .adjoin_assign(&std::mem::replace(
- &mut collapsible_margins_in_children.end,
- CollapsedMargin::zero(),
- ));
- }
- }
- if end_margin_can_collapse_with_children {
- block_margins_collapsed_with_children
- .end
- .adjoin_assign(&collapsible_margins_in_children.end);
- } else {
- content_block_size += collapsible_margins_in_children.end.solve();
- }
- let computed_min_block_size = style.min_block_size();
- block_margins_collapsed_with_children.collapsed_through =
- collapsible_margins_in_children.collapsed_through &&
- pbm.padding_border_sums.block == Length::zero() &&
- (computed_block_size.is_definitely_zero() || computed_block_size.is_auto()) &&
- (computed_min_block_size.is_definitely_zero() ||
- computed_min_block_size.is_auto());
- },
- NonReplacedContents::EstablishesAnIndependentFormattingContext(non_replaced) => {
- let independent_layout = non_replaced.layout(
- layout_context,
- positioning_context,
- &containing_block_for_children,
- );
- fragments = independent_layout.fragments;
- content_block_size = independent_layout.content_block_size;
- },
- };
-
- let block_size = block_size.auto_is(|| {
+ let block_size = containing_block_for_children.block_size.auto_is(|| {
content_block_size.clamp_between_extremums(min_box_size.block, max_box_size.block)
});
@@ -835,14 +751,92 @@ fn layout_in_flow_non_replaced_block_level(
},
size: Vec2 {
block: block_size,
- inline: inline_size,
+ inline: containing_block_for_children.inline_size,
},
};
BoxFragment::new(
base_fragment_info,
style.clone(),
- fragments,
+ flow_layout.fragments,
+ content_rect,
+ pbm.padding,
+ pbm.border,
+ margin,
+ clearance,
+ block_margins_collapsed_with_children,
+ )
+}
+
+/// Lay out a normal flow non-replaced block that establishes and independent formatting
+/// context in its containing formatting context.
+///
+/// - https://drafts.csswg.org/css2/visudet.html#blockwidth
+/// - https://drafts.csswg.org/css2/visudet.html#normal-block
+fn layout_in_flow_non_replaced_block_level_independent_formatting_context(
+ layout_context: &LayoutContext,
+ positioning_context: &mut PositioningContext,
+ containing_block: &ContainingBlock,
+ base_fragment_info: BaseFragmentInfo,
+ style: &Arc<ComputedValues>,
+ independent_formatting_context: &NonReplacedFormattingContext,
+ mut sequential_layout_state: Option<&mut SequentialLayoutState>,
+) -> BoxFragment {
+ let ContainingBlockPaddingBorderAndMargin {
+ containing_block: containing_block_for_children,
+ pbm,
+ min_box_size,
+ max_box_size,
+ margin,
+ } = solve_containing_block_padding_border_and_margin_for_in_flow_box(containing_block, style);
+
+ let layout = independent_formatting_context.layout(
+ layout_context,
+ positioning_context,
+ &containing_block_for_children,
+ );
+
+ let content_block_size = layout.content_block_size;
+ let block_size = containing_block_for_children.block_size.auto_is(|| {
+ content_block_size.clamp_between_extremums(min_box_size.block, max_box_size.block)
+ });
+
+ let mut clearance = None;
+ if let Some(ref mut sequential_layout_state) = sequential_layout_state {
+ clearance = sequential_layout_state.calculate_clearance_and_adjoin_margin(
+ style,
+ &CollapsedMargin::new(margin.block_start),
+ );
+ sequential_layout_state.collapse_margins();
+
+ // Account for padding and border. We also might have to readjust the
+ // `bfc_relative_block_position` if it was different from the content size (i.e. was
+ // non-`auto` and/or was affected by min/max block size).
+ sequential_layout_state.advance_block_position(
+ (block_size - content_block_size) + pbm.padding.block_sum() + pbm.border.block_sum(),
+ );
+
+ sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
+ }
+
+ let content_rect = Rect {
+ start_corner: Vec2 {
+ block: pbm.padding.block_start +
+ pbm.border.block_start +
+ clearance.unwrap_or_else(Length::zero),
+ inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
+ },
+ size: Vec2 {
+ block: block_size,
+ inline: containing_block_for_children.inline_size,
+ },
+ };
+
+ let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
+ BoxFragment::new(
+ base_fragment_info,
+ style.clone(),
+ layout.fragments,
content_rect,
pbm.padding,
pbm.border,
@@ -915,6 +909,90 @@ fn layout_in_flow_replaced_block_level<'a>(
)
}
+struct ContainingBlockPaddingBorderAndMargin<'a> {
+ containing_block: ContainingBlock<'a>,
+ pbm: PaddingBorderMargin,
+ min_box_size: Vec2<Length>,
+ max_box_size: Vec2<Option<Length>>,
+ margin: Sides<Length>,
+}
+
+/// Given the style for in in flow box and its containing block, determine the containing
+/// block for its children and the margin it should use.
+fn solve_containing_block_padding_border_and_margin_for_in_flow_box<'a>(
+ containing_block: &ContainingBlock<'_>,
+ style: &'a Arc<ComputedValues>,
+) -> ContainingBlockPaddingBorderAndMargin<'a> {
+ let pbm = style.padding_border_margin(containing_block);
+ let box_size = style.content_box_size(containing_block, &pbm);
+ let max_box_size = style.content_max_box_size(containing_block, &pbm);
+ let min_box_size = style
+ .content_min_box_size(containing_block, &pbm)
+ .auto_is(Length::zero);
+
+ let (mut inline_size, mut inline_margins) =
+ if let Some(inline_size) = box_size.inline.non_auto() {
+ (
+ inline_size,
+ solve_inline_margins_for_in_flow_block_level(containing_block, &pbm, inline_size),
+ )
+ } else {
+ let margin_inline_start = pbm.margin.inline_start.auto_is(Length::zero);
+ let margin_inline_end = pbm.margin.inline_end.auto_is(Length::zero);
+ let inline_size = containing_block.inline_size -
+ pbm.padding_border_sums.inline -
+ margin_inline_start -
+ margin_inline_end;
+ (inline_size, (margin_inline_start, margin_inline_end))
+ };
+
+ // https://drafts.csswg.org/css2/visudet.html#min-max-widths
+ if let Some(max_inline_size) = max_box_size.inline {
+ if inline_size > max_inline_size {
+ inline_size = max_inline_size;
+ inline_margins =
+ solve_inline_margins_for_in_flow_block_level(containing_block, &pbm, inline_size);
+ }
+ }
+
+ if inline_size < min_box_size.inline {
+ inline_size = min_box_size.inline;
+ inline_margins =
+ solve_inline_margins_for_in_flow_block_level(containing_block, &pbm, inline_size);
+ }
+
+ let margin = Sides {
+ inline_start: inline_margins.0,
+ inline_end: inline_margins.1,
+ block_start: pbm.margin.block_start.auto_is(Length::zero),
+ block_end: pbm.margin.block_end.auto_is(Length::zero),
+ };
+
+ // https://drafts.csswg.org/css2/visudet.html#min-max-heights
+ let mut block_size = box_size.block;
+ if let LengthOrAuto::LengthPercentage(ref mut block_size) = block_size {
+ *block_size = block_size.clamp_between_extremums(min_box_size.block, max_box_size.block);
+ }
+
+ let containing_block_for_children = ContainingBlock {
+ inline_size,
+ block_size,
+ style,
+ };
+ // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
+ assert_eq!(
+ containing_block.style.writing_mode, containing_block_for_children.style.writing_mode,
+ "Mixed writing modes are not supported yet"
+ );
+ ContainingBlockPaddingBorderAndMargin {
+ containing_block: containing_block_for_children,
+ pbm,
+ min_box_size,
+ max_box_size,
+ margin,
+ }
+}
+
fn solve_inline_margins_for_in_flow_block_level(
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,