1#![allow(rustdoc::private_intra_doc_links)]
5
6use app_units::{Au, MAX_AU};
9use inline::InlineFormattingContext;
10use layout_api::LayoutNode;
11use malloc_size_of_derive::MallocSizeOf;
12use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
13use script::layout_dom::ServoLayoutNode;
14use servo_arc::Arc as ServoArc;
15use style::Zero;
16use style::computed_values::clear::T as StyleClear;
17use style::context::SharedStyleContext;
18use style::logical_geometry::Direction;
19use style::properties::ComputedValues;
20use style::servo::selector_parser::PseudoElement;
21use style::values::specified::align::AlignFlags;
22use style::values::specified::{Display, TextAlignKeyword};
23
24use crate::cell::ArcRefCell;
25use crate::context::LayoutContext;
26use crate::dom::WeakLayoutBox;
27use crate::flow::float::{Clear, FloatBox, FloatSide, PlacementAmongFloats, SequentialLayoutState};
28use crate::flow::same_formatting_context_block::SameFormattingContextBlock;
29use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
30use crate::fragment_tree::{
31 BaseFragmentInfo, BlockLevelLayoutInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin,
32 Fragment, FragmentFlags, PositioningFragment,
33};
34use crate::geom::{
35 AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
36 PhysicalSides, ToLogical, ToLogicalWithContainingBlock,
37};
38use crate::layout_box_base::{IndependentFormattingContextLayoutResult, LayoutBoxBase};
39use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
40use crate::sizing::{
41 self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize, Size,
42 SizeConstraint, Sizes,
43};
44use crate::style_ext::{AspectRatio, ContentBoxSizesAndPBM, LayoutStyle, PaddingBorderMargin};
45use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
46
47mod construct;
48pub mod float;
49pub mod inline;
50mod root;
51mod same_formatting_context_block;
52
53pub(crate) use construct::{BlockContainerBuilder, BlockLevelCreator};
54pub(crate) use root::BoxTree;
55
56#[derive(Debug, MallocSizeOf)]
57pub(crate) struct BlockFormattingContext {
58 pub contents: BlockContainer,
59 pub contains_floats: bool,
60}
61
62#[derive(Debug, MallocSizeOf)]
63pub(crate) enum BlockContainer {
64 BlockLevelBoxes(Vec<ArcRefCell<BlockLevelBox>>),
65 InlineFormattingContext(InlineFormattingContext),
66}
67
68impl BlockContainer {
69 fn contains_floats(&self) -> bool {
70 match self {
71 BlockContainer::BlockLevelBoxes(boxes) => boxes
72 .iter()
73 .any(|block_level_box| block_level_box.borrow().contains_floats()),
74 BlockContainer::InlineFormattingContext(context) => context.contains_floats,
75 }
76 }
77
78 pub(crate) fn repair_style(
79 &mut self,
80 context: &SharedStyleContext,
81 node: &ServoLayoutNode,
82 new_style: &ServoArc<ComputedValues>,
83 ) {
84 match self {
85 BlockContainer::BlockLevelBoxes(..) => {},
86 BlockContainer::InlineFormattingContext(inline_formatting_context) => {
87 inline_formatting_context.repair_style(context, node, new_style)
88 },
89 }
90 }
91
92 pub(crate) fn subtree_size(&self) -> usize {
93 match self {
94 BlockContainer::BlockLevelBoxes(boxes) => boxes
95 .iter()
96 .map(|block_level_box| block_level_box.borrow().subtree_size())
97 .sum(),
98 BlockContainer::InlineFormattingContext(inline_formatting_context) => {
99 inline_formatting_context.subtree_size()
100 },
101 }
102 }
103}
104
105#[derive(Debug, MallocSizeOf)]
106pub(crate) enum BlockLevelBox {
107 Independent(IndependentFormattingContext),
108 OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
109 OutOfFlowFloatBox(FloatBox),
110 OutsideMarker(OutsideMarker),
111 SameFormattingContextBlock(SameFormattingContextBlock),
112}
113
114impl BlockLevelBox {
115 pub(crate) fn repair_style(
116 &mut self,
117 context: &SharedStyleContext,
118 node: &ServoLayoutNode,
119 new_style: &ServoArc<ComputedValues>,
120 ) {
121 match self {
122 BlockLevelBox::Independent(independent_formatting_context) => {
123 independent_formatting_context.repair_style(context, node, new_style)
124 },
125 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
126 .borrow_mut()
127 .context
128 .repair_style(context, node, new_style),
129 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
130 float_box.contents.repair_style(context, node, new_style)
131 },
132 BlockLevelBox::OutsideMarker(outside_marker) => {
133 outside_marker.repair_style(context, node, new_style)
134 },
135 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
136 same_formatting_context_block.repair_style(context, node, new_style)
137 },
138 }
139 }
140
141 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
142 match self {
143 BlockLevelBox::Independent(independent_formatting_context) => {
144 callback(&independent_formatting_context.base)
145 },
146 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
147 callback(&positioned_box.borrow().context.base)
148 },
149 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&float_box.contents.base),
150 BlockLevelBox::OutsideMarker(outside_marker) => callback(&outside_marker.context.base),
151 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
152 callback(&same_formatting_context_block.base)
153 },
154 }
155 }
156
157 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
158 match self {
159 BlockLevelBox::Independent(independent_formatting_context) => {
160 callback(&mut independent_formatting_context.base)
161 },
162 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
163 callback(&mut positioned_box.borrow_mut().context.base)
164 },
165 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base),
166 BlockLevelBox::OutsideMarker(outside_marker) => {
167 callback(&mut outside_marker.context.base)
168 },
169 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
170 callback(&mut same_formatting_context_block.base)
171 },
172 }
173 }
174
175 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
176 match self {
177 Self::Independent(independent_formatting_context) => {
178 independent_formatting_context.attached_to_tree(layout_box)
179 },
180 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
181 positioned_box.borrow().context.attached_to_tree(layout_box)
182 },
183 Self::OutOfFlowFloatBox(float_box) => float_box.contents.attached_to_tree(layout_box),
184 Self::OutsideMarker(outside_marker) => {
185 outside_marker.context.attached_to_tree(layout_box)
186 },
187 Self::SameFormattingContextBlock(same_formatting_context_block) => {
188 same_formatting_context_block
189 .contents
190 .attached_to_tree(layout_box)
191 },
192 }
193 }
194
195 fn contains_floats(&self) -> bool {
196 match self {
197 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
198 same_formatting_context_block.contains_floats
199 },
200 BlockLevelBox::OutOfFlowFloatBox { .. } => true,
201 _ => false,
202 }
203 }
204
205 fn find_block_margin_collapsing_with_parent(
206 &self,
207 layout_context: &LayoutContext,
208 collected_margin: &mut CollapsedMargin,
209 containing_block: &ContainingBlock,
210 ) -> bool {
211 let layout_style = match self {
212 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
213 same_formatting_context_block.layout_style()
214 },
215 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
216 BlockLevelBox::OutOfFlowFloatBox(_) => return true,
217 BlockLevelBox::OutsideMarker(_) => return false,
218 BlockLevelBox::Independent(context) => {
219 context.layout_style()
222 },
223 };
224
225 let style = layout_style.style();
227 if style.get_box().clear != StyleClear::None {
228 return false;
229 }
230
231 let ContentBoxSizesAndPBM {
232 content_box_sizes,
233 pbm,
234 ..
235 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
236 let margin = pbm.margin.auto_is(Au::zero);
237 collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_start));
238
239 let BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) = self else {
240 return false;
241 };
242
243 if !pbm.padding.block_start.is_zero() || !pbm.border.block_start.is_zero() {
244 return false;
245 }
246
247 let available_inline_size =
248 containing_block.size.inline - pbm.padding_border_sums.inline - margin.inline_sum();
249 let available_block_size = containing_block.size.block.to_definite().map(|block_size| {
250 Au::zero().max(block_size - pbm.padding_border_sums.block - margin.block_sum())
251 });
252
253 let tentative_block_size = content_box_sizes.block.resolve_extrinsic(
254 Size::FitContent,
255 Au::zero(),
256 available_block_size,
257 );
258
259 let get_inline_content_sizes = || {
260 let constraint_space = ConstraintSpace::new(
261 tentative_block_size,
262 style,
263 None, );
265 self.inline_content_sizes(layout_context, &constraint_space)
266 .sizes
267 };
268 let inline_size = content_box_sizes.inline.resolve(
269 Direction::Inline,
270 Size::Stretch,
271 Au::zero,
272 Some(available_inline_size),
273 get_inline_content_sizes,
274 false, );
276
277 let containing_block_for_children = ContainingBlock {
278 size: ContainingBlockSize {
279 inline: inline_size,
280 block: tentative_block_size,
281 },
282 style,
283 };
284
285 if !same_formatting_context_block
286 .contents
287 .find_block_margin_collapsing_with_parent(
288 layout_context,
289 collected_margin,
290 &containing_block_for_children,
291 )
292 {
293 return false;
294 }
295
296 if !tentative_block_size.definite_or_min().is_zero() ||
297 !pbm.padding_border_sums.block.is_zero()
298 {
299 return false;
300 }
301
302 collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_end));
303
304 true
305 }
306
307 fn subtree_size(&self) -> usize {
308 self.with_base(|base| base.subtree_size())
309 }
310}
311
312#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
313pub(crate) struct CollapsibleWithParentStartMargin(bool);
314
315#[derive(Debug, MallocSizeOf)]
318pub(crate) struct OutsideMarker {
319 pub list_item_style: ServoArc<ComputedValues>,
320 pub context: IndependentFormattingContext,
321}
322
323impl OutsideMarker {
324 fn layout(
325 &self,
326 layout_context: &LayoutContext<'_>,
327 containing_block: &ContainingBlock<'_>,
328 positioning_context: &mut PositioningContext,
329 ) -> Fragment {
330 let style = &self.context.base.style;
331 let preferred_aspect_ratio = self.context.preferred_aspect_ratio(&LogicalVec2::zero());
332 let constraint_space =
333 ConstraintSpace::new(SizeConstraint::default(), style, preferred_aspect_ratio);
334 let content_sizes = self
335 .context
336 .inline_content_sizes(layout_context, &constraint_space);
337 let containing_block_for_children = ContainingBlock {
338 size: ContainingBlockSize {
339 inline: content_sizes.sizes.max_content,
340 block: SizeConstraint::default(),
341 },
342 style,
343 };
344
345 let layout = self.context.layout(
346 layout_context,
347 positioning_context,
348 &containing_block_for_children,
349 containing_block,
350 preferred_aspect_ratio,
351 &LazySize::intrinsic(),
352 );
353
354 let max_inline_size = layout
355 .fragments
356 .iter()
357 .map(|fragment| {
358 fragment
359 .base()
360 .map(|base| base.rect())
361 .unwrap_or_default()
362 .to_logical(&containing_block_for_children)
363 .max_inline_position()
364 })
365 .max()
366 .unwrap_or_default();
367
368 let pbm_of_list_item =
378 LayoutStyle::Default(&self.list_item_style).padding_border_margin(containing_block);
379 let content_rect = LogicalRect {
380 start_corner: LogicalVec2 {
381 inline: -max_inline_size -
382 (pbm_of_list_item.border.inline_start +
383 pbm_of_list_item.padding.inline_start),
384 block: Zero::zero(),
385 },
386 size: LogicalVec2 {
387 inline: max_inline_size,
388 block: layout.content_block_size,
389 },
390 };
391
392 let mut base_fragment_info = BaseFragmentInfo::anonymous();
393 base_fragment_info.flags |= FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER;
394
395 Fragment::Box(
396 BoxFragment::new(
397 base_fragment_info,
398 style.clone(),
399 layout.fragments,
400 content_rect.as_physical(Some(containing_block)),
401 PhysicalSides::zero(),
402 PhysicalSides::zero(),
403 PhysicalSides::zero(),
404 layout.specific_layout_info,
405 )
406 .into(),
407 )
408 }
409
410 fn repair_style(
411 &mut self,
412 context: &SharedStyleContext,
413 node: &ServoLayoutNode,
414 new_style: &ServoArc<ComputedValues>,
415 ) {
416 self.list_item_style = node.parent_style(context);
417 self.context.repair_style(context, node, new_style);
418 }
419}
420
421impl BlockFormattingContext {
422 pub(super) fn layout(
423 &self,
424 layout_context: &LayoutContext,
425 positioning_context: &mut PositioningContext,
426 containing_block: &ContainingBlock,
427 lazy_block_size: &LazySize,
428 base: Option<&LayoutBoxBase>,
429 ) -> IndependentFormattingContextLayoutResult {
430 let mut sequential_layout_state =
431 if self.contains_floats || !layout_context.allow_parallel_layout {
432 Some(SequentialLayoutState::new(containing_block.size.inline))
433 } else {
434 None
435 };
436
437 let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
441
442 let previous_positioning_context_len = positioning_context.len();
445
446 let mut flow_layout = self.contents.layout(
447 layout_context,
448 positioning_context,
449 containing_block,
450 sequential_layout_state.as_mut(),
451 CollapsibleWithParentStartMargin(false),
452 ignore_block_margins_for_stretch,
453 );
454 debug_assert!(
455 !flow_layout
456 .collapsible_margins_in_children
457 .collapsed_through
458 );
459
460 let clearance = sequential_layout_state.and_then(|sequential_layout_state| {
464 sequential_layout_state.calculate_clearance(Clear::Both, &CollapsedMargin::zero())
465 });
466
467 let content_block_size = flow_layout.content_block_size +
468 flow_layout.collapsible_margins_in_children.end.solve() +
469 clearance.unwrap_or_default();
470
471 if let Some(base) = base &&
475 base.base_fragment_info
476 .flags
477 .contains(FragmentFlags::IS_BUTTON)
478 {
479 flow_layout.depends_on_block_constraints = true;
480 let final_block_size = lazy_block_size.resolve(|| content_block_size);
481 let align_fragment_rect = LogicalRect {
482 start_corner: LogicalVec2 {
483 inline: Au::zero(),
484 block: Au::zero().max((final_block_size - content_block_size) / 2),
485 },
486 size: LogicalVec2 {
487 inline: containing_block.size.inline,
488 block: content_block_size,
489 },
490 }
491 .as_physical(Some(containing_block));
492 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
493 &align_fragment_rect.origin.to_vector(),
494 previous_positioning_context_len,
495 );
496 let align_fragment = PositioningFragment::new_anonymous(
497 base.style.clone(),
498 align_fragment_rect,
499 flow_layout.fragments,
500 false, );
502 flow_layout.fragments = vec![Fragment::Positioning(align_fragment)];
503 }
504
505 IndependentFormattingContextLayoutResult {
506 fragments: flow_layout.fragments,
507 content_block_size,
508 content_inline_size_for_table: None,
509 baselines: flow_layout.baselines,
510 depends_on_block_constraints: flow_layout.depends_on_block_constraints,
511 specific_layout_info: None,
512 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
513 }
514 }
515
516 #[inline]
517 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
518 LayoutStyle::Default(&base.style)
519 }
520
521 pub(crate) fn repair_style(
522 &mut self,
523 context: &SharedStyleContext,
524 node: &ServoLayoutNode,
525 new_style: &ServoArc<ComputedValues>,
526 ) {
527 self.contents.repair_style(context, node, new_style);
528 }
529
530 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
531 self.contents.attached_to_tree(layout_box);
532 }
533}
534
535fn compute_inline_content_sizes_for_block_level_boxes(
541 boxes: &[ArcRefCell<BlockLevelBox>],
542 layout_context: &LayoutContext,
543 containing_block: &IndefiniteContainingBlock,
544) -> InlineContentSizesResult {
545 let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
546 match &*box_.borrow() {
547 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
548 BlockLevelBox::OutsideMarker { .. } => None,
549 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
550 let inline_content_sizes_result = float_box.contents.outer_inline_content_sizes(
551 layout_context,
552 containing_block,
553 &LogicalVec2::zero(),
554 false, );
556 let style = &float_box.contents.style();
557 let container_writing_mode = containing_block.style.writing_mode;
558 Some((
559 inline_content_sizes_result,
560 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode),
561 Clear::from_style_and_container_writing_mode(style, container_writing_mode),
562 ))
563 },
564 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
565 let base = &same_formatting_context_block.base;
566 let contents = &same_formatting_context_block.contents;
567 let is_anonymous_block =
568 matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
569 let inline_content_sizes_result = sizing::outer_inline(
570 base,
571 &contents.layout_style(base),
572 containing_block,
573 &LogicalVec2::zero(),
574 false, false, !is_anonymous_block, |_| None, |constraint_space| {
579 base.inline_content_sizes(layout_context, constraint_space, contents)
580 },
581 |_aspect_ratio| None,
582 );
583 let clear = if is_anonymous_block {
592 Clear::None
593 } else {
594 Clear::Both
595 };
596 Some((inline_content_sizes_result, None, clear))
597 },
598 BlockLevelBox::Independent(independent) => {
599 let inline_content_sizes_result = independent.outer_inline_content_sizes(
600 layout_context,
601 containing_block,
602 &LogicalVec2::zero(),
603 false, );
605 Some((
606 inline_content_sizes_result,
607 None,
608 Clear::from_style_and_container_writing_mode(
609 independent.style(),
610 containing_block.style.writing_mode,
611 ),
612 ))
613 },
614 }
615 };
616
617 #[derive(Default)]
620 struct AccumulatedData {
621 depends_on_block_constraints: bool,
623 max_size: ContentSizes,
625 floats: LogicalSides1D<ContentSizes>,
628 }
629
630 impl AccumulatedData {
631 fn max_size_including_uncleared_floats(&self) -> ContentSizes {
632 self.max_size.max(self.floats.start.union(&self.floats.end))
633 }
634 fn clear_floats(&mut self, clear: Clear) {
635 match clear {
636 Clear::InlineStart => {
637 self.max_size = self.max_size_including_uncleared_floats();
638 self.floats.start = ContentSizes::default();
639 },
640 Clear::InlineEnd => {
641 self.max_size = self.max_size_including_uncleared_floats();
642 self.floats.end = ContentSizes::default();
643 },
644 Clear::Both => {
645 self.max_size = self.max_size_including_uncleared_floats();
646 self.floats = LogicalSides1D::default();
647 },
648 Clear::None => {},
649 };
650 }
651 }
652
653 let accumulate =
654 |mut data: AccumulatedData,
655 (inline_content_sizes_result, float, clear): (InlineContentSizesResult, _, _)| {
656 let size = inline_content_sizes_result.sizes.max(ContentSizes::zero());
657 let depends_on_block_constraints =
658 inline_content_sizes_result.depends_on_block_constraints;
659 data.depends_on_block_constraints |= depends_on_block_constraints;
660 data.clear_floats(clear);
661 match float {
662 Some(FloatSide::InlineStart) => data.floats.start.union_assign(&size),
663 Some(FloatSide::InlineEnd) => data.floats.end.union_assign(&size),
664 None => {
665 data.max_size
666 .max_assign(data.floats.start.union(&data.floats.end).union(&size));
667 data.floats = LogicalSides1D::default();
668 },
669 }
670 data
671 };
672
673 let job_counts = boxes
674 .iter()
675 .map(|block_level_box| block_level_box.borrow().subtree_size());
676 let data = if layout_context.should_parallelize_layout(job_counts) {
677 boxes
678 .par_iter()
679 .filter_map(get_box_info)
680 .collect::<Vec<_>>()
681 .into_iter()
682 .fold(AccumulatedData::default(), accumulate)
683 } else {
684 boxes
685 .iter()
686 .filter_map(get_box_info)
687 .fold(AccumulatedData::default(), accumulate)
688 };
689 InlineContentSizesResult {
690 depends_on_block_constraints: data.depends_on_block_constraints,
691 sizes: data.max_size_including_uncleared_floats(),
692 }
693}
694
695impl BlockContainer {
696 fn layout(
697 &self,
698 layout_context: &LayoutContext,
699 positioning_context: &mut PositioningContext,
700 containing_block: &ContainingBlock,
701 sequential_layout_state: Option<&mut SequentialLayoutState>,
702 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
703 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
704 ) -> IndependentFormattingContextLayoutResult {
705 match self {
706 BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children(
707 layout_context,
708 positioning_context,
709 child_boxes,
710 containing_block,
711 sequential_layout_state,
712 collapsible_with_parent_start_margin,
713 ignore_block_margins_for_stretch,
714 ),
715 BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
716 layout_context,
717 positioning_context,
718 containing_block,
719 sequential_layout_state,
720 collapsible_with_parent_start_margin,
721 ),
722 }
723 }
724
725 #[inline]
726 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
727 LayoutStyle::Default(&base.style)
728 }
729
730 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
731 match self {
732 Self::BlockLevelBoxes(child_boxes) => {
733 for child_box in child_boxes {
734 child_box.borrow_mut().with_base_mut(|base| {
735 base.parent_box.replace(layout_box.clone());
736 });
737 }
738 },
739 Self::InlineFormattingContext(ifc) => ifc.attached_to_tree(layout_box),
740 }
741 }
742
743 fn find_block_margin_collapsing_with_parent(
744 &self,
745 layout_context: &LayoutContext,
746 collected_margin: &mut CollapsedMargin,
747 containing_block_for_children: &ContainingBlock,
748 ) -> bool {
749 match self {
750 BlockContainer::BlockLevelBoxes(boxes) => boxes.iter().all(|block_level_box| {
751 block_level_box
752 .borrow()
753 .find_block_margin_collapsing_with_parent(
754 layout_context,
755 collected_margin,
756 containing_block_for_children,
757 )
758 }),
759 BlockContainer::InlineFormattingContext(context) => context
760 .find_block_margin_collapsing_with_parent(
761 layout_context,
762 collected_margin,
763 containing_block_for_children,
764 ),
765 }
766 }
767}
768
769impl ComputeInlineContentSizes for BlockContainer {
770 fn compute_inline_content_sizes(
771 &self,
772 layout_context: &LayoutContext,
773 constraint_space: &ConstraintSpace,
774 ) -> InlineContentSizesResult {
775 match &self {
776 Self::BlockLevelBoxes(boxes) => compute_inline_content_sizes_for_block_level_boxes(
777 boxes,
778 layout_context,
779 &constraint_space.into(),
780 ),
781 Self::InlineFormattingContext(context) => {
782 context.compute_inline_content_sizes(layout_context, constraint_space)
783 },
784 }
785 }
786}
787
788fn layout_block_level_children(
789 layout_context: &LayoutContext,
790 positioning_context: &mut PositioningContext,
791 child_boxes: &[ArcRefCell<BlockLevelBox>],
792 containing_block: &ContainingBlock,
793 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
794 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
795 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
796) -> IndependentFormattingContextLayoutResult {
797 let mut placement_state =
798 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
799
800 let fragments = match sequential_layout_state {
801 Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
802 layout_context,
803 positioning_context,
804 child_boxes,
805 sequential_layout_state,
806 &mut placement_state,
807 ignore_block_margins_for_stretch,
808 ),
809 None => layout_block_level_children_in_parallel(
810 layout_context,
811 positioning_context,
812 child_boxes,
813 &mut placement_state,
814 ignore_block_margins_for_stretch,
815 ),
816 };
817
818 let depends_on_block_constraints = fragments.iter().any(|fragment| {
819 fragment.base().is_some_and(|base| {
820 base.flags.contains(
821 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
822 )
823 })
824 });
825
826 let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
827 IndependentFormattingContextLayoutResult {
828 fragments,
829 content_block_size,
830 collapsible_margins_in_children,
831 baselines,
832 depends_on_block_constraints,
833 content_inline_size_for_table: None,
834 specific_layout_info: None,
835 }
836}
837
838fn layout_block_level_children_in_parallel(
839 layout_context: &LayoutContext,
840 positioning_context: &mut PositioningContext,
841 child_boxes: &[ArcRefCell<BlockLevelBox>],
842 placement_state: &mut PlacementState,
843 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
844) -> Vec<Fragment> {
845 let mut layout_results: Vec<(Fragment, PositioningContext)> =
846 Vec::with_capacity(child_boxes.len());
847
848 child_boxes
849 .par_iter()
850 .map(|child_box| {
851 let mut child_positioning_context = PositioningContext::default();
852 let fragment = child_box.borrow().layout(
853 layout_context,
854 &mut child_positioning_context,
855 placement_state.containing_block,
856 None,
857 None,
858 ignore_block_margins_for_stretch,
859 false, );
861 (fragment, child_positioning_context)
862 })
863 .collect_into_vec(&mut layout_results);
864
865 layout_results
866 .into_iter()
867 .map(|(mut fragment, mut child_positioning_context)| {
868 placement_state.place_fragment_and_update_baseline(&mut fragment, None);
869 child_positioning_context.adjust_static_position_of_hoisted_fragments(
870 &fragment,
871 PositioningContextLength::zero(),
872 );
873 positioning_context.append(child_positioning_context);
874 fragment
875 })
876 .collect()
877}
878
879fn layout_block_level_children_sequentially(
880 layout_context: &LayoutContext,
881 positioning_context: &mut PositioningContext,
882 child_boxes: &[ArcRefCell<BlockLevelBox>],
883 sequential_layout_state: &mut SequentialLayoutState,
884 placement_state: &mut PlacementState,
885 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
886) -> Vec<Fragment> {
887 child_boxes
891 .iter()
892 .map(|child_box| {
893 layout_block_level_child(
894 layout_context,
895 positioning_context,
896 &child_box.borrow(),
897 Some(sequential_layout_state),
898 placement_state,
899 ignore_block_margins_for_stretch,
900 false, )
902 })
903 .collect()
904}
905
906fn layout_block_level_child(
907 layout_context: &LayoutContext,
908 positioning_context: &mut PositioningContext,
909 child_box: &BlockLevelBox,
910 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
911 placement_state: &mut PlacementState,
912 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
913 has_inline_parent: bool,
914) -> Fragment {
915 let positioning_context_length_before_layout = positioning_context.len();
916 let mut fragment = child_box.layout(
917 layout_context,
918 positioning_context,
919 placement_state.containing_block,
920 sequential_layout_state.as_deref_mut(),
921 Some(CollapsibleWithParentStartMargin(
922 placement_state.next_in_flow_margin_collapses_with_parent_start_margin,
923 )),
924 ignore_block_margins_for_stretch,
925 has_inline_parent,
926 );
927
928 placement_state.place_fragment_and_update_baseline(&mut fragment, sequential_layout_state);
929 positioning_context.adjust_static_position_of_hoisted_fragments(
930 &fragment,
931 positioning_context_length_before_layout,
932 );
933
934 fragment
935}
936
937impl BlockLevelBox {
938 #[allow(clippy::too_many_arguments)]
939 fn layout(
940 &self,
941 layout_context: &LayoutContext,
942 positioning_context: &mut PositioningContext,
943 containing_block: &ContainingBlock,
944 sequential_layout_state: Option<&mut SequentialLayoutState>,
945 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
946 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
947 has_inline_parent: bool,
948 ) -> Fragment {
949 let fragment = match self {
950 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
951 Fragment::Box(
952 same_formatting_context_block.layout_in_flow_non_replaced_block_level_cached(
953 layout_context,
954 positioning_context,
955 containing_block,
956 sequential_layout_state,
957 collapsible_with_parent_start_margin,
958 ignore_block_margins_for_stretch,
959 has_inline_parent,
960 ),
961 )
962 },
963 BlockLevelBox::Independent(independent) => Fragment::Box(
964 positioning_context
965 .layout_maybe_position_relative_fragment(
966 layout_context,
967 containing_block,
968 &independent.base,
969 |positioning_context| {
970 independent.layout_in_flow_block_level(
971 layout_context,
972 positioning_context,
973 containing_block,
974 sequential_layout_state,
975 ignore_block_margins_for_stretch,
976 has_inline_parent,
977 )
978 },
979 )
980 .into(),
981 ),
982 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
983 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
987 box_.clone(),
988 PhysicalRect::zero(),
992 LogicalVec2 {
993 inline: AlignFlags::START,
994 block: AlignFlags::START,
995 },
996 containing_block.style.writing_mode,
997 );
998 let hoisted_fragment = hoisted_box.fragment.clone();
999 positioning_context.push(hoisted_box);
1000 Fragment::AbsoluteOrFixedPositionedPlaceholder(hoisted_fragment)
1001 },
1002 BlockLevelBox::OutOfFlowFloatBox(float_box) => Fragment::Float(
1003 float_box
1004 .layout(layout_context, positioning_context, containing_block)
1005 .into(),
1006 ),
1007 BlockLevelBox::OutsideMarker(outside_marker) => {
1008 outside_marker.layout(layout_context, containing_block, positioning_context)
1009 },
1010 };
1011
1012 self.with_base(|base| base.set_fragment(fragment.clone()));
1013
1014 fragment
1015 }
1016
1017 fn inline_content_sizes(
1018 &self,
1019 layout_context: &LayoutContext,
1020 constraint_space: &ConstraintSpace,
1021 ) -> InlineContentSizesResult {
1022 let independent_formatting_context = match self {
1023 BlockLevelBox::Independent(independent) => independent,
1024 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => &box_.borrow().context,
1025 BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents,
1026 BlockLevelBox::OutsideMarker(outside_marker) => &outside_marker.context,
1027 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
1028 return same_formatting_context_block
1029 .inline_content_sizes(layout_context, constraint_space);
1030 },
1031 };
1032 independent_formatting_context.inline_content_sizes(layout_context, constraint_space)
1033 }
1034}
1035
1036impl IndependentFormattingContext {
1037 pub(crate) fn layout_in_flow_block_level(
1045 &self,
1046 layout_context: &LayoutContext,
1047 positioning_context: &mut PositioningContext,
1048 containing_block: &ContainingBlock,
1049 sequential_layout_state: Option<&mut SequentialLayoutState>,
1050 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1051 has_inline_parent: bool,
1052 ) -> BoxFragment {
1053 if let Some(sequential_layout_state) = sequential_layout_state {
1054 return self.layout_in_flow_block_level_sequentially(
1055 layout_context,
1056 positioning_context,
1057 containing_block,
1058 sequential_layout_state,
1059 ignore_block_margins_for_stretch,
1060 has_inline_parent,
1061 );
1062 }
1063
1064 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1065 self.inline_content_sizes(layout_context, constraint_space)
1066 .sizes
1067 };
1068 let layout_style = self.layout_style();
1069 let ContainingBlockPaddingAndBorder {
1070 containing_block: containing_block_for_children,
1071 pbm,
1072 block_sizes,
1073 depends_on_block_constraints,
1074 available_block_size,
1075 justify_self,
1076 preferred_aspect_ratio,
1077 } = solve_containing_block_padding_and_border_for_in_flow_box(
1078 containing_block,
1079 &layout_style,
1080 get_inline_content_sizes,
1081 ignore_block_margins_for_stretch,
1082 Some(self),
1083 has_inline_parent,
1084 );
1085
1086 let lazy_block_size = LazySize::new(
1087 &block_sizes,
1088 Direction::Block,
1089 Size::FitContent,
1090 Au::zero,
1091 available_block_size,
1092 layout_style.is_table(),
1093 );
1094
1095 let layout = self.layout(
1096 layout_context,
1097 positioning_context,
1098 &containing_block_for_children,
1099 containing_block,
1100 preferred_aspect_ratio,
1101 &lazy_block_size,
1102 );
1103
1104 let inline_size = layout
1105 .content_inline_size_for_table
1106 .unwrap_or(containing_block_for_children.size.inline);
1107 let block_size = lazy_block_size.resolve(|| layout.content_block_size);
1108
1109 let ResolvedMargins {
1110 margin,
1111 effective_margin_inline_start,
1112 } = solve_margins(containing_block, &pbm, inline_size, justify_self);
1113
1114 let content_rect = LogicalRect {
1115 start_corner: LogicalVec2 {
1116 block: pbm.padding.block_start + pbm.border.block_start,
1117 inline: pbm.padding.inline_start +
1118 pbm.border.inline_start +
1119 effective_margin_inline_start,
1120 },
1121 size: LogicalVec2 {
1122 block: block_size,
1123 inline: inline_size,
1124 },
1125 };
1126
1127 let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1128 let containing_block_writing_mode = containing_block.style.writing_mode;
1129
1130 let mut base_fragment_info = self.base.base_fragment_info;
1131 if depends_on_block_constraints {
1132 base_fragment_info.flags.insert(
1133 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1134 );
1135 }
1136 BoxFragment::new(
1137 base_fragment_info,
1138 self.base.style.clone(),
1139 layout.fragments,
1140 content_rect.as_physical(Some(containing_block)),
1141 pbm.padding.to_physical(containing_block_writing_mode),
1142 pbm.border.to_physical(containing_block_writing_mode),
1143 margin.to_physical(containing_block_writing_mode),
1144 layout.specific_layout_info,
1145 )
1146 .with_baselines(layout.baselines)
1147 .with_block_level_layout_info(block_margins_collapsed_with_children, None)
1148 }
1149
1150 fn layout_in_flow_block_level_sequentially(
1154 &self,
1155 layout_context: &LayoutContext<'_>,
1156 positioning_context: &mut PositioningContext,
1157 containing_block: &ContainingBlock<'_>,
1158 sequential_layout_state: &mut SequentialLayoutState,
1159 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1160 has_inline_parent: bool,
1161 ) -> BoxFragment {
1162 let style = &self.base.style;
1163 let containing_block_writing_mode = containing_block.style.writing_mode;
1164 let ContentBoxSizesAndPBM {
1165 content_box_sizes,
1166 pbm,
1167 depends_on_block_constraints,
1168 ..
1169 } = self
1170 .layout_style()
1171 .content_box_sizes_and_padding_border_margin(&containing_block.into());
1172
1173 let (margin_block_start, margin_block_end) =
1174 solve_block_margins_for_in_flow_block_level(&pbm);
1175 let collapsed_margin_block_start = CollapsedMargin::new(margin_block_start);
1176
1177 let mut content_size;
1188 let mut layout;
1189 let mut placement_rect;
1190
1191 let clear_position = sequential_layout_state.calculate_clear_position(
1195 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode),
1196 &collapsed_margin_block_start,
1197 );
1198 let ceiling = clear_position.unwrap_or_else(|| {
1199 sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
1200 });
1201
1202 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1204 let available_block_size = containing_block
1205 .size
1206 .block
1207 .to_definite()
1208 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1209 let is_table = self.is_table();
1210 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
1211
1212 #[derive(Default)]
1213 struct Cache {
1214 min_block_size: Au,
1215 max_block_size: Option<Au>,
1216 tentative_block_size: SizeConstraint,
1217 depends_on_stretch_size: bool,
1218 }
1219 let mut cache = Cache {
1220 depends_on_stretch_size: true,
1221 ..Default::default()
1222 };
1223
1224 let update_cache = |cache: &mut Cache, stretch_size| {
1225 let tentative_block_content_size = self
1226 .tentative_block_content_size_with_dependency(preferred_aspect_ratio, stretch_size);
1227 let (preferred_block_size, min_block_size, max_block_size, depends_on_stretch_size) =
1228 if let Some(result) = tentative_block_content_size {
1229 let (block_content_size, depends_on_stretch_size) = result;
1230 let (preferred, min, max) = content_box_sizes.block.resolve_each(
1231 Size::FitContent,
1232 Au::zero,
1233 available_block_size,
1234 || block_content_size,
1235 is_table,
1236 );
1237 (Some(preferred), min, max, depends_on_stretch_size)
1238 } else {
1239 let (preferred, min, max) = content_box_sizes.block.resolve_each_extrinsic(
1240 Size::FitContent,
1241 Au::zero(),
1242 available_block_size,
1243 );
1244 (preferred, min, max, false)
1245 };
1246 cache.min_block_size = min_block_size;
1247 cache.max_block_size = max_block_size;
1248 cache.tentative_block_size =
1249 SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
1250 cache.depends_on_stretch_size = depends_on_stretch_size;
1251 };
1252
1253 let get_inline_content_sizes = |cache: &Cache| {
1255 let constraint_space =
1256 ConstraintSpace::new(cache.tentative_block_size, style, preferred_aspect_ratio);
1257 self.inline_content_sizes(layout_context, &constraint_space)
1258 .sizes
1259 };
1260
1261 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1262 let automatic_inline_size = automatic_inline_size(justify_self, Some(self));
1263 let compute_inline_size = |cache: &mut Cache, stretch_size| {
1264 if cache.depends_on_stretch_size {
1265 update_cache(cache, stretch_size);
1266 }
1267 content_box_sizes.inline.resolve(
1268 Direction::Inline,
1269 automatic_inline_size,
1270 Au::zero,
1271 Some(stretch_size),
1272 || get_inline_content_sizes(cache),
1273 is_table,
1274 )
1275 };
1276
1277 let get_lazy_block_size = || {
1278 LazySize::new(
1279 &content_box_sizes.block,
1280 Direction::Block,
1281 Size::FitContent,
1282 Au::zero,
1283 available_block_size,
1284 is_table,
1285 )
1286 };
1287
1288 let inline_size_with_max_available_space = compute_inline_size(&mut cache, MAX_AU);
1297 let inline_size_with_no_available_space = compute_inline_size(&mut cache, Au::zero());
1298 if inline_size_with_no_available_space == inline_size_with_max_available_space {
1299 let inline_size = inline_size_with_no_available_space;
1303 let lazy_block_size = get_lazy_block_size();
1304 layout = self.layout(
1305 layout_context,
1306 positioning_context,
1307 &ContainingBlock {
1308 size: ContainingBlockSize {
1309 inline: inline_size,
1310 block: cache.tentative_block_size,
1314 },
1315 style,
1316 },
1317 containing_block,
1318 preferred_aspect_ratio,
1319 &lazy_block_size,
1320 );
1321
1322 content_size = LogicalVec2 {
1323 block: lazy_block_size.resolve(|| layout.content_block_size),
1324 inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
1325 };
1326
1327 let mut placement = PlacementAmongFloats::new(
1328 &sequential_layout_state.floats,
1329 ceiling,
1330 content_size + pbm.padding_border_sums,
1331 &pbm,
1332 );
1333 placement_rect = placement.place();
1334 } else {
1335 let minimum_size_of_block = LogicalVec2 {
1341 inline: inline_size_with_no_available_space,
1345 block: match cache.tentative_block_size {
1353 SizeConstraint::Definite(size) if cache.max_block_size.is_some() => size,
1356 _ => cache.min_block_size,
1359 },
1360 } + pbm.padding_border_sums;
1361 let mut placement = PlacementAmongFloats::new(
1362 &sequential_layout_state.floats,
1363 ceiling,
1364 minimum_size_of_block,
1365 &pbm,
1366 );
1367
1368 loop {
1369 placement_rect = placement.place();
1371 let available_inline_size =
1372 placement_rect.size.inline - pbm.padding_border_sums.inline;
1373 let proposed_inline_size = compute_inline_size(&mut cache, available_inline_size);
1374
1375 let positioning_context_length = positioning_context.len();
1379 let lazy_block_size = get_lazy_block_size();
1380 layout = self.layout(
1381 layout_context,
1382 positioning_context,
1383 &ContainingBlock {
1384 size: ContainingBlockSize {
1385 inline: proposed_inline_size,
1386 block: cache.tentative_block_size,
1387 },
1388 style,
1389 },
1390 containing_block,
1391 preferred_aspect_ratio,
1392 &lazy_block_size,
1393 );
1394
1395 let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
1396 debug_assert!(inline_size < proposed_inline_size);
1401 inline_size
1402 } else {
1403 proposed_inline_size
1404 };
1405 content_size = LogicalVec2 {
1406 block: lazy_block_size.resolve(|| layout.content_block_size),
1407 inline: inline_size,
1408 };
1409
1410 if placement.try_to_expand_for_auto_block_size(
1414 content_size.block + pbm.padding_border_sums.block,
1415 &placement_rect.size,
1416 ) {
1417 break;
1418 }
1419
1420 positioning_context.truncate(&positioning_context_length);
1424 }
1425 }
1426
1427 let has_clearance = clear_position.is_some() || placement_rect.start_corner.block > ceiling;
1431 let clearance = has_clearance.then(|| {
1432 placement_rect.start_corner.block -
1433 sequential_layout_state
1434 .position_with_zero_clearance(&collapsed_margin_block_start)
1435 });
1436
1437 let ((margin_inline_start, margin_inline_end), effective_margin_inline_start) =
1438 solve_inline_margins_avoiding_floats(
1439 sequential_layout_state,
1440 containing_block,
1441 &pbm,
1442 content_size.inline + pbm.padding_border_sums.inline,
1443 placement_rect,
1444 justify_self,
1445 );
1446
1447 let margin = LogicalSides {
1448 inline_start: margin_inline_start,
1449 inline_end: margin_inline_end,
1450 block_start: margin_block_start,
1451 block_end: margin_block_end,
1452 };
1453
1454 if clearance.is_some() {
1457 sequential_layout_state.commit_margin();
1458 }
1459 sequential_layout_state.adjoin_assign(&collapsed_margin_block_start);
1460
1461 sequential_layout_state.commit_margin();
1463 sequential_layout_state.advance_block_position(
1464 pbm.padding_border_sums.block + content_size.block + clearance.unwrap_or_else(Au::zero),
1465 );
1466 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1467
1468 let content_rect = LogicalRect {
1469 start_corner: LogicalVec2 {
1470 block: pbm.padding.block_start +
1471 pbm.border.block_start +
1472 clearance.unwrap_or_else(Au::zero),
1473 inline: pbm.padding.inline_start +
1474 pbm.border.inline_start +
1475 effective_margin_inline_start,
1476 },
1477 size: content_size,
1478 };
1479
1480 let mut base_fragment_info = self.base.base_fragment_info;
1481 if depends_on_block_constraints {
1482 base_fragment_info.flags.insert(
1483 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1484 );
1485 }
1486
1487 BoxFragment::new(
1488 base_fragment_info,
1489 style.clone(),
1490 layout.fragments,
1491 content_rect.as_physical(Some(containing_block)),
1492 pbm.padding.to_physical(containing_block_writing_mode),
1493 pbm.border.to_physical(containing_block_writing_mode),
1494 margin.to_physical(containing_block_writing_mode),
1495 layout.specific_layout_info,
1496 )
1497 .with_baselines(layout.baselines)
1498 .with_block_level_layout_info(CollapsedBlockMargins::from_margin(&margin), clearance)
1499 }
1500}
1501
1502struct ContainingBlockPaddingAndBorder<'a> {
1503 containing_block: ContainingBlock<'a>,
1504 pbm: PaddingBorderMargin,
1505 block_sizes: Sizes,
1506 depends_on_block_constraints: bool,
1507 available_block_size: Option<Au>,
1508 justify_self: AlignFlags,
1509 preferred_aspect_ratio: Option<AspectRatio>,
1510}
1511
1512struct ResolvedMargins {
1513 pub margin: LogicalSides<Au>,
1515
1516 pub effective_margin_inline_start: Au,
1523}
1524
1525fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
1531 containing_block: &ContainingBlock<'_>,
1532 layout_style: &'a LayoutStyle,
1533 get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
1534 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1535 context: Option<&IndependentFormattingContext>,
1536 has_inline_parent: bool,
1537) -> ContainingBlockPaddingAndBorder<'a> {
1538 let style = layout_style.style();
1539 if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
1540 let containing_block_for_children = ContainingBlock {
1544 size: ContainingBlockSize {
1545 inline: containing_block.size.inline,
1546 block: containing_block.size.block,
1547 },
1548 style,
1549 };
1550 return ContainingBlockPaddingAndBorder {
1553 containing_block: containing_block_for_children,
1554 pbm: PaddingBorderMargin::zero(),
1555 block_sizes: Sizes::default(),
1556 depends_on_block_constraints: false,
1557 available_block_size: None,
1560 justify_self: AlignFlags::NORMAL,
1563 preferred_aspect_ratio: None,
1564 };
1565 }
1566
1567 let ContentBoxSizesAndPBM {
1568 content_box_sizes,
1569 pbm,
1570 depends_on_block_constraints,
1571 ..
1572 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
1573
1574 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1575 let available_inline_size = Au::zero().max(containing_block.size.inline - pbm_sums.inline);
1576 let available_block_size = containing_block
1577 .size
1578 .block
1579 .to_definite()
1580 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1581
1582 let preferred_aspect_ratio =
1585 context.and_then(|context| context.preferred_aspect_ratio(&pbm.padding_border_sums));
1586 let is_table = layout_style.is_table();
1587
1588 let tentative_block_content_size = context.and_then(|context| {
1591 context.tentative_block_content_size(preferred_aspect_ratio, available_inline_size)
1592 });
1593 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
1594 SizeConstraint::Definite(content_box_sizes.block.resolve(
1595 Direction::Block,
1596 Size::FitContent,
1597 Au::zero,
1598 available_block_size,
1599 || block_content_size,
1600 is_table,
1601 ))
1602 } else {
1603 content_box_sizes.block.resolve_extrinsic(
1604 Size::FitContent,
1605 Au::zero(),
1606 available_block_size,
1607 )
1608 };
1609
1610 let get_inline_content_sizes = || {
1613 get_inline_content_sizes(&ConstraintSpace::new(
1614 tentative_block_size,
1615 style,
1616 preferred_aspect_ratio,
1617 ))
1618 };
1619 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1620 let inline_size = content_box_sizes.inline.resolve(
1621 Direction::Inline,
1622 automatic_inline_size(justify_self, context),
1623 Au::zero,
1624 Some(available_inline_size),
1625 get_inline_content_sizes,
1626 is_table,
1627 );
1628
1629 let containing_block_for_children = ContainingBlock {
1630 size: ContainingBlockSize {
1631 inline: inline_size,
1632 block: tentative_block_size,
1633 },
1634 style,
1635 };
1636 assert_eq!(
1638 containing_block.style.writing_mode.is_horizontal(),
1639 containing_block_for_children
1640 .style
1641 .writing_mode
1642 .is_horizontal(),
1643 "Vertical writing modes are not supported yet"
1644 );
1645 ContainingBlockPaddingAndBorder {
1646 containing_block: containing_block_for_children,
1647 pbm,
1648 block_sizes: content_box_sizes.block,
1649 depends_on_block_constraints,
1650 available_block_size,
1651 justify_self,
1652 preferred_aspect_ratio,
1653 }
1654}
1655
1656fn solve_margins(
1661 containing_block: &ContainingBlock<'_>,
1662 pbm: &PaddingBorderMargin,
1663 inline_size: Au,
1664 justify_self: AlignFlags,
1665) -> ResolvedMargins {
1666 let (inline_margins, effective_margin_inline_start) =
1667 solve_inline_margins_for_in_flow_block_level(
1668 containing_block,
1669 pbm,
1670 inline_size,
1671 justify_self,
1672 );
1673 let block_margins = solve_block_margins_for_in_flow_block_level(pbm);
1674 ResolvedMargins {
1675 margin: LogicalSides {
1676 inline_start: inline_margins.0,
1677 inline_end: inline_margins.1,
1678 block_start: block_margins.0,
1679 block_end: block_margins.1,
1680 },
1681 effective_margin_inline_start,
1682 }
1683}
1684
1685fn solve_block_margins_for_in_flow_block_level(pbm: &PaddingBorderMargin) -> (Au, Au) {
1689 (
1690 pbm.margin.block_start.auto_is(Au::zero),
1691 pbm.margin.block_end.auto_is(Au::zero),
1692 )
1693}
1694
1695fn resolve_justify_self(
1697 style: &ComputedValues,
1698 containing_block_style: &ComputedValues,
1699 has_inline_parent: bool,
1700) -> AlignFlags {
1701 let alignment = match style.clone_justify_self().0 {
1707 AlignFlags::AUTO if has_inline_parent => AlignFlags::NORMAL,
1708 AlignFlags::AUTO => containing_block_style.clone_justify_items().computed.0.0,
1709 alignment => alignment,
1710 };
1711 let is_ltr = |style: &ComputedValues| style.writing_mode.line_left_is_inline_start();
1712 let alignment_value = match alignment.value() {
1713 AlignFlags::LEFT if is_ltr(containing_block_style) => AlignFlags::START,
1714 AlignFlags::LEFT => AlignFlags::END,
1715 AlignFlags::RIGHT if is_ltr(containing_block_style) => AlignFlags::END,
1716 AlignFlags::RIGHT => AlignFlags::START,
1717 AlignFlags::SELF_START if is_ltr(containing_block_style) == is_ltr(style) => {
1718 AlignFlags::START
1719 },
1720 AlignFlags::SELF_START => AlignFlags::END,
1721 AlignFlags::SELF_END if is_ltr(containing_block_style) == is_ltr(style) => AlignFlags::END,
1722 AlignFlags::SELF_END => AlignFlags::START,
1723 alignment_value => alignment_value,
1724 };
1725 alignment.flags() | alignment_value
1726}
1727
1728#[inline]
1731fn automatic_inline_size<T>(
1732 justify_self: AlignFlags,
1733 context: Option<&IndependentFormattingContext>,
1734) -> Size<T> {
1735 let normal_stretches = || {
1736 !context.is_some_and(|context| {
1737 context
1738 .base
1739 .base_fragment_info
1740 .flags
1741 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET) ||
1742 context.is_table()
1743 })
1744 };
1745 match justify_self {
1746 AlignFlags::STRETCH => Size::Stretch,
1747 AlignFlags::NORMAL if normal_stretches() => Size::Stretch,
1748 _ => Size::FitContent,
1749 }
1750}
1751
1752fn justify_self_alignment(
1759 containing_block: &ContainingBlock,
1760 free_space: Au,
1761 justify_self: AlignFlags,
1762) -> Au {
1763 let mut alignment = justify_self.value();
1764 let is_safe = justify_self.flags() == AlignFlags::SAFE || alignment == AlignFlags::NORMAL;
1765 if is_safe && free_space <= Au::zero() {
1766 alignment = AlignFlags::START
1767 }
1768 match alignment {
1769 AlignFlags::NORMAL => {},
1770 AlignFlags::CENTER => return free_space / 2,
1771 AlignFlags::END => return free_space,
1772 _ => return Au::zero(),
1773 }
1774
1775 let style = containing_block.style;
1777 match style.clone_text_align() {
1778 TextAlignKeyword::MozCenter => free_space / 2,
1779 TextAlignKeyword::MozLeft if !style.writing_mode.line_left_is_inline_start() => free_space,
1780 TextAlignKeyword::MozRight if style.writing_mode.line_left_is_inline_start() => free_space,
1781 _ => Au::zero(),
1782 }
1783}
1784
1785fn solve_inline_margins_for_in_flow_block_level(
1798 containing_block: &ContainingBlock,
1799 pbm: &PaddingBorderMargin,
1800 inline_size: Au,
1801 justify_self: AlignFlags,
1802) -> ((Au, Au), Au) {
1803 let free_space = containing_block.size.inline - pbm.padding_border_sums.inline - inline_size;
1804 let mut justification = Au::zero();
1805 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
1806 (AuOrAuto::Auto, AuOrAuto::Auto) => {
1807 let start = Au::zero().max(free_space / 2);
1808 (start, free_space - start)
1809 },
1810 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
1811 (Au::zero().max(free_space - end), end)
1812 },
1813 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, free_space - start),
1814 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
1815 justification =
1820 justify_self_alignment(containing_block, free_space - start - end, justify_self);
1821 (start, end)
1822 },
1823 };
1824 let effective_margin_inline_start = inline_margins.0 + justification;
1825 (inline_margins, effective_margin_inline_start)
1826}
1827
1828fn solve_inline_margins_avoiding_floats(
1838 sequential_layout_state: &SequentialLayoutState,
1839 containing_block: &ContainingBlock,
1840 pbm: &PaddingBorderMargin,
1841 inline_size: Au,
1842 placement_rect: LogicalRect<Au>,
1843 justify_self: AlignFlags,
1844) -> ((Au, Au), Au) {
1845 let free_space = Au::zero().max(placement_rect.size.inline - inline_size);
1849 let cb_info = &sequential_layout_state.floats.containing_block_info;
1850 let start_adjustment = placement_rect.start_corner.inline - cb_info.inline_start;
1851 let end_adjustment = cb_info.inline_end - placement_rect.max_inline_position();
1852 let mut justification = Au::zero();
1853 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
1854 (AuOrAuto::Auto, AuOrAuto::Auto) => {
1855 let half = free_space / 2;
1856 (start_adjustment + half, end_adjustment + free_space - half)
1857 },
1858 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (start_adjustment + free_space, end),
1859 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, end_adjustment + free_space),
1860 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
1861 justification = justify_self_alignment(containing_block, free_space, justify_self);
1867 (start, end)
1868 },
1869 };
1870 let effective_margin_inline_start = inline_margins.0.max(start_adjustment) + justification;
1871 (inline_margins, effective_margin_inline_start)
1872}
1873
1874struct PlacementState<'container> {
1879 next_in_flow_margin_collapses_with_parent_start_margin: bool,
1880 last_in_flow_margin_collapses_with_parent_end_margin: bool,
1881 start_margin: CollapsedMargin,
1882 current_margin: CollapsedMargin,
1883 current_block_direction_position: Au,
1884 inflow_baselines: Baselines,
1885 is_inline_block_context: bool,
1886
1887 marker_block_size: Option<Au>,
1892
1893 containing_block: &'container ContainingBlock<'container>,
1896}
1897
1898impl<'container> PlacementState<'container> {
1899 fn new(
1900 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1901 containing_block: &'container ContainingBlock<'container>,
1902 ) -> PlacementState<'container> {
1903 let is_inline_block_context =
1904 containing_block.style.get_box().clone_display() == Display::InlineBlock;
1905 PlacementState {
1906 next_in_flow_margin_collapses_with_parent_start_margin:
1907 collapsible_with_parent_start_margin.0,
1908 last_in_flow_margin_collapses_with_parent_end_margin: true,
1909 start_margin: CollapsedMargin::zero(),
1910 current_margin: CollapsedMargin::zero(),
1911 current_block_direction_position: Au::zero(),
1912 inflow_baselines: Baselines::default(),
1913 is_inline_block_context,
1914 marker_block_size: None,
1915 containing_block,
1916 }
1917 }
1918
1919 fn place_fragment_and_update_baseline(
1920 &mut self,
1921 fragment: &mut Fragment,
1922 sequential_layout_state: Option<&mut SequentialLayoutState>,
1923 ) {
1924 self.place_fragment(fragment, sequential_layout_state);
1925
1926 let box_fragment = match fragment {
1927 Fragment::LayoutRoot(..) | Fragment::Box(..) => fragment
1928 .retrieve_box_fragment()
1929 .expect("Should be guaranteed by surrounding check"),
1930 _ => return,
1931 };
1932
1933 if self.is_inline_block_context && box_fragment.is_table_wrapper() {
1938 return;
1939 }
1940
1941 let box_block_offset = box_fragment
1942 .content_rect()
1943 .origin
1944 .to_logical(self.containing_block)
1945 .block;
1946 let box_fragment_baselines =
1947 box_fragment.baselines(self.containing_block.style.writing_mode);
1948 if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) {
1949 self.inflow_baselines.first = Some(first + box_block_offset);
1950 }
1951 if let Some(last) = box_fragment_baselines.last {
1952 self.inflow_baselines.last = Some(last + box_block_offset);
1953 }
1954 }
1955
1956 fn place_fragment(
1959 &mut self,
1960 fragment: &mut Fragment,
1961 sequential_layout_state: Option<&mut SequentialLayoutState>,
1962 ) {
1963 match fragment {
1964 Fragment::LayoutRoot(..) | Fragment::Box(..) => {
1965 let fragment = fragment
1966 .retrieve_box_fragment()
1967 .expect("Should be guaranteed by surrounding condition");
1968
1969 let is_outside_marker = fragment
1978 .base
1979 .flags
1980 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER);
1981 if is_outside_marker {
1982 assert!(self.marker_block_size.is_none());
1983 self.marker_block_size = Some(
1984 fragment
1985 .content_rect()
1986 .size
1987 .to_logical(self.containing_block.style.writing_mode)
1988 .block,
1989 );
1990 return;
1991 }
1992
1993 let BlockLevelLayoutInfo {
1994 clearance,
1995 block_margins_collapsed_with_children: fragment_block_margins,
1996 } = *fragment
1997 .block_level_layout_info
1998 .clone()
1999 .expect("A block-level fragment should have a BlockLevelLayoutInfo.");
2000 let mut fragment_block_size = fragment
2001 .border_rect()
2002 .size
2003 .to_logical(self.containing_block.style.writing_mode)
2004 .block;
2005
2006 if let Some(clearance) = clearance {
2012 fragment_block_size += clearance;
2013 self.current_block_direction_position += self.current_margin.solve();
2018 self.current_margin = CollapsedMargin::zero();
2019 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2020 if fragment_block_margins.collapsed_through {
2021 self.last_in_flow_margin_collapses_with_parent_end_margin = false;
2022 }
2023 } else if !fragment_block_margins.collapsed_through {
2024 self.last_in_flow_margin_collapses_with_parent_end_margin = true;
2025 }
2026
2027 if self.next_in_flow_margin_collapses_with_parent_start_margin {
2028 debug_assert!(self.current_margin.solve().is_zero());
2029 self.start_margin
2030 .adjoin_assign(&fragment_block_margins.start);
2031 if fragment_block_margins.collapsed_through {
2032 self.start_margin.adjoin_assign(&fragment_block_margins.end);
2033 return;
2034 }
2035 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2036 } else {
2037 self.current_margin
2038 .adjoin_assign(&fragment_block_margins.start);
2039 }
2040
2041 fragment.base.translate_rect(
2042 LogicalVec2 {
2043 inline: Au::zero(),
2044 block: self.current_margin.solve() + self.current_block_direction_position,
2045 }
2046 .to_physical_size(self.containing_block.style.writing_mode),
2047 );
2048
2049 if fragment_block_margins.collapsed_through {
2050 self.current_block_direction_position += fragment_block_size;
2053 self.current_margin
2054 .adjoin_assign(&fragment_block_margins.end);
2055 } else {
2056 self.current_block_direction_position +=
2057 self.current_margin.solve() + fragment_block_size;
2058 self.current_margin = fragment_block_margins.end;
2059 }
2060 },
2061 Fragment::AbsoluteOrFixedPositionedPlaceholder(fragment) => {
2062 fragment.borrow_mut().original_static_position_rect = LogicalRect {
2065 start_corner: LogicalVec2 {
2066 block: (self.current_margin.solve() +
2067 self.current_block_direction_position),
2068 inline: Au::zero(),
2069 },
2070 size: LogicalVec2::zero(),
2071 }
2072 .as_physical(Some(self.containing_block));
2073 },
2074 Fragment::Float(box_fragment) => {
2075 let sequential_layout_state = sequential_layout_state
2076 .expect("Found float fragment without SequentialLayoutState");
2077 let block_offset_from_containing_block_top =
2078 self.current_block_direction_position + self.current_margin.solve();
2079 sequential_layout_state.place_float_fragment(
2080 box_fragment,
2081 self.containing_block,
2082 self.start_margin,
2083 block_offset_from_containing_block_top,
2084 );
2085 },
2086 Fragment::Positioning(_) => {},
2087 _ => unreachable!("Unexpected Fragment type encountered during flow layout"),
2088 }
2089 }
2090
2091 fn finish(mut self) -> (Au, CollapsedBlockMargins, Baselines) {
2092 if !self.last_in_flow_margin_collapses_with_parent_end_margin {
2093 self.current_block_direction_position += self.current_margin.solve();
2094 self.current_margin = CollapsedMargin::zero();
2095 }
2096 let (total_block_size, collapsed_through) = match self.marker_block_size {
2097 Some(marker_block_size) => (
2098 self.current_block_direction_position.max(marker_block_size),
2099 false,
2102 ),
2103 None => (
2104 self.current_block_direction_position,
2105 self.next_in_flow_margin_collapses_with_parent_start_margin,
2106 ),
2107 };
2108
2109 (
2110 total_block_size,
2111 CollapsedBlockMargins {
2112 collapsed_through,
2113 start: self.start_margin,
2114 end: self.current_margin,
2115 },
2116 self.inflow_baselines,
2117 )
2118 }
2119}
2120
2121pub(crate) struct IndependentFloatOrAtomicLayoutResult {
2122 pub fragment: BoxFragment,
2123 pub baselines: Baselines,
2124 pub pbm_sums: LogicalSides<Au>,
2125}
2126
2127impl IndependentFormattingContext {
2128 pub(crate) fn layout_float_or_atomic_inline(
2129 &self,
2130 layout_context: &LayoutContext,
2131 child_positioning_context: &mut PositioningContext,
2132 containing_block: &ContainingBlock,
2133 ) -> IndependentFloatOrAtomicLayoutResult {
2134 let style = self.style();
2135 let container_writing_mode = containing_block.style.writing_mode;
2136 let layout_style = self.layout_style();
2137 let content_box_sizes_and_pbm =
2138 layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
2139 let pbm = &content_box_sizes_and_pbm.pbm;
2140 let margin = pbm.margin.auto_is(Au::zero);
2141 let pbm_sums = pbm.padding + pbm.border + margin;
2142 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
2143 let is_table = self.is_table();
2144
2145 let available_inline_size =
2146 Au::zero().max(containing_block.size.inline - pbm_sums.inline_sum());
2147 let available_block_size = containing_block
2148 .size
2149 .block
2150 .to_definite()
2151 .map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum()));
2152
2153 let tentative_block_content_size =
2154 self.tentative_block_content_size(preferred_aspect_ratio, available_inline_size);
2155 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
2156 SizeConstraint::Definite(content_box_sizes_and_pbm.content_box_sizes.block.resolve(
2157 Direction::Block,
2158 Size::FitContent,
2159 Au::zero,
2160 available_block_size,
2161 || block_content_size,
2162 is_table,
2163 ))
2164 } else {
2165 content_box_sizes_and_pbm
2166 .content_box_sizes
2167 .block
2168 .resolve_extrinsic(Size::FitContent, Au::zero(), available_block_size)
2169 };
2170
2171 let get_content_size = || {
2172 let constraint_space =
2173 ConstraintSpace::new(tentative_block_size, style, preferred_aspect_ratio);
2174 self.inline_content_sizes(layout_context, &constraint_space)
2175 .sizes
2176 };
2177
2178 let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
2179 Direction::Inline,
2180 Size::FitContent,
2181 Au::zero,
2182 Some(available_inline_size),
2183 get_content_size,
2184 is_table,
2185 );
2186
2187 let containing_block_for_children = ContainingBlock {
2188 size: ContainingBlockSize {
2189 inline: inline_size,
2190 block: tentative_block_size,
2191 },
2192 style,
2193 };
2194 assert_eq!(
2195 container_writing_mode.is_horizontal(),
2196 style.writing_mode.is_horizontal(),
2197 "Mixed horizontal and vertical writing modes are not supported yet"
2198 );
2199
2200 let lazy_block_size = LazySize::new(
2201 &content_box_sizes_and_pbm.content_box_sizes.block,
2202 Direction::Block,
2203 Size::FitContent,
2204 Au::zero,
2205 available_block_size,
2206 is_table,
2207 );
2208
2209 let IndependentFormattingContextLayoutResult {
2210 content_inline_size_for_table,
2211 content_block_size,
2212 fragments,
2213 baselines,
2214 specific_layout_info,
2215 ..
2216 } = self.layout(
2217 layout_context,
2218 child_positioning_context,
2219 &containing_block_for_children,
2220 containing_block,
2221 preferred_aspect_ratio,
2222 &lazy_block_size,
2223 );
2224
2225 let content_size = LogicalVec2 {
2226 inline: content_inline_size_for_table.unwrap_or(inline_size),
2227 block: lazy_block_size.resolve(|| content_block_size),
2228 }
2229 .to_physical_size(container_writing_mode);
2230 let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size);
2231
2232 let mut base_fragment_info = self.base_fragment_info();
2233 if content_box_sizes_and_pbm.depends_on_block_constraints {
2234 base_fragment_info.flags.insert(
2235 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2236 );
2237 }
2238
2239 let fragment = BoxFragment::new(
2243 base_fragment_info,
2244 style.clone(),
2245 fragments,
2246 content_rect,
2247 pbm.padding.to_physical(container_writing_mode),
2248 pbm.border.to_physical(container_writing_mode),
2249 margin.to_physical(container_writing_mode),
2250 specific_layout_info,
2251 );
2252
2253 IndependentFloatOrAtomicLayoutResult {
2254 fragment,
2255 baselines,
2256 pbm_sums,
2257 }
2258 }
2259}