1#![allow(rustdoc::private_intra_doc_links)]
5
6use app_units::{Au, MAX_AU};
9use inline::InlineFormattingContext;
10use layout_api::wrapper_traits::ThreadSafeLayoutNode;
11use malloc_size_of_derive::MallocSizeOf;
12use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
13use script::layout_dom::ServoThreadSafeLayoutNode;
14use servo_arc::Arc;
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::{
28 Clear, ContainingBlockPositionInfo, FloatBox, FloatSide, PlacementAmongFloats,
29 SequentialLayoutState,
30};
31use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
32use crate::fragment_tree::{
33 BaseFragmentInfo, BlockLevelLayoutInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin,
34 Fragment, FragmentFlags,
35};
36use crate::geom::{
37 AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
38 PhysicalSides, ToLogical, ToLogicalWithContainingBlock,
39};
40use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
41use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
42use crate::sizing::{
43 self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize, Size,
44 SizeConstraint, Sizes,
45};
46use crate::style_ext::{AspectRatio, ContentBoxSizesAndPBM, LayoutStyle, PaddingBorderMargin};
47use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
48
49mod construct;
50pub mod float;
51pub mod inline;
52mod root;
53
54pub(crate) use construct::{BlockContainerBuilder, BlockLevelCreator};
55pub(crate) use root::BoxTree;
56
57#[derive(Debug, MallocSizeOf)]
58pub(crate) struct BlockFormattingContext {
59 pub contents: BlockContainer,
60 pub contains_floats: bool,
61}
62
63#[derive(Debug, MallocSizeOf)]
64pub(crate) enum BlockContainer {
65 BlockLevelBoxes(Vec<ArcRefCell<BlockLevelBox>>),
66 InlineFormattingContext(InlineFormattingContext),
67}
68
69impl BlockContainer {
70 fn contains_floats(&self) -> bool {
71 match self {
72 BlockContainer::BlockLevelBoxes(boxes) => boxes
73 .iter()
74 .any(|block_level_box| block_level_box.borrow().contains_floats()),
75 BlockContainer::InlineFormattingContext(context) => context.contains_floats,
76 }
77 }
78
79 pub(crate) fn repair_style(
80 &mut self,
81 context: &SharedStyleContext,
82 node: &ServoThreadSafeLayoutNode,
83 new_style: &Arc<ComputedValues>,
84 ) {
85 match self {
86 BlockContainer::BlockLevelBoxes(..) => {},
87 BlockContainer::InlineFormattingContext(inline_formatting_context) => {
88 inline_formatting_context.repair_style(context, node, new_style)
89 },
90 }
91 }
92}
93
94#[derive(Debug, MallocSizeOf)]
95pub(crate) enum BlockLevelBox {
96 Independent(IndependentFormattingContext),
97 OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
98 OutOfFlowFloatBox(FloatBox),
99 OutsideMarker(OutsideMarker),
100 SameFormattingContextBlock {
101 base: LayoutBoxBase,
102 contents: BlockContainer,
103 contains_floats: bool,
104 },
105}
106
107impl BlockLevelBox {
108 pub(crate) fn repair_style(
109 &mut self,
110 context: &SharedStyleContext,
111 node: &ServoThreadSafeLayoutNode,
112 new_style: &Arc<ComputedValues>,
113 ) {
114 match self {
115 BlockLevelBox::Independent(independent_formatting_context) => {
116 independent_formatting_context.repair_style(context, node, new_style)
117 },
118 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
119 .borrow_mut()
120 .context
121 .repair_style(context, node, new_style),
122 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
123 float_box.contents.repair_style(context, node, new_style)
124 },
125 BlockLevelBox::OutsideMarker(outside_marker) => {
126 outside_marker.repair_style(context, node, new_style)
127 },
128 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
129 base.repair_style(new_style);
130 contents.repair_style(context, node, new_style);
131 },
132 }
133 }
134
135 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
136 match self {
137 BlockLevelBox::Independent(independent_formatting_context) => {
138 callback(&independent_formatting_context.base)
139 },
140 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
141 callback(&positioned_box.borrow().context.base)
142 },
143 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&float_box.contents.base),
144 BlockLevelBox::OutsideMarker(outside_marker) => callback(&outside_marker.context.base),
145 BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
146 }
147 }
148
149 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
150 match self {
151 BlockLevelBox::Independent(independent_formatting_context) => {
152 callback(&mut independent_formatting_context.base)
153 },
154 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
155 callback(&mut positioned_box.borrow_mut().context.base)
156 },
157 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base),
158 BlockLevelBox::OutsideMarker(outside_marker) => {
159 callback(&mut outside_marker.context.base)
160 },
161 BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
162 }
163 }
164
165 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
166 match self {
167 Self::Independent(independent_formatting_context) => {
168 independent_formatting_context.attached_to_tree(layout_box)
169 },
170 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
171 positioned_box.borrow().context.attached_to_tree(layout_box)
172 },
173 Self::OutOfFlowFloatBox(float_box) => float_box.contents.attached_to_tree(layout_box),
174 Self::OutsideMarker(outside_marker) => {
175 outside_marker.context.attached_to_tree(layout_box)
176 },
177 Self::SameFormattingContextBlock { contents, .. } => {
178 contents.attached_to_tree(layout_box)
179 },
180 }
181 }
182
183 fn contains_floats(&self) -> bool {
184 match self {
185 BlockLevelBox::SameFormattingContextBlock {
186 contains_floats, ..
187 } => *contains_floats,
188 BlockLevelBox::OutOfFlowFloatBox { .. } => true,
189 _ => false,
190 }
191 }
192
193 fn find_block_margin_collapsing_with_parent(
194 &self,
195 layout_context: &LayoutContext,
196 collected_margin: &mut CollapsedMargin,
197 containing_block: &ContainingBlock,
198 ) -> bool {
199 let layout_style = match self {
200 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
201 contents.layout_style(base)
202 },
203 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
204 BlockLevelBox::OutOfFlowFloatBox(_) => return true,
205 BlockLevelBox::OutsideMarker(_) => return false,
206 BlockLevelBox::Independent(context) => {
207 context.layout_style()
210 },
211 };
212
213 let style = layout_style.style();
215 if style.get_box().clear != StyleClear::None {
216 return false;
217 }
218
219 let ContentBoxSizesAndPBM {
220 content_box_sizes,
221 pbm,
222 ..
223 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
224 let margin = pbm.margin.auto_is(Au::zero);
225 collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_start));
226
227 let BlockLevelBox::SameFormattingContextBlock { contents, .. } = self else {
228 return false;
229 };
230
231 if !pbm.padding.block_start.is_zero() || !pbm.border.block_start.is_zero() {
232 return false;
233 }
234
235 let available_inline_size =
236 containing_block.size.inline - pbm.padding_border_sums.inline - margin.inline_sum();
237 let available_block_size = containing_block.size.block.to_definite().map(|block_size| {
238 Au::zero().max(block_size - pbm.padding_border_sums.block - margin.block_sum())
239 });
240
241 let tentative_block_size = content_box_sizes.block.resolve_extrinsic(
242 Size::FitContent,
243 Au::zero(),
244 available_block_size,
245 );
246
247 let get_inline_content_sizes = || {
248 let constraint_space = ConstraintSpace::new(
249 tentative_block_size,
250 style,
251 None, );
253 self.inline_content_sizes(layout_context, &constraint_space)
254 .sizes
255 };
256 let inline_size = content_box_sizes.inline.resolve(
257 Direction::Inline,
258 Size::Stretch,
259 Au::zero,
260 Some(available_inline_size),
261 get_inline_content_sizes,
262 false, );
264
265 let containing_block_for_children = ContainingBlock {
266 size: ContainingBlockSize {
267 inline: inline_size,
268 block: tentative_block_size,
269 },
270 style,
271 };
272
273 if !contents.find_block_margin_collapsing_with_parent(
274 layout_context,
275 collected_margin,
276 &containing_block_for_children,
277 ) {
278 return false;
279 }
280
281 if !tentative_block_size.definite_or_min().is_zero() ||
282 !pbm.padding_border_sums.block.is_zero()
283 {
284 return false;
285 }
286
287 collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_end));
288
289 true
290 }
291}
292
293#[derive(Clone, Copy)]
294pub(crate) struct CollapsibleWithParentStartMargin(bool);
295
296#[derive(Debug, MallocSizeOf)]
299pub(crate) struct OutsideMarker {
300 pub list_item_style: Arc<ComputedValues>,
301 pub context: IndependentFormattingContext,
302}
303
304impl OutsideMarker {
305 fn layout(
306 &self,
307 layout_context: &LayoutContext<'_>,
308 containing_block: &ContainingBlock<'_>,
309 positioning_context: &mut PositioningContext,
310 ) -> Fragment {
311 let style = &self.context.base.style;
312 let preferred_aspect_ratio = self.context.preferred_aspect_ratio(&LogicalVec2::zero());
313 let constraint_space =
314 ConstraintSpace::new(SizeConstraint::default(), style, preferred_aspect_ratio);
315 let content_sizes = self
316 .context
317 .inline_content_sizes(layout_context, &constraint_space);
318 let containing_block_for_children = ContainingBlock {
319 size: ContainingBlockSize {
320 inline: content_sizes.sizes.max_content,
321 block: SizeConstraint::default(),
322 },
323 style,
324 };
325
326 let layout = self.context.layout(
327 layout_context,
328 positioning_context,
329 &containing_block_for_children,
330 containing_block,
331 preferred_aspect_ratio,
332 &LazySize::intrinsic(),
333 );
334
335 let max_inline_size = layout
336 .fragments
337 .iter()
338 .map(|fragment| {
339 fragment
340 .base()
341 .map(|base| base.rect)
342 .unwrap_or_default()
343 .to_logical(&containing_block_for_children)
344 .max_inline_position()
345 })
346 .max()
347 .unwrap_or_default();
348
349 let pbm_of_list_item =
359 LayoutStyle::Default(&self.list_item_style).padding_border_margin(containing_block);
360 let content_rect = LogicalRect {
361 start_corner: LogicalVec2 {
362 inline: -max_inline_size -
363 (pbm_of_list_item.border.inline_start +
364 pbm_of_list_item.padding.inline_start),
365 block: Zero::zero(),
366 },
367 size: LogicalVec2 {
368 inline: max_inline_size,
369 block: layout.content_block_size,
370 },
371 };
372
373 let mut base_fragment_info = BaseFragmentInfo::anonymous();
374 base_fragment_info.flags |= FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER;
375
376 Fragment::Box(ArcRefCell::new(BoxFragment::new(
377 base_fragment_info,
378 style.clone(),
379 layout.fragments,
380 content_rect.as_physical(Some(containing_block)),
381 PhysicalSides::zero(),
382 PhysicalSides::zero(),
383 PhysicalSides::zero(),
384 layout.specific_layout_info,
385 )))
386 }
387
388 fn repair_style(
389 &mut self,
390 context: &SharedStyleContext,
391 node: &ServoThreadSafeLayoutNode,
392 new_style: &Arc<ComputedValues>,
393 ) {
394 self.list_item_style = node.parent_style(context);
395 self.context.repair_style(context, node, new_style);
396 }
397}
398
399impl BlockFormattingContext {
400 pub(super) fn layout(
401 &self,
402 layout_context: &LayoutContext,
403 positioning_context: &mut PositioningContext,
404 containing_block: &ContainingBlock,
405 ) -> CacheableLayoutResult {
406 let mut sequential_layout_state = if self.contains_floats || !layout_context.use_rayon {
407 Some(SequentialLayoutState::new(containing_block.size.inline))
408 } else {
409 None
410 };
411
412 let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
416
417 let flow_layout = self.contents.layout(
418 layout_context,
419 positioning_context,
420 containing_block,
421 sequential_layout_state.as_mut(),
422 CollapsibleWithParentStartMargin(false),
423 ignore_block_margins_for_stretch,
424 );
425 debug_assert!(
426 !flow_layout
427 .collapsible_margins_in_children
428 .collapsed_through
429 );
430
431 let clearance = sequential_layout_state.and_then(|sequential_layout_state| {
435 sequential_layout_state.calculate_clearance(Clear::Both, &CollapsedMargin::zero())
436 });
437
438 CacheableLayoutResult {
439 fragments: flow_layout.fragments,
440 content_block_size: flow_layout.content_block_size +
441 flow_layout.collapsible_margins_in_children.end.solve() +
442 clearance.unwrap_or_default(),
443 content_inline_size_for_table: None,
444 baselines: flow_layout.baselines,
445 depends_on_block_constraints: flow_layout.depends_on_block_constraints,
446 specific_layout_info: None,
447 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
448 }
449 }
450
451 #[inline]
452 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
453 LayoutStyle::Default(&base.style)
454 }
455
456 pub(crate) fn repair_style(
457 &mut self,
458 context: &SharedStyleContext,
459 node: &ServoThreadSafeLayoutNode,
460 new_style: &Arc<ComputedValues>,
461 ) {
462 self.contents.repair_style(context, node, new_style);
463 }
464
465 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
466 self.contents.attached_to_tree(layout_box);
467 }
468}
469
470fn compute_inline_content_sizes_for_block_level_boxes(
476 boxes: &[ArcRefCell<BlockLevelBox>],
477 layout_context: &LayoutContext,
478 containing_block: &IndefiniteContainingBlock,
479) -> InlineContentSizesResult {
480 let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
481 match &*box_.borrow() {
482 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
483 BlockLevelBox::OutsideMarker { .. } => None,
484 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
485 let inline_content_sizes_result = float_box.contents.outer_inline_content_sizes(
486 layout_context,
487 containing_block,
488 &LogicalVec2::zero(),
489 false, );
491 let style = &float_box.contents.style();
492 let container_writing_mode = containing_block.style.writing_mode;
493 Some((
494 inline_content_sizes_result,
495 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode),
496 Clear::from_style_and_container_writing_mode(style, container_writing_mode),
497 ))
498 },
499 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
500 let is_anonymous_block =
501 matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
502 let inline_content_sizes_result = sizing::outer_inline(
503 base,
504 &contents.layout_style(base),
505 containing_block,
506 &LogicalVec2::zero(),
507 false, false, !is_anonymous_block, |_| None, |constraint_space| {
512 base.inline_content_sizes(layout_context, constraint_space, contents)
513 },
514 |_aspect_ratio| None,
515 );
516 let clear = if is_anonymous_block {
525 Clear::None
526 } else {
527 Clear::Both
528 };
529 Some((inline_content_sizes_result, None, clear))
530 },
531 BlockLevelBox::Independent(independent) => {
532 let inline_content_sizes_result = independent.outer_inline_content_sizes(
533 layout_context,
534 containing_block,
535 &LogicalVec2::zero(),
536 false, );
538 Some((
539 inline_content_sizes_result,
540 None,
541 Clear::from_style_and_container_writing_mode(
542 independent.style(),
543 containing_block.style.writing_mode,
544 ),
545 ))
546 },
547 }
548 };
549
550 #[derive(Default)]
553 struct AccumulatedData {
554 depends_on_block_constraints: bool,
556 max_size: ContentSizes,
558 floats: LogicalSides1D<ContentSizes>,
561 }
562
563 impl AccumulatedData {
564 fn max_size_including_uncleared_floats(&self) -> ContentSizes {
565 self.max_size.max(self.floats.start.union(&self.floats.end))
566 }
567 fn clear_floats(&mut self, clear: Clear) {
568 match clear {
569 Clear::InlineStart => {
570 self.max_size = self.max_size_including_uncleared_floats();
571 self.floats.start = ContentSizes::default();
572 },
573 Clear::InlineEnd => {
574 self.max_size = self.max_size_including_uncleared_floats();
575 self.floats.end = ContentSizes::default();
576 },
577 Clear::Both => {
578 self.max_size = self.max_size_including_uncleared_floats();
579 self.floats = LogicalSides1D::default();
580 },
581 Clear::None => {},
582 };
583 }
584 }
585
586 let accumulate =
587 |mut data: AccumulatedData,
588 (inline_content_sizes_result, float, clear): (InlineContentSizesResult, _, _)| {
589 let size = inline_content_sizes_result.sizes.max(ContentSizes::zero());
590 let depends_on_block_constraints =
591 inline_content_sizes_result.depends_on_block_constraints;
592 data.depends_on_block_constraints |= depends_on_block_constraints;
593 data.clear_floats(clear);
594 match float {
595 Some(FloatSide::InlineStart) => data.floats.start.union_assign(&size),
596 Some(FloatSide::InlineEnd) => data.floats.end.union_assign(&size),
597 None => {
598 data.max_size
599 .max_assign(data.floats.start.union(&data.floats.end).union(&size));
600 data.floats = LogicalSides1D::default();
601 },
602 }
603 data
604 };
605 let data = if layout_context.use_rayon {
606 boxes
607 .par_iter()
608 .filter_map(get_box_info)
609 .collect::<Vec<_>>()
610 .into_iter()
611 .fold(AccumulatedData::default(), accumulate)
612 } else {
613 boxes
614 .iter()
615 .filter_map(get_box_info)
616 .fold(AccumulatedData::default(), accumulate)
617 };
618 InlineContentSizesResult {
619 depends_on_block_constraints: data.depends_on_block_constraints,
620 sizes: data.max_size_including_uncleared_floats(),
621 }
622}
623
624impl BlockContainer {
625 fn layout(
626 &self,
627 layout_context: &LayoutContext,
628 positioning_context: &mut PositioningContext,
629 containing_block: &ContainingBlock,
630 sequential_layout_state: Option<&mut SequentialLayoutState>,
631 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
632 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
633 ) -> CacheableLayoutResult {
634 match self {
635 BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children(
636 layout_context,
637 positioning_context,
638 child_boxes,
639 containing_block,
640 sequential_layout_state,
641 collapsible_with_parent_start_margin,
642 ignore_block_margins_for_stretch,
643 ),
644 BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
645 layout_context,
646 positioning_context,
647 containing_block,
648 sequential_layout_state,
649 collapsible_with_parent_start_margin,
650 ),
651 }
652 }
653
654 #[inline]
655 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
656 LayoutStyle::Default(&base.style)
657 }
658
659 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
660 match self {
661 Self::BlockLevelBoxes(child_boxes) => {
662 for child_box in child_boxes {
663 child_box.borrow_mut().with_base_mut(|base| {
664 base.parent_box.replace(layout_box.clone());
665 });
666 }
667 },
668 Self::InlineFormattingContext(ifc) => ifc.attached_to_tree(layout_box),
669 }
670 }
671
672 fn find_block_margin_collapsing_with_parent(
673 &self,
674 layout_context: &LayoutContext,
675 collected_margin: &mut CollapsedMargin,
676 containing_block_for_children: &ContainingBlock,
677 ) -> bool {
678 match self {
679 BlockContainer::BlockLevelBoxes(boxes) => boxes.iter().all(|block_level_box| {
680 block_level_box
681 .borrow()
682 .find_block_margin_collapsing_with_parent(
683 layout_context,
684 collected_margin,
685 containing_block_for_children,
686 )
687 }),
688 BlockContainer::InlineFormattingContext(context) => context
689 .find_block_margin_collapsing_with_parent(
690 layout_context,
691 collected_margin,
692 containing_block_for_children,
693 ),
694 }
695 }
696}
697
698impl ComputeInlineContentSizes for BlockContainer {
699 fn compute_inline_content_sizes(
700 &self,
701 layout_context: &LayoutContext,
702 constraint_space: &ConstraintSpace,
703 ) -> InlineContentSizesResult {
704 match &self {
705 Self::BlockLevelBoxes(boxes) => compute_inline_content_sizes_for_block_level_boxes(
706 boxes,
707 layout_context,
708 &constraint_space.into(),
709 ),
710 Self::InlineFormattingContext(context) => {
711 context.compute_inline_content_sizes(layout_context, constraint_space)
712 },
713 }
714 }
715}
716
717fn layout_block_level_children(
718 layout_context: &LayoutContext,
719 positioning_context: &mut PositioningContext,
720 child_boxes: &[ArcRefCell<BlockLevelBox>],
721 containing_block: &ContainingBlock,
722 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
723 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
724 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
725) -> CacheableLayoutResult {
726 let mut placement_state =
727 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
728
729 let fragments = match sequential_layout_state {
730 Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
731 layout_context,
732 positioning_context,
733 child_boxes,
734 sequential_layout_state,
735 &mut placement_state,
736 ignore_block_margins_for_stretch,
737 ),
738 None => layout_block_level_children_in_parallel(
739 layout_context,
740 positioning_context,
741 child_boxes,
742 &mut placement_state,
743 ignore_block_margins_for_stretch,
744 ),
745 };
746
747 let depends_on_block_constraints = fragments.iter().any(|fragment| {
748 fragment.base().is_some_and(|base| {
749 base.flags.contains(
750 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
751 )
752 })
753 });
754
755 let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
756 CacheableLayoutResult {
757 fragments,
758 content_block_size,
759 collapsible_margins_in_children,
760 baselines,
761 depends_on_block_constraints,
762 content_inline_size_for_table: None,
763 specific_layout_info: None,
764 }
765}
766
767fn layout_block_level_children_in_parallel(
768 layout_context: &LayoutContext,
769 positioning_context: &mut PositioningContext,
770 child_boxes: &[ArcRefCell<BlockLevelBox>],
771 placement_state: &mut PlacementState,
772 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
773) -> Vec<Fragment> {
774 let mut layout_results: Vec<(Fragment, PositioningContext)> =
775 Vec::with_capacity(child_boxes.len());
776
777 child_boxes
778 .par_iter()
779 .map(|child_box| {
780 let mut child_positioning_context = PositioningContext::default();
781 let fragment = child_box.borrow().layout(
782 layout_context,
783 &mut child_positioning_context,
784 placement_state.containing_block,
785 None,
786 None,
787 ignore_block_margins_for_stretch,
788 false, );
790 (fragment, child_positioning_context)
791 })
792 .collect_into_vec(&mut layout_results);
793
794 layout_results
795 .into_iter()
796 .map(|(mut fragment, mut child_positioning_context)| {
797 placement_state.place_fragment_and_update_baseline(&mut fragment, None);
798 child_positioning_context.adjust_static_position_of_hoisted_fragments(
799 &fragment,
800 PositioningContextLength::zero(),
801 );
802 positioning_context.append(child_positioning_context);
803 fragment
804 })
805 .collect()
806}
807
808fn layout_block_level_children_sequentially(
809 layout_context: &LayoutContext,
810 positioning_context: &mut PositioningContext,
811 child_boxes: &[ArcRefCell<BlockLevelBox>],
812 sequential_layout_state: &mut SequentialLayoutState,
813 placement_state: &mut PlacementState,
814 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
815) -> Vec<Fragment> {
816 child_boxes
820 .iter()
821 .map(|child_box| {
822 layout_block_level_child(
823 layout_context,
824 positioning_context,
825 &child_box.borrow(),
826 Some(sequential_layout_state),
827 placement_state,
828 ignore_block_margins_for_stretch,
829 false, )
831 })
832 .collect()
833}
834
835fn layout_block_level_child(
836 layout_context: &LayoutContext,
837 positioning_context: &mut PositioningContext,
838 child_box: &BlockLevelBox,
839 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
840 placement_state: &mut PlacementState,
841 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
842 has_inline_parent: bool,
843) -> Fragment {
844 let positioning_context_length_before_layout = positioning_context.len();
845 let mut fragment = child_box.layout(
846 layout_context,
847 positioning_context,
848 placement_state.containing_block,
849 sequential_layout_state.as_deref_mut(),
850 Some(CollapsibleWithParentStartMargin(
851 placement_state.next_in_flow_margin_collapses_with_parent_start_margin,
852 )),
853 ignore_block_margins_for_stretch,
854 has_inline_parent,
855 );
856
857 placement_state.place_fragment_and_update_baseline(&mut fragment, sequential_layout_state);
858 positioning_context.adjust_static_position_of_hoisted_fragments(
859 &fragment,
860 positioning_context_length_before_layout,
861 );
862
863 fragment
864}
865
866impl BlockLevelBox {
867 #[allow(clippy::too_many_arguments)]
868 fn layout(
869 &self,
870 layout_context: &LayoutContext,
871 positioning_context: &mut PositioningContext,
872 containing_block: &ContainingBlock,
873 sequential_layout_state: Option<&mut SequentialLayoutState>,
874 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
875 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
876 has_inline_parent: bool,
877 ) -> Fragment {
878 let fragment = match self {
879 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => Fragment::Box(
880 ArcRefCell::new(positioning_context.layout_maybe_position_relative_fragment(
881 layout_context,
882 containing_block,
883 base,
884 |positioning_context| {
885 layout_in_flow_non_replaced_block_level_same_formatting_context(
886 layout_context,
887 positioning_context,
888 containing_block,
889 base,
890 contents,
891 sequential_layout_state,
892 collapsible_with_parent_start_margin,
893 ignore_block_margins_for_stretch,
894 has_inline_parent,
895 )
896 },
897 )),
898 ),
899 BlockLevelBox::Independent(independent) => Fragment::Box(ArcRefCell::new(
900 positioning_context.layout_maybe_position_relative_fragment(
901 layout_context,
902 containing_block,
903 &independent.base,
904 |positioning_context| {
905 independent.layout_in_flow_block_level(
906 layout_context,
907 positioning_context,
908 containing_block,
909 sequential_layout_state,
910 ignore_block_margins_for_stretch,
911 has_inline_parent,
912 )
913 },
914 ),
915 )),
916 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
917 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
921 box_.clone(),
922 PhysicalRect::zero(),
926 LogicalVec2 {
927 inline: AlignFlags::START,
928 block: AlignFlags::START,
929 },
930 containing_block.style.writing_mode,
931 );
932 let hoisted_fragment = hoisted_box.fragment.clone();
933 positioning_context.push(hoisted_box);
934 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
935 },
936 BlockLevelBox::OutOfFlowFloatBox(float_box) => Fragment::Float(ArcRefCell::new(
937 float_box.layout(layout_context, positioning_context, containing_block),
938 )),
939 BlockLevelBox::OutsideMarker(outside_marker) => {
940 outside_marker.layout(layout_context, containing_block, positioning_context)
941 },
942 };
943
944 self.with_base(|base| base.set_fragment(fragment.clone()));
945
946 fragment
947 }
948
949 fn inline_content_sizes(
950 &self,
951 layout_context: &LayoutContext,
952 constraint_space: &ConstraintSpace,
953 ) -> InlineContentSizesResult {
954 let independent_formatting_context = match self {
955 BlockLevelBox::Independent(independent) => independent,
956 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => &box_.borrow().context,
957 BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents,
958 BlockLevelBox::OutsideMarker(outside_marker) => &outside_marker.context,
959 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
960 return base.inline_content_sizes(layout_context, constraint_space, contents);
961 },
962 };
963 independent_formatting_context.inline_content_sizes(layout_context, constraint_space)
964 }
965}
966
967#[allow(clippy::too_many_arguments)]
973fn layout_in_flow_non_replaced_block_level_same_formatting_context(
974 layout_context: &LayoutContext,
975 positioning_context: &mut PositioningContext,
976 containing_block: &ContainingBlock,
977 base: &LayoutBoxBase,
978 contents: &BlockContainer,
979 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
980 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
981 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
982 has_inline_parent: bool,
983) -> BoxFragment {
984 let style = &base.style;
985 let layout_style = contents.layout_style(base);
986 let containing_block_writing_mode = containing_block.style.writing_mode;
987 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
988 base.inline_content_sizes(layout_context, constraint_space, contents)
989 .sizes
990 };
991 let ContainingBlockPaddingAndBorder {
992 containing_block: containing_block_for_children,
993 pbm,
994 block_sizes,
995 depends_on_block_constraints,
996 available_block_size,
997 justify_self,
998 ..
999 } = solve_containing_block_padding_and_border_for_in_flow_box(
1000 containing_block,
1001 &layout_style,
1002 get_inline_content_sizes,
1003 ignore_block_margins_for_stretch,
1004 None,
1005 has_inline_parent,
1006 );
1007 let ResolvedMargins {
1008 margin,
1009 effective_margin_inline_start,
1010 } = solve_margins(
1011 containing_block,
1012 &pbm,
1013 containing_block_for_children.size.inline,
1014 justify_self,
1015 );
1016
1017 let start_margin_can_collapse_with_children =
1018 pbm.padding.block_start.is_zero() && pbm.border.block_start.is_zero();
1019
1020 let mut clearance = None;
1021 let parent_containing_block_position_info;
1022 match sequential_layout_state {
1023 None => parent_containing_block_position_info = None,
1024 Some(ref mut sequential_layout_state) => {
1025 let clear =
1026 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode);
1027 let mut block_start_margin = CollapsedMargin::new(margin.block_start);
1028
1029 let collapsible_with_parent_start_margin = collapsible_with_parent_start_margin.expect(
1041 "We should know whether we are collapsing the block start margin with the parent \
1042 when laying out sequentially",
1043 ).0 && clear == Clear::None;
1044 if !collapsible_with_parent_start_margin && start_margin_can_collapse_with_children {
1045 contents.find_block_margin_collapsing_with_parent(
1046 layout_context,
1047 &mut block_start_margin,
1048 &containing_block_for_children,
1049 );
1050 }
1051
1052 clearance = sequential_layout_state.calculate_clearance(clear, &block_start_margin);
1054 if clearance.is_some() {
1055 sequential_layout_state.collapse_margins();
1056 }
1057 sequential_layout_state.adjoin_assign(&block_start_margin);
1058 if !start_margin_can_collapse_with_children {
1059 sequential_layout_state.collapse_margins();
1060 }
1061
1062 sequential_layout_state.advance_block_position(
1065 pbm.padding.block_start +
1066 pbm.border.block_start +
1067 clearance.unwrap_or_else(Au::zero),
1068 );
1069
1070 let inline_start = sequential_layout_state
1076 .floats
1077 .containing_block_info
1078 .inline_start +
1079 pbm.padding.inline_start +
1080 pbm.border.inline_start +
1081 effective_margin_inline_start;
1082 let new_cb_offsets = ContainingBlockPositionInfo {
1083 block_start: sequential_layout_state.bfc_relative_block_position,
1084 block_start_margins_not_collapsed: sequential_layout_state.current_margin,
1085 inline_start,
1086 inline_end: inline_start + containing_block_for_children.size.inline,
1087 };
1088 parent_containing_block_position_info = Some(
1089 sequential_layout_state.replace_containing_block_position_info(new_cb_offsets),
1090 );
1091 },
1092 };
1093
1094 let ignore_block_margins_for_stretch = LogicalSides1D::new(
1100 pbm.border.block_start.is_zero() && pbm.padding.block_start.is_zero(),
1101 pbm.border.block_end.is_zero() && pbm.padding.block_end.is_zero(),
1102 );
1103
1104 let flow_layout = contents.layout(
1105 layout_context,
1106 positioning_context,
1107 &containing_block_for_children,
1108 sequential_layout_state.as_deref_mut(),
1109 CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
1110 ignore_block_margins_for_stretch,
1111 );
1112 let mut content_block_size = flow_layout.content_block_size;
1113
1114 let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1116 let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
1117 if start_margin_can_collapse_with_children {
1118 block_margins_collapsed_with_children
1119 .start
1120 .adjoin_assign(&collapsible_margins_in_children.start);
1121 if collapsible_margins_in_children.collapsed_through {
1122 block_margins_collapsed_with_children
1123 .start
1124 .adjoin_assign(&std::mem::replace(
1125 &mut collapsible_margins_in_children.end,
1126 CollapsedMargin::zero(),
1127 ));
1128 }
1129 }
1130
1131 let is_anonymous = matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
1132 let tentative_block_size = if is_anonymous {
1133 &Default::default()
1137 } else {
1138 &containing_block_for_children.size.block
1139 };
1140 let collapsed_through = collapsible_margins_in_children.collapsed_through &&
1141 pbm.padding_border_sums.block.is_zero() &&
1142 tentative_block_size.definite_or_min().is_zero();
1143 block_margins_collapsed_with_children.collapsed_through = collapsed_through;
1144
1145 let end_margin_can_collapse_with_children =
1146 pbm.padding.block_end.is_zero() && pbm.border.block_end.is_zero();
1147 if !end_margin_can_collapse_with_children {
1148 content_block_size += collapsible_margins_in_children.end.solve();
1149 }
1150
1151 let block_size = block_sizes.resolve(
1152 Direction::Block,
1153 Size::FitContent,
1154 Au::zero,
1155 available_block_size,
1156 || content_block_size.into(),
1157 false, );
1159
1160 let end_margin_can_collapse_with_children = end_margin_can_collapse_with_children &&
1175 block_size == content_block_size &&
1176 (collapsed_through || !tentative_block_size.is_definite());
1177 if end_margin_can_collapse_with_children {
1178 block_margins_collapsed_with_children
1179 .end
1180 .adjoin_assign(&collapsible_margins_in_children.end);
1181 }
1182
1183 if let Some(ref mut sequential_layout_state) = sequential_layout_state {
1184 sequential_layout_state
1187 .replace_containing_block_position_info(parent_containing_block_position_info.unwrap());
1188
1189 sequential_layout_state.advance_block_position(
1199 block_size - content_block_size + pbm.padding.block_end + pbm.border.block_end,
1200 );
1201
1202 if !end_margin_can_collapse_with_children {
1203 sequential_layout_state.collapse_margins();
1204 }
1205 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1206 }
1207
1208 let content_rect = LogicalRect {
1209 start_corner: LogicalVec2 {
1210 block: (pbm.padding.block_start +
1211 pbm.border.block_start +
1212 clearance.unwrap_or_else(Au::zero)),
1213 inline: pbm.padding.inline_start +
1214 pbm.border.inline_start +
1215 effective_margin_inline_start,
1216 },
1217 size: LogicalVec2 {
1218 block: block_size,
1219 inline: containing_block_for_children.size.inline,
1220 },
1221 };
1222
1223 let mut base_fragment_info = base.base_fragment_info;
1224
1225 if depends_on_block_constraints || (is_anonymous && flow_layout.depends_on_block_constraints) {
1229 base_fragment_info
1230 .flags
1231 .insert(FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM);
1232 }
1233
1234 BoxFragment::new(
1235 base_fragment_info,
1236 style.clone(),
1237 flow_layout.fragments,
1238 content_rect.as_physical(Some(containing_block)),
1239 pbm.padding.to_physical(containing_block_writing_mode),
1240 pbm.border.to_physical(containing_block_writing_mode),
1241 margin.to_physical(containing_block_writing_mode),
1242 flow_layout.specific_layout_info,
1243 )
1244 .with_baselines(flow_layout.baselines)
1245 .with_block_level_layout_info(block_margins_collapsed_with_children, clearance)
1246}
1247
1248impl IndependentFormattingContext {
1249 pub(crate) fn layout_in_flow_block_level(
1257 &self,
1258 layout_context: &LayoutContext,
1259 positioning_context: &mut PositioningContext,
1260 containing_block: &ContainingBlock,
1261 sequential_layout_state: Option<&mut SequentialLayoutState>,
1262 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1263 has_inline_parent: bool,
1264 ) -> BoxFragment {
1265 if let Some(sequential_layout_state) = sequential_layout_state {
1266 return self.layout_in_flow_block_level_sequentially(
1267 layout_context,
1268 positioning_context,
1269 containing_block,
1270 sequential_layout_state,
1271 ignore_block_margins_for_stretch,
1272 has_inline_parent,
1273 );
1274 }
1275
1276 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1277 self.inline_content_sizes(layout_context, constraint_space)
1278 .sizes
1279 };
1280 let layout_style = self.layout_style();
1281 let ContainingBlockPaddingAndBorder {
1282 containing_block: containing_block_for_children,
1283 pbm,
1284 block_sizes,
1285 depends_on_block_constraints,
1286 available_block_size,
1287 justify_self,
1288 preferred_aspect_ratio,
1289 } = solve_containing_block_padding_and_border_for_in_flow_box(
1290 containing_block,
1291 &layout_style,
1292 get_inline_content_sizes,
1293 ignore_block_margins_for_stretch,
1294 Some(self),
1295 has_inline_parent,
1296 );
1297
1298 let lazy_block_size = LazySize::new(
1299 &block_sizes,
1300 Direction::Block,
1301 Size::FitContent,
1302 Au::zero,
1303 available_block_size,
1304 layout_style.is_table(),
1305 );
1306
1307 let layout = self.layout(
1308 layout_context,
1309 positioning_context,
1310 &containing_block_for_children,
1311 containing_block,
1312 preferred_aspect_ratio,
1313 &lazy_block_size,
1314 );
1315
1316 let inline_size = layout
1317 .content_inline_size_for_table
1318 .unwrap_or(containing_block_for_children.size.inline);
1319 let block_size = lazy_block_size.resolve(|| layout.content_block_size);
1320
1321 let ResolvedMargins {
1322 margin,
1323 effective_margin_inline_start,
1324 } = solve_margins(containing_block, &pbm, inline_size, justify_self);
1325
1326 let content_rect = LogicalRect {
1327 start_corner: LogicalVec2 {
1328 block: pbm.padding.block_start + pbm.border.block_start,
1329 inline: pbm.padding.inline_start +
1330 pbm.border.inline_start +
1331 effective_margin_inline_start,
1332 },
1333 size: LogicalVec2 {
1334 block: block_size,
1335 inline: inline_size,
1336 },
1337 };
1338
1339 let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1340 let containing_block_writing_mode = containing_block.style.writing_mode;
1341
1342 let mut base_fragment_info = self.base.base_fragment_info;
1343 if depends_on_block_constraints {
1344 base_fragment_info.flags.insert(
1345 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1346 );
1347 }
1348 BoxFragment::new(
1349 base_fragment_info,
1350 self.base.style.clone(),
1351 layout.fragments,
1352 content_rect.as_physical(Some(containing_block)),
1353 pbm.padding.to_physical(containing_block_writing_mode),
1354 pbm.border.to_physical(containing_block_writing_mode),
1355 margin.to_physical(containing_block_writing_mode),
1356 layout.specific_layout_info,
1357 )
1358 .with_baselines(layout.baselines)
1359 .with_block_level_layout_info(block_margins_collapsed_with_children, None)
1360 }
1361
1362 fn layout_in_flow_block_level_sequentially(
1366 &self,
1367 layout_context: &LayoutContext<'_>,
1368 positioning_context: &mut PositioningContext,
1369 containing_block: &ContainingBlock<'_>,
1370 sequential_layout_state: &mut SequentialLayoutState,
1371 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1372 has_inline_parent: bool,
1373 ) -> BoxFragment {
1374 let style = &self.base.style;
1375 let containing_block_writing_mode = containing_block.style.writing_mode;
1376 let ContentBoxSizesAndPBM {
1377 content_box_sizes,
1378 pbm,
1379 depends_on_block_constraints,
1380 ..
1381 } = self
1382 .layout_style()
1383 .content_box_sizes_and_padding_border_margin(&containing_block.into());
1384
1385 let (margin_block_start, margin_block_end) =
1386 solve_block_margins_for_in_flow_block_level(&pbm);
1387 let collapsed_margin_block_start = CollapsedMargin::new(margin_block_start);
1388
1389 let mut content_size;
1400 let mut layout;
1401 let mut placement_rect;
1402
1403 let clear_position = sequential_layout_state.calculate_clear_position(
1407 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode),
1408 &collapsed_margin_block_start,
1409 );
1410 let ceiling = clear_position.unwrap_or_else(|| {
1411 sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
1412 });
1413
1414 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1416 let available_block_size = containing_block
1417 .size
1418 .block
1419 .to_definite()
1420 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1421 let is_table = self.is_table();
1422 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
1423
1424 #[derive(Default)]
1425 struct Cache {
1426 min_block_size: Au,
1427 max_block_size: Option<Au>,
1428 tentative_block_size: SizeConstraint,
1429 depends_on_stretch_size: bool,
1430 }
1431 let mut cache = Cache {
1432 depends_on_stretch_size: true,
1433 ..Default::default()
1434 };
1435
1436 let update_cache = |cache: &mut Cache, stretch_size| {
1437 let tentative_block_content_size = self
1438 .tentative_block_content_size_with_dependency(preferred_aspect_ratio, stretch_size);
1439 let (preferred_block_size, min_block_size, max_block_size, depends_on_stretch_size) =
1440 if let Some(result) = tentative_block_content_size {
1441 let (block_content_size, depends_on_stretch_size) = result;
1442 let (preferred, min, max) = content_box_sizes.block.resolve_each(
1443 Size::FitContent,
1444 Au::zero,
1445 available_block_size,
1446 || block_content_size,
1447 is_table,
1448 );
1449 (Some(preferred), min, max, depends_on_stretch_size)
1450 } else {
1451 let (preferred, min, max) = content_box_sizes.block.resolve_each_extrinsic(
1452 Size::FitContent,
1453 Au::zero(),
1454 available_block_size,
1455 );
1456 (preferred, min, max, false)
1457 };
1458 cache.min_block_size = min_block_size;
1459 cache.max_block_size = max_block_size;
1460 cache.tentative_block_size =
1461 SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
1462 cache.depends_on_stretch_size = depends_on_stretch_size;
1463 };
1464
1465 let get_inline_content_sizes = |cache: &Cache| {
1467 let constraint_space =
1468 ConstraintSpace::new(cache.tentative_block_size, style, preferred_aspect_ratio);
1469 self.inline_content_sizes(layout_context, &constraint_space)
1470 .sizes
1471 };
1472
1473 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1474 let automatic_inline_size = automatic_inline_size(justify_self, Some(self));
1475 let compute_inline_size = |cache: &mut Cache, stretch_size| {
1476 if cache.depends_on_stretch_size {
1477 update_cache(cache, stretch_size);
1478 }
1479 content_box_sizes.inline.resolve(
1480 Direction::Inline,
1481 automatic_inline_size,
1482 Au::zero,
1483 Some(stretch_size),
1484 || get_inline_content_sizes(cache),
1485 is_table,
1486 )
1487 };
1488
1489 let get_lazy_block_size = || {
1490 LazySize::new(
1491 &content_box_sizes.block,
1492 Direction::Block,
1493 Size::FitContent,
1494 Au::zero,
1495 available_block_size,
1496 is_table,
1497 )
1498 };
1499
1500 let inline_size_with_max_available_space = compute_inline_size(&mut cache, MAX_AU);
1509 let inline_size_with_no_available_space = compute_inline_size(&mut cache, Au::zero());
1510 if inline_size_with_no_available_space == inline_size_with_max_available_space {
1511 let inline_size = inline_size_with_no_available_space;
1515 let lazy_block_size = get_lazy_block_size();
1516 layout = self.layout(
1517 layout_context,
1518 positioning_context,
1519 &ContainingBlock {
1520 size: ContainingBlockSize {
1521 inline: inline_size,
1522 block: cache.tentative_block_size,
1526 },
1527 style,
1528 },
1529 containing_block,
1530 preferred_aspect_ratio,
1531 &lazy_block_size,
1532 );
1533
1534 content_size = LogicalVec2 {
1535 block: lazy_block_size.resolve(|| layout.content_block_size),
1536 inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
1537 };
1538
1539 let mut placement = PlacementAmongFloats::new(
1540 &sequential_layout_state.floats,
1541 ceiling,
1542 content_size + pbm.padding_border_sums,
1543 &pbm,
1544 );
1545 placement_rect = placement.place();
1546 } else {
1547 let minimum_size_of_block = LogicalVec2 {
1553 inline: inline_size_with_no_available_space,
1557 block: match cache.tentative_block_size {
1565 SizeConstraint::Definite(size) if cache.max_block_size.is_some() => size,
1568 _ => cache.min_block_size,
1571 },
1572 } + pbm.padding_border_sums;
1573 let mut placement = PlacementAmongFloats::new(
1574 &sequential_layout_state.floats,
1575 ceiling,
1576 minimum_size_of_block,
1577 &pbm,
1578 );
1579
1580 loop {
1581 placement_rect = placement.place();
1583 let available_inline_size =
1584 placement_rect.size.inline - pbm.padding_border_sums.inline;
1585 let proposed_inline_size = compute_inline_size(&mut cache, available_inline_size);
1586
1587 let positioning_context_length = positioning_context.len();
1591 let lazy_block_size = get_lazy_block_size();
1592 layout = self.layout(
1593 layout_context,
1594 positioning_context,
1595 &ContainingBlock {
1596 size: ContainingBlockSize {
1597 inline: proposed_inline_size,
1598 block: cache.tentative_block_size,
1599 },
1600 style,
1601 },
1602 containing_block,
1603 preferred_aspect_ratio,
1604 &lazy_block_size,
1605 );
1606
1607 let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
1608 debug_assert!(inline_size < proposed_inline_size);
1613 inline_size
1614 } else {
1615 proposed_inline_size
1616 };
1617 content_size = LogicalVec2 {
1618 block: lazy_block_size.resolve(|| layout.content_block_size),
1619 inline: inline_size,
1620 };
1621
1622 if placement.try_to_expand_for_auto_block_size(
1626 content_size.block + pbm.padding_border_sums.block,
1627 &placement_rect.size,
1628 ) {
1629 break;
1630 }
1631
1632 positioning_context.truncate(&positioning_context_length);
1636 }
1637 }
1638
1639 let has_clearance = clear_position.is_some() || placement_rect.start_corner.block > ceiling;
1643 let clearance = has_clearance.then(|| {
1644 placement_rect.start_corner.block -
1645 sequential_layout_state
1646 .position_with_zero_clearance(&collapsed_margin_block_start)
1647 });
1648
1649 let ((margin_inline_start, margin_inline_end), effective_margin_inline_start) =
1650 solve_inline_margins_avoiding_floats(
1651 sequential_layout_state,
1652 containing_block,
1653 &pbm,
1654 content_size.inline + pbm.padding_border_sums.inline,
1655 placement_rect,
1656 justify_self,
1657 );
1658
1659 let margin = LogicalSides {
1660 inline_start: margin_inline_start,
1661 inline_end: margin_inline_end,
1662 block_start: margin_block_start,
1663 block_end: margin_block_end,
1664 };
1665
1666 if clearance.is_some() {
1669 sequential_layout_state.collapse_margins();
1670 }
1671 sequential_layout_state.adjoin_assign(&collapsed_margin_block_start);
1672
1673 sequential_layout_state.collapse_margins();
1675 sequential_layout_state.advance_block_position(
1676 pbm.padding_border_sums.block + content_size.block + clearance.unwrap_or_else(Au::zero),
1677 );
1678 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1679
1680 let content_rect = LogicalRect {
1681 start_corner: LogicalVec2 {
1682 block: pbm.padding.block_start +
1683 pbm.border.block_start +
1684 clearance.unwrap_or_else(Au::zero),
1685 inline: pbm.padding.inline_start +
1686 pbm.border.inline_start +
1687 effective_margin_inline_start,
1688 },
1689 size: content_size,
1690 };
1691
1692 let mut base_fragment_info = self.base.base_fragment_info;
1693 if depends_on_block_constraints {
1694 base_fragment_info.flags.insert(
1695 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1696 );
1697 }
1698
1699 BoxFragment::new(
1700 base_fragment_info,
1701 style.clone(),
1702 layout.fragments,
1703 content_rect.as_physical(Some(containing_block)),
1704 pbm.padding.to_physical(containing_block_writing_mode),
1705 pbm.border.to_physical(containing_block_writing_mode),
1706 margin.to_physical(containing_block_writing_mode),
1707 layout.specific_layout_info,
1708 )
1709 .with_baselines(layout.baselines)
1710 .with_block_level_layout_info(CollapsedBlockMargins::from_margin(&margin), clearance)
1711 }
1712}
1713
1714struct ContainingBlockPaddingAndBorder<'a> {
1715 containing_block: ContainingBlock<'a>,
1716 pbm: PaddingBorderMargin,
1717 block_sizes: Sizes,
1718 depends_on_block_constraints: bool,
1719 available_block_size: Option<Au>,
1720 justify_self: AlignFlags,
1721 preferred_aspect_ratio: Option<AspectRatio>,
1722}
1723
1724struct ResolvedMargins {
1725 pub margin: LogicalSides<Au>,
1727
1728 pub effective_margin_inline_start: Au,
1735}
1736
1737fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
1743 containing_block: &ContainingBlock<'_>,
1744 layout_style: &'a LayoutStyle,
1745 get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
1746 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1747 context: Option<&IndependentFormattingContext>,
1748 has_inline_parent: bool,
1749) -> ContainingBlockPaddingAndBorder<'a> {
1750 let style = layout_style.style();
1751 if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
1752 let containing_block_for_children = ContainingBlock {
1756 size: ContainingBlockSize {
1757 inline: containing_block.size.inline,
1758 block: containing_block.size.block,
1759 },
1760 style,
1761 };
1762 return ContainingBlockPaddingAndBorder {
1765 containing_block: containing_block_for_children,
1766 pbm: PaddingBorderMargin::zero(),
1767 block_sizes: Sizes::default(),
1768 depends_on_block_constraints: false,
1769 available_block_size: None,
1772 justify_self: AlignFlags::NORMAL,
1775 preferred_aspect_ratio: None,
1776 };
1777 }
1778
1779 let ContentBoxSizesAndPBM {
1780 content_box_sizes,
1781 pbm,
1782 depends_on_block_constraints,
1783 ..
1784 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
1785
1786 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1787 let available_inline_size = Au::zero().max(containing_block.size.inline - pbm_sums.inline);
1788 let available_block_size = containing_block
1789 .size
1790 .block
1791 .to_definite()
1792 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1793
1794 let preferred_aspect_ratio =
1797 context.and_then(|context| context.preferred_aspect_ratio(&pbm.padding_border_sums));
1798 let is_table = layout_style.is_table();
1799
1800 let tentative_block_content_size = context.and_then(|context| {
1803 context.tentative_block_content_size(preferred_aspect_ratio, available_inline_size)
1804 });
1805 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
1806 SizeConstraint::Definite(content_box_sizes.block.resolve(
1807 Direction::Block,
1808 Size::FitContent,
1809 Au::zero,
1810 available_block_size,
1811 || block_content_size,
1812 is_table,
1813 ))
1814 } else {
1815 content_box_sizes.block.resolve_extrinsic(
1816 Size::FitContent,
1817 Au::zero(),
1818 available_block_size,
1819 )
1820 };
1821
1822 let get_inline_content_sizes = || {
1825 get_inline_content_sizes(&ConstraintSpace::new(
1826 tentative_block_size,
1827 style,
1828 preferred_aspect_ratio,
1829 ))
1830 };
1831 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1832 let inline_size = content_box_sizes.inline.resolve(
1833 Direction::Inline,
1834 automatic_inline_size(justify_self, context),
1835 Au::zero,
1836 Some(available_inline_size),
1837 get_inline_content_sizes,
1838 is_table,
1839 );
1840
1841 let containing_block_for_children = ContainingBlock {
1842 size: ContainingBlockSize {
1843 inline: inline_size,
1844 block: tentative_block_size,
1845 },
1846 style,
1847 };
1848 assert_eq!(
1850 containing_block.style.writing_mode.is_horizontal(),
1851 containing_block_for_children
1852 .style
1853 .writing_mode
1854 .is_horizontal(),
1855 "Vertical writing modes are not supported yet"
1856 );
1857 ContainingBlockPaddingAndBorder {
1858 containing_block: containing_block_for_children,
1859 pbm,
1860 block_sizes: content_box_sizes.block,
1861 depends_on_block_constraints,
1862 available_block_size,
1863 justify_self,
1864 preferred_aspect_ratio,
1865 }
1866}
1867
1868fn solve_margins(
1873 containing_block: &ContainingBlock<'_>,
1874 pbm: &PaddingBorderMargin,
1875 inline_size: Au,
1876 justify_self: AlignFlags,
1877) -> ResolvedMargins {
1878 let (inline_margins, effective_margin_inline_start) =
1879 solve_inline_margins_for_in_flow_block_level(
1880 containing_block,
1881 pbm,
1882 inline_size,
1883 justify_self,
1884 );
1885 let block_margins = solve_block_margins_for_in_flow_block_level(pbm);
1886 ResolvedMargins {
1887 margin: LogicalSides {
1888 inline_start: inline_margins.0,
1889 inline_end: inline_margins.1,
1890 block_start: block_margins.0,
1891 block_end: block_margins.1,
1892 },
1893 effective_margin_inline_start,
1894 }
1895}
1896
1897fn solve_block_margins_for_in_flow_block_level(pbm: &PaddingBorderMargin) -> (Au, Au) {
1901 (
1902 pbm.margin.block_start.auto_is(Au::zero),
1903 pbm.margin.block_end.auto_is(Au::zero),
1904 )
1905}
1906
1907fn resolve_justify_self(
1909 style: &ComputedValues,
1910 containing_block_style: &ComputedValues,
1911 has_inline_parent: bool,
1912) -> AlignFlags {
1913 let alignment = match style.clone_justify_self().0 {
1919 AlignFlags::AUTO if has_inline_parent => AlignFlags::NORMAL,
1920 AlignFlags::AUTO => containing_block_style.clone_justify_items().computed.0.0,
1921 alignment => alignment,
1922 };
1923 let is_ltr = |style: &ComputedValues| style.writing_mode.line_left_is_inline_start();
1924 let alignment_value = match alignment.value() {
1925 AlignFlags::LEFT if is_ltr(containing_block_style) => AlignFlags::START,
1926 AlignFlags::LEFT => AlignFlags::END,
1927 AlignFlags::RIGHT if is_ltr(containing_block_style) => AlignFlags::END,
1928 AlignFlags::RIGHT => AlignFlags::START,
1929 AlignFlags::SELF_START if is_ltr(containing_block_style) == is_ltr(style) => {
1930 AlignFlags::START
1931 },
1932 AlignFlags::SELF_START => AlignFlags::END,
1933 AlignFlags::SELF_END if is_ltr(containing_block_style) == is_ltr(style) => AlignFlags::END,
1934 AlignFlags::SELF_END => AlignFlags::START,
1935 alignment_value => alignment_value,
1936 };
1937 alignment.flags() | alignment_value
1938}
1939
1940#[inline]
1943fn automatic_inline_size<T>(
1944 justify_self: AlignFlags,
1945 context: Option<&IndependentFormattingContext>,
1946) -> Size<T> {
1947 let normal_stretches = || {
1948 !context.is_some_and(|context| {
1949 context
1950 .base
1951 .base_fragment_info
1952 .flags
1953 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET) ||
1954 context.is_table()
1955 })
1956 };
1957 match justify_self {
1958 AlignFlags::STRETCH => Size::Stretch,
1959 AlignFlags::NORMAL if normal_stretches() => Size::Stretch,
1960 _ => Size::FitContent,
1961 }
1962}
1963
1964fn justify_self_alignment(
1971 containing_block: &ContainingBlock,
1972 free_space: Au,
1973 justify_self: AlignFlags,
1974) -> Au {
1975 let mut alignment = justify_self.value();
1976 let is_safe = justify_self.flags() == AlignFlags::SAFE || alignment == AlignFlags::NORMAL;
1977 if is_safe && free_space <= Au::zero() {
1978 alignment = AlignFlags::START
1979 }
1980 match alignment {
1981 AlignFlags::NORMAL => {},
1982 AlignFlags::CENTER => return free_space / 2,
1983 AlignFlags::END => return free_space,
1984 _ => return Au::zero(),
1985 }
1986
1987 let style = containing_block.style;
1989 match style.clone_text_align() {
1990 TextAlignKeyword::MozCenter => free_space / 2,
1991 TextAlignKeyword::MozLeft if !style.writing_mode.line_left_is_inline_start() => free_space,
1992 TextAlignKeyword::MozRight if style.writing_mode.line_left_is_inline_start() => free_space,
1993 _ => Au::zero(),
1994 }
1995}
1996
1997fn solve_inline_margins_for_in_flow_block_level(
2010 containing_block: &ContainingBlock,
2011 pbm: &PaddingBorderMargin,
2012 inline_size: Au,
2013 justify_self: AlignFlags,
2014) -> ((Au, Au), Au) {
2015 let free_space = containing_block.size.inline - pbm.padding_border_sums.inline - inline_size;
2016 let mut justification = Au::zero();
2017 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
2018 (AuOrAuto::Auto, AuOrAuto::Auto) => {
2019 let start = Au::zero().max(free_space / 2);
2020 (start, free_space - start)
2021 },
2022 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
2023 (Au::zero().max(free_space - end), end)
2024 },
2025 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, free_space - start),
2026 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
2027 justification =
2032 justify_self_alignment(containing_block, free_space - start - end, justify_self);
2033 (start, end)
2034 },
2035 };
2036 let effective_margin_inline_start = inline_margins.0 + justification;
2037 (inline_margins, effective_margin_inline_start)
2038}
2039
2040fn solve_inline_margins_avoiding_floats(
2050 sequential_layout_state: &SequentialLayoutState,
2051 containing_block: &ContainingBlock,
2052 pbm: &PaddingBorderMargin,
2053 inline_size: Au,
2054 placement_rect: LogicalRect<Au>,
2055 justify_self: AlignFlags,
2056) -> ((Au, Au), Au) {
2057 let free_space = Au::zero().max(placement_rect.size.inline - inline_size);
2061 let cb_info = &sequential_layout_state.floats.containing_block_info;
2062 let start_adjustment = placement_rect.start_corner.inline - cb_info.inline_start;
2063 let end_adjustment = cb_info.inline_end - placement_rect.max_inline_position();
2064 let mut justification = Au::zero();
2065 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
2066 (AuOrAuto::Auto, AuOrAuto::Auto) => {
2067 let half = free_space / 2;
2068 (start_adjustment + half, end_adjustment + free_space - half)
2069 },
2070 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (start_adjustment + free_space, end),
2071 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, end_adjustment + free_space),
2072 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
2073 justification = justify_self_alignment(containing_block, free_space, justify_self);
2079 (start, end)
2080 },
2081 };
2082 let effective_margin_inline_start = inline_margins.0.max(start_adjustment) + justification;
2083 (inline_margins, effective_margin_inline_start)
2084}
2085
2086struct PlacementState<'container> {
2091 next_in_flow_margin_collapses_with_parent_start_margin: bool,
2092 last_in_flow_margin_collapses_with_parent_end_margin: bool,
2093 start_margin: CollapsedMargin,
2094 current_margin: CollapsedMargin,
2095 current_block_direction_position: Au,
2096 inflow_baselines: Baselines,
2097 is_inline_block_context: bool,
2098
2099 marker_block_size: Option<Au>,
2104
2105 containing_block: &'container ContainingBlock<'container>,
2108}
2109
2110impl<'container> PlacementState<'container> {
2111 fn new(
2112 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2113 containing_block: &'container ContainingBlock<'container>,
2114 ) -> PlacementState<'container> {
2115 let is_inline_block_context =
2116 containing_block.style.get_box().clone_display() == Display::InlineBlock;
2117 PlacementState {
2118 next_in_flow_margin_collapses_with_parent_start_margin:
2119 collapsible_with_parent_start_margin.0,
2120 last_in_flow_margin_collapses_with_parent_end_margin: true,
2121 start_margin: CollapsedMargin::zero(),
2122 current_margin: CollapsedMargin::zero(),
2123 current_block_direction_position: Au::zero(),
2124 inflow_baselines: Baselines::default(),
2125 is_inline_block_context,
2126 marker_block_size: None,
2127 containing_block,
2128 }
2129 }
2130
2131 fn place_fragment_and_update_baseline(
2132 &mut self,
2133 fragment: &mut Fragment,
2134 sequential_layout_state: Option<&mut SequentialLayoutState>,
2135 ) {
2136 self.place_fragment(fragment, sequential_layout_state);
2137
2138 let box_fragment = match fragment {
2139 Fragment::Box(box_fragment) => box_fragment,
2140 _ => return,
2141 };
2142 let box_fragment = box_fragment.borrow();
2143
2144 if self.is_inline_block_context && box_fragment.is_table_wrapper() {
2149 return;
2150 }
2151
2152 let box_block_offset = box_fragment
2153 .content_rect()
2154 .origin
2155 .to_logical(self.containing_block)
2156 .block;
2157 let box_fragment_baselines =
2158 box_fragment.baselines(self.containing_block.style.writing_mode);
2159 if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) {
2160 self.inflow_baselines.first = Some(first + box_block_offset);
2161 }
2162 if let Some(last) = box_fragment_baselines.last {
2163 self.inflow_baselines.last = Some(last + box_block_offset);
2164 }
2165 }
2166
2167 fn place_fragment(
2170 &mut self,
2171 fragment: &mut Fragment,
2172 sequential_layout_state: Option<&mut SequentialLayoutState>,
2173 ) {
2174 match fragment {
2175 Fragment::Box(fragment) => {
2176 let fragment = &mut *fragment.borrow_mut();
2185 let is_outside_marker = fragment
2186 .base
2187 .flags
2188 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER);
2189 if is_outside_marker {
2190 assert!(self.marker_block_size.is_none());
2191 self.marker_block_size = Some(
2192 fragment
2193 .content_rect()
2194 .size
2195 .to_logical(self.containing_block.style.writing_mode)
2196 .block,
2197 );
2198 return;
2199 }
2200
2201 let BlockLevelLayoutInfo {
2202 clearance,
2203 block_margins_collapsed_with_children: fragment_block_margins,
2204 } = &**fragment
2205 .block_level_layout_info
2206 .as_ref()
2207 .expect("A block-level fragment should have a BlockLevelLayoutInfo.");
2208 let mut fragment_block_size = fragment
2209 .border_rect()
2210 .size
2211 .to_logical(self.containing_block.style.writing_mode)
2212 .block;
2213
2214 if let Some(clearance) = *clearance {
2220 fragment_block_size += clearance;
2221 self.current_block_direction_position += self.current_margin.solve();
2226 self.current_margin = CollapsedMargin::zero();
2227 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2228 if fragment_block_margins.collapsed_through {
2229 self.last_in_flow_margin_collapses_with_parent_end_margin = false;
2230 }
2231 } else if !fragment_block_margins.collapsed_through {
2232 self.last_in_flow_margin_collapses_with_parent_end_margin = true;
2233 }
2234
2235 if self.next_in_flow_margin_collapses_with_parent_start_margin {
2236 debug_assert!(self.current_margin.solve().is_zero());
2237 self.start_margin
2238 .adjoin_assign(&fragment_block_margins.start);
2239 if fragment_block_margins.collapsed_through {
2240 self.start_margin.adjoin_assign(&fragment_block_margins.end);
2241 return;
2242 }
2243 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2244 } else {
2245 self.current_margin
2246 .adjoin_assign(&fragment_block_margins.start);
2247 }
2248
2249 fragment.base.rect.origin += LogicalVec2 {
2250 inline: Au::zero(),
2251 block: self.current_margin.solve() + self.current_block_direction_position,
2252 }
2253 .to_physical_size(self.containing_block.style.writing_mode);
2254
2255 if fragment_block_margins.collapsed_through {
2256 self.current_block_direction_position += fragment_block_size;
2259 self.current_margin
2260 .adjoin_assign(&fragment_block_margins.end);
2261 } else {
2262 self.current_block_direction_position +=
2263 self.current_margin.solve() + fragment_block_size;
2264 self.current_margin = fragment_block_margins.end;
2265 }
2266 },
2267 Fragment::AbsoluteOrFixedPositioned(fragment) => {
2268 fragment.borrow_mut().original_static_position_rect = LogicalRect {
2271 start_corner: LogicalVec2 {
2272 block: (self.current_margin.solve() +
2273 self.current_block_direction_position),
2274 inline: Au::zero(),
2275 },
2276 size: LogicalVec2::zero(),
2277 }
2278 .as_physical(Some(self.containing_block));
2279 },
2280 Fragment::Float(box_fragment) => {
2281 let sequential_layout_state = sequential_layout_state
2282 .expect("Found float fragment without SequentialLayoutState");
2283 let block_offset_from_containing_block_top =
2284 self.current_block_direction_position + self.current_margin.solve();
2285 let box_fragment = &mut *box_fragment.borrow_mut();
2286 sequential_layout_state.place_float_fragment(
2287 box_fragment,
2288 self.containing_block,
2289 self.start_margin,
2290 block_offset_from_containing_block_top,
2291 );
2292 },
2293 Fragment::Positioning(_) => {},
2294 _ => unreachable!(),
2295 }
2296 }
2297
2298 fn finish(mut self) -> (Au, CollapsedBlockMargins, Baselines) {
2299 if !self.last_in_flow_margin_collapses_with_parent_end_margin {
2300 self.current_block_direction_position += self.current_margin.solve();
2301 self.current_margin = CollapsedMargin::zero();
2302 }
2303 let (total_block_size, collapsed_through) = match self.marker_block_size {
2304 Some(marker_block_size) => (
2305 self.current_block_direction_position.max(marker_block_size),
2306 false,
2309 ),
2310 None => (
2311 self.current_block_direction_position,
2312 self.next_in_flow_margin_collapses_with_parent_start_margin,
2313 ),
2314 };
2315
2316 (
2317 total_block_size,
2318 CollapsedBlockMargins {
2319 collapsed_through,
2320 start: self.start_margin,
2321 end: self.current_margin,
2322 },
2323 self.inflow_baselines,
2324 )
2325 }
2326}
2327
2328pub(crate) struct IndependentFloatOrAtomicLayoutResult {
2329 pub fragment: BoxFragment,
2330 pub baselines: Baselines,
2331 pub pbm_sums: LogicalSides<Au>,
2332}
2333
2334impl IndependentFormattingContext {
2335 pub(crate) fn layout_float_or_atomic_inline(
2336 &self,
2337 layout_context: &LayoutContext,
2338 child_positioning_context: &mut PositioningContext,
2339 containing_block: &ContainingBlock,
2340 ) -> IndependentFloatOrAtomicLayoutResult {
2341 let style = self.style();
2342 let container_writing_mode = containing_block.style.writing_mode;
2343 let layout_style = self.layout_style();
2344 let content_box_sizes_and_pbm =
2345 layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
2346 let pbm = &content_box_sizes_and_pbm.pbm;
2347 let margin = pbm.margin.auto_is(Au::zero);
2348 let pbm_sums = pbm.padding + pbm.border + margin;
2349 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
2350 let is_table = self.is_table();
2351
2352 let available_inline_size =
2353 Au::zero().max(containing_block.size.inline - pbm_sums.inline_sum());
2354 let available_block_size = containing_block
2355 .size
2356 .block
2357 .to_definite()
2358 .map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum()));
2359
2360 let tentative_block_content_size =
2361 self.tentative_block_content_size(preferred_aspect_ratio, available_inline_size);
2362 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
2363 SizeConstraint::Definite(content_box_sizes_and_pbm.content_box_sizes.block.resolve(
2364 Direction::Block,
2365 Size::FitContent,
2366 Au::zero,
2367 available_block_size,
2368 || block_content_size,
2369 is_table,
2370 ))
2371 } else {
2372 content_box_sizes_and_pbm
2373 .content_box_sizes
2374 .block
2375 .resolve_extrinsic(Size::FitContent, Au::zero(), available_block_size)
2376 };
2377
2378 let get_content_size = || {
2379 let constraint_space =
2380 ConstraintSpace::new(tentative_block_size, style, preferred_aspect_ratio);
2381 self.inline_content_sizes(layout_context, &constraint_space)
2382 .sizes
2383 };
2384
2385 let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
2386 Direction::Inline,
2387 Size::FitContent,
2388 Au::zero,
2389 Some(available_inline_size),
2390 get_content_size,
2391 is_table,
2392 );
2393
2394 let containing_block_for_children = ContainingBlock {
2395 size: ContainingBlockSize {
2396 inline: inline_size,
2397 block: tentative_block_size,
2398 },
2399 style,
2400 };
2401 assert_eq!(
2402 container_writing_mode.is_horizontal(),
2403 style.writing_mode.is_horizontal(),
2404 "Mixed horizontal and vertical writing modes are not supported yet"
2405 );
2406
2407 let lazy_block_size = LazySize::new(
2408 &content_box_sizes_and_pbm.content_box_sizes.block,
2409 Direction::Block,
2410 Size::FitContent,
2411 Au::zero,
2412 available_block_size,
2413 is_table,
2414 );
2415
2416 let CacheableLayoutResult {
2417 content_inline_size_for_table,
2418 content_block_size,
2419 fragments,
2420 baselines,
2421 specific_layout_info,
2422 ..
2423 } = self.layout(
2424 layout_context,
2425 child_positioning_context,
2426 &containing_block_for_children,
2427 containing_block,
2428 preferred_aspect_ratio,
2429 &lazy_block_size,
2430 );
2431
2432 let content_size = LogicalVec2 {
2433 inline: content_inline_size_for_table.unwrap_or(inline_size),
2434 block: lazy_block_size.resolve(|| content_block_size),
2435 }
2436 .to_physical_size(container_writing_mode);
2437 let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size);
2438
2439 let mut base_fragment_info = self.base_fragment_info();
2440 if content_box_sizes_and_pbm.depends_on_block_constraints {
2441 base_fragment_info.flags.insert(
2442 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2443 );
2444 }
2445
2446 let fragment = BoxFragment::new(
2450 base_fragment_info,
2451 style.clone(),
2452 fragments,
2453 content_rect,
2454 pbm.padding.to_physical(container_writing_mode),
2455 pbm.border.to_physical(container_writing_mode),
2456 margin.to_physical(container_writing_mode),
2457 specific_layout_info,
2458 );
2459
2460 IndependentFloatOrAtomicLayoutResult {
2461 fragment,
2462 baselines,
2463 pbm_sums,
2464 }
2465 }
2466}