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;
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::{IndependentFormattingContextLayoutResult, 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: &ServoLayoutNode,
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: &ServoLayoutNode,
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, MallocSizeOf, PartialEq)]
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: &ServoLayoutNode,
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 ) -> IndependentFormattingContextLayoutResult {
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 IndependentFormattingContextLayoutResult {
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: &ServoLayoutNode,
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 ) -> IndependentFormattingContextLayoutResult {
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) -> IndependentFormattingContextLayoutResult {
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 IndependentFormattingContextLayoutResult {
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 layout_in_flow_non_replaced_block_level_same_formatting_context_cached(
881 layout_context,
882 positioning_context,
883 containing_block,
884 sequential_layout_state,
885 collapsible_with_parent_start_margin,
886 ignore_block_margins_for_stretch,
887 has_inline_parent,
888 base,
889 contents,
890 ),
891 ),
892 BlockLevelBox::Independent(independent) => Fragment::Box(ArcRefCell::new(
893 positioning_context.layout_maybe_position_relative_fragment(
894 layout_context,
895 containing_block,
896 &independent.base,
897 |positioning_context| {
898 independent.layout_in_flow_block_level(
899 layout_context,
900 positioning_context,
901 containing_block,
902 sequential_layout_state,
903 ignore_block_margins_for_stretch,
904 has_inline_parent,
905 )
906 },
907 ),
908 )),
909 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
910 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
914 box_.clone(),
915 PhysicalRect::zero(),
919 LogicalVec2 {
920 inline: AlignFlags::START,
921 block: AlignFlags::START,
922 },
923 containing_block.style.writing_mode,
924 );
925 let hoisted_fragment = hoisted_box.fragment.clone();
926 positioning_context.push(hoisted_box);
927 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
928 },
929 BlockLevelBox::OutOfFlowFloatBox(float_box) => Fragment::Float(ArcRefCell::new(
930 float_box.layout(layout_context, positioning_context, containing_block),
931 )),
932 BlockLevelBox::OutsideMarker(outside_marker) => {
933 outside_marker.layout(layout_context, containing_block, positioning_context)
934 },
935 };
936
937 self.with_base(|base| base.set_fragment(fragment.clone()));
938
939 fragment
940 }
941
942 fn inline_content_sizes(
943 &self,
944 layout_context: &LayoutContext,
945 constraint_space: &ConstraintSpace,
946 ) -> InlineContentSizesResult {
947 let independent_formatting_context = match self {
948 BlockLevelBox::Independent(independent) => independent,
949 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => &box_.borrow().context,
950 BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents,
951 BlockLevelBox::OutsideMarker(outside_marker) => &outside_marker.context,
952 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
953 return base.inline_content_sizes(layout_context, constraint_space, contents);
954 },
955 };
956 independent_formatting_context.inline_content_sizes(layout_context, constraint_space)
957 }
958}
959
960#[allow(clippy::too_many_arguments)]
967fn layout_in_flow_non_replaced_block_level_same_formatting_context_cached(
968 layout_context: &LayoutContext<'_>,
969 positioning_context: &mut PositioningContext,
970 containing_block: &ContainingBlock<'_>,
971 sequential_layout_state: Option<&mut SequentialLayoutState>,
972 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
973 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
974 has_inline_parent: bool,
975 base: &LayoutBoxBase,
976 contents: &BlockContainer,
977) -> ArcRefCell<BoxFragment> {
978 let mut allows_caching = sequential_layout_state.is_none();
979
980 if allows_caching {
981 if let Some(cached_result) = base.cached_same_formatting_context_block_if_applicable(
982 containing_block,
983 collapsible_with_parent_start_margin,
984 ignore_block_margins_for_stretch,
985 has_inline_parent,
986 ) {
987 return cached_result;
988 };
989 }
990
991 let positioning_context_length = positioning_context.len();
992 let fragment = ArcRefCell::new(positioning_context.layout_maybe_position_relative_fragment(
993 layout_context,
994 containing_block,
995 base,
996 |positioning_context| {
997 layout_in_flow_non_replaced_block_level_same_formatting_context(
998 layout_context,
999 positioning_context,
1000 containing_block,
1001 base,
1002 contents,
1003 sequential_layout_state,
1004 collapsible_with_parent_start_margin,
1005 ignore_block_margins_for_stretch,
1006 has_inline_parent,
1007 )
1008 },
1009 ));
1010
1011 allows_caching = allows_caching && positioning_context_length == positioning_context.len();
1017
1018 if !allows_caching {
1019 base.clear_fragments_and_fragment_cache();
1020 } else {
1021 base.cache_same_formatting_context_block_layout(
1022 containing_block,
1023 collapsible_with_parent_start_margin,
1024 ignore_block_margins_for_stretch,
1025 has_inline_parent,
1026 fragment.clone(),
1027 );
1028 }
1029
1030 fragment
1031}
1032
1033#[allow(clippy::too_many_arguments)]
1039fn layout_in_flow_non_replaced_block_level_same_formatting_context(
1040 layout_context: &LayoutContext,
1041 positioning_context: &mut PositioningContext,
1042 containing_block: &ContainingBlock,
1043 base: &LayoutBoxBase,
1044 contents: &BlockContainer,
1045 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
1046 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
1047 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1048 has_inline_parent: bool,
1049) -> BoxFragment {
1050 let style = &base.style;
1051 let layout_style = contents.layout_style(base);
1052 let containing_block_writing_mode = containing_block.style.writing_mode;
1053 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1054 base.inline_content_sizes(layout_context, constraint_space, contents)
1055 .sizes
1056 };
1057 let ContainingBlockPaddingAndBorder {
1058 containing_block: containing_block_for_children,
1059 pbm,
1060 block_sizes,
1061 depends_on_block_constraints,
1062 available_block_size,
1063 justify_self,
1064 ..
1065 } = solve_containing_block_padding_and_border_for_in_flow_box(
1066 containing_block,
1067 &layout_style,
1068 get_inline_content_sizes,
1069 ignore_block_margins_for_stretch,
1070 None,
1071 has_inline_parent,
1072 );
1073 let ResolvedMargins {
1074 margin,
1075 effective_margin_inline_start,
1076 } = solve_margins(
1077 containing_block,
1078 &pbm,
1079 containing_block_for_children.size.inline,
1080 justify_self,
1081 );
1082
1083 let start_margin_can_collapse_with_children =
1084 pbm.padding.block_start.is_zero() && pbm.border.block_start.is_zero();
1085
1086 let mut clearance = None;
1087 let parent_containing_block_position_info;
1088 match sequential_layout_state {
1089 None => parent_containing_block_position_info = None,
1090 Some(ref mut sequential_layout_state) => {
1091 let clear =
1092 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode);
1093 let mut block_start_margin = CollapsedMargin::new(margin.block_start);
1094
1095 let collapsible_with_parent_start_margin = collapsible_with_parent_start_margin.expect(
1107 "We should know whether we are collapsing the block start margin with the parent \
1108 when laying out sequentially",
1109 ).0 && clear == Clear::None;
1110 if !collapsible_with_parent_start_margin && start_margin_can_collapse_with_children {
1111 contents.find_block_margin_collapsing_with_parent(
1112 layout_context,
1113 &mut block_start_margin,
1114 &containing_block_for_children,
1115 );
1116 }
1117
1118 clearance = sequential_layout_state.calculate_clearance(clear, &block_start_margin);
1120 if clearance.is_some() {
1121 sequential_layout_state.commit_margin();
1122 }
1123 sequential_layout_state.adjoin_assign(&block_start_margin);
1124 if !start_margin_can_collapse_with_children {
1125 sequential_layout_state.commit_margin();
1126 }
1127
1128 sequential_layout_state.advance_block_position(
1131 pbm.padding.block_start +
1132 pbm.border.block_start +
1133 clearance.unwrap_or_else(Au::zero),
1134 );
1135
1136 let inline_start = sequential_layout_state
1142 .floats
1143 .containing_block_info
1144 .inline_start +
1145 pbm.padding.inline_start +
1146 pbm.border.inline_start +
1147 effective_margin_inline_start;
1148 let new_cb_offsets = ContainingBlockPositionInfo {
1149 block_start: sequential_layout_state.bfc_relative_block_position,
1150 block_start_margins_not_collapsed: sequential_layout_state.current_margin,
1151 inline_start,
1152 inline_end: inline_start + containing_block_for_children.size.inline,
1153 };
1154 parent_containing_block_position_info = Some(
1155 sequential_layout_state.replace_containing_block_position_info(new_cb_offsets),
1156 );
1157 },
1158 };
1159
1160 let ignore_block_margins_for_stretch = LogicalSides1D::new(
1166 pbm.border.block_start.is_zero() && pbm.padding.block_start.is_zero(),
1167 pbm.border.block_end.is_zero() && pbm.padding.block_end.is_zero(),
1168 );
1169
1170 let flow_layout = contents.layout(
1171 layout_context,
1172 positioning_context,
1173 &containing_block_for_children,
1174 sequential_layout_state.as_deref_mut(),
1175 CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
1176 ignore_block_margins_for_stretch,
1177 );
1178 let mut content_block_size = flow_layout.content_block_size;
1179
1180 let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1182 let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
1183 if start_margin_can_collapse_with_children {
1184 block_margins_collapsed_with_children
1185 .start
1186 .adjoin_assign(&collapsible_margins_in_children.start);
1187 if collapsible_margins_in_children.collapsed_through {
1188 block_margins_collapsed_with_children
1189 .start
1190 .adjoin_assign(&std::mem::replace(
1191 &mut collapsible_margins_in_children.end,
1192 CollapsedMargin::zero(),
1193 ));
1194 }
1195 }
1196
1197 let is_anonymous = matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
1198 let tentative_block_size = if is_anonymous {
1199 &Default::default()
1203 } else {
1204 &containing_block_for_children.size.block
1205 };
1206 let collapsed_through = collapsible_margins_in_children.collapsed_through &&
1207 pbm.padding_border_sums.block.is_zero() &&
1208 tentative_block_size.definite_or_min().is_zero();
1209 block_margins_collapsed_with_children.collapsed_through = collapsed_through;
1210
1211 let end_margin_can_collapse_with_children =
1212 pbm.padding.block_end.is_zero() && pbm.border.block_end.is_zero();
1213 if !end_margin_can_collapse_with_children {
1214 content_block_size += collapsible_margins_in_children.end.solve();
1215 }
1216
1217 let block_size = block_sizes.resolve(
1218 Direction::Block,
1219 Size::FitContent,
1220 Au::zero,
1221 available_block_size,
1222 || content_block_size.into(),
1223 false, );
1225
1226 let end_margin_can_collapse_with_children = end_margin_can_collapse_with_children &&
1241 block_size == content_block_size &&
1242 (collapsed_through || !tentative_block_size.is_definite());
1243 if end_margin_can_collapse_with_children {
1244 block_margins_collapsed_with_children
1245 .end
1246 .adjoin_assign(&collapsible_margins_in_children.end);
1247 }
1248
1249 if let Some(ref mut sequential_layout_state) = sequential_layout_state {
1250 sequential_layout_state
1253 .replace_containing_block_position_info(parent_containing_block_position_info.unwrap());
1254
1255 sequential_layout_state.advance_block_position(
1265 block_size - content_block_size + pbm.padding.block_end + pbm.border.block_end,
1266 );
1267
1268 if !end_margin_can_collapse_with_children {
1269 sequential_layout_state.commit_margin();
1270 }
1271 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1272 }
1273
1274 let content_rect = LogicalRect {
1275 start_corner: LogicalVec2 {
1276 block: (pbm.padding.block_start +
1277 pbm.border.block_start +
1278 clearance.unwrap_or_else(Au::zero)),
1279 inline: pbm.padding.inline_start +
1280 pbm.border.inline_start +
1281 effective_margin_inline_start,
1282 },
1283 size: LogicalVec2 {
1284 block: block_size,
1285 inline: containing_block_for_children.size.inline,
1286 },
1287 };
1288
1289 let mut base_fragment_info = base.base_fragment_info;
1290
1291 if depends_on_block_constraints || (is_anonymous && flow_layout.depends_on_block_constraints) {
1295 base_fragment_info
1296 .flags
1297 .insert(FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM);
1298 }
1299
1300 BoxFragment::new(
1301 base_fragment_info,
1302 style.clone(),
1303 flow_layout.fragments,
1304 content_rect.as_physical(Some(containing_block)),
1305 pbm.padding.to_physical(containing_block_writing_mode),
1306 pbm.border.to_physical(containing_block_writing_mode),
1307 margin.to_physical(containing_block_writing_mode),
1308 flow_layout.specific_layout_info,
1309 )
1310 .with_baselines(flow_layout.baselines)
1311 .with_block_level_layout_info(block_margins_collapsed_with_children, clearance)
1312}
1313
1314impl IndependentFormattingContext {
1315 pub(crate) fn layout_in_flow_block_level(
1323 &self,
1324 layout_context: &LayoutContext,
1325 positioning_context: &mut PositioningContext,
1326 containing_block: &ContainingBlock,
1327 sequential_layout_state: Option<&mut SequentialLayoutState>,
1328 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1329 has_inline_parent: bool,
1330 ) -> BoxFragment {
1331 if let Some(sequential_layout_state) = sequential_layout_state {
1332 return self.layout_in_flow_block_level_sequentially(
1333 layout_context,
1334 positioning_context,
1335 containing_block,
1336 sequential_layout_state,
1337 ignore_block_margins_for_stretch,
1338 has_inline_parent,
1339 );
1340 }
1341
1342 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1343 self.inline_content_sizes(layout_context, constraint_space)
1344 .sizes
1345 };
1346 let layout_style = self.layout_style();
1347 let ContainingBlockPaddingAndBorder {
1348 containing_block: containing_block_for_children,
1349 pbm,
1350 block_sizes,
1351 depends_on_block_constraints,
1352 available_block_size,
1353 justify_self,
1354 preferred_aspect_ratio,
1355 } = solve_containing_block_padding_and_border_for_in_flow_box(
1356 containing_block,
1357 &layout_style,
1358 get_inline_content_sizes,
1359 ignore_block_margins_for_stretch,
1360 Some(self),
1361 has_inline_parent,
1362 );
1363
1364 let lazy_block_size = LazySize::new(
1365 &block_sizes,
1366 Direction::Block,
1367 Size::FitContent,
1368 Au::zero,
1369 available_block_size,
1370 layout_style.is_table(),
1371 );
1372
1373 let layout = self.layout(
1374 layout_context,
1375 positioning_context,
1376 &containing_block_for_children,
1377 containing_block,
1378 preferred_aspect_ratio,
1379 &lazy_block_size,
1380 );
1381
1382 let inline_size = layout
1383 .content_inline_size_for_table
1384 .unwrap_or(containing_block_for_children.size.inline);
1385 let block_size = lazy_block_size.resolve(|| layout.content_block_size);
1386
1387 let ResolvedMargins {
1388 margin,
1389 effective_margin_inline_start,
1390 } = solve_margins(containing_block, &pbm, inline_size, justify_self);
1391
1392 let content_rect = LogicalRect {
1393 start_corner: LogicalVec2 {
1394 block: pbm.padding.block_start + pbm.border.block_start,
1395 inline: pbm.padding.inline_start +
1396 pbm.border.inline_start +
1397 effective_margin_inline_start,
1398 },
1399 size: LogicalVec2 {
1400 block: block_size,
1401 inline: inline_size,
1402 },
1403 };
1404
1405 let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1406 let containing_block_writing_mode = containing_block.style.writing_mode;
1407
1408 let mut base_fragment_info = self.base.base_fragment_info;
1409 if depends_on_block_constraints {
1410 base_fragment_info.flags.insert(
1411 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1412 );
1413 }
1414 BoxFragment::new(
1415 base_fragment_info,
1416 self.base.style.clone(),
1417 layout.fragments,
1418 content_rect.as_physical(Some(containing_block)),
1419 pbm.padding.to_physical(containing_block_writing_mode),
1420 pbm.border.to_physical(containing_block_writing_mode),
1421 margin.to_physical(containing_block_writing_mode),
1422 layout.specific_layout_info,
1423 )
1424 .with_baselines(layout.baselines)
1425 .with_block_level_layout_info(block_margins_collapsed_with_children, None)
1426 }
1427
1428 fn layout_in_flow_block_level_sequentially(
1432 &self,
1433 layout_context: &LayoutContext<'_>,
1434 positioning_context: &mut PositioningContext,
1435 containing_block: &ContainingBlock<'_>,
1436 sequential_layout_state: &mut SequentialLayoutState,
1437 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1438 has_inline_parent: bool,
1439 ) -> BoxFragment {
1440 let style = &self.base.style;
1441 let containing_block_writing_mode = containing_block.style.writing_mode;
1442 let ContentBoxSizesAndPBM {
1443 content_box_sizes,
1444 pbm,
1445 depends_on_block_constraints,
1446 ..
1447 } = self
1448 .layout_style()
1449 .content_box_sizes_and_padding_border_margin(&containing_block.into());
1450
1451 let (margin_block_start, margin_block_end) =
1452 solve_block_margins_for_in_flow_block_level(&pbm);
1453 let collapsed_margin_block_start = CollapsedMargin::new(margin_block_start);
1454
1455 let mut content_size;
1466 let mut layout;
1467 let mut placement_rect;
1468
1469 let clear_position = sequential_layout_state.calculate_clear_position(
1473 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode),
1474 &collapsed_margin_block_start,
1475 );
1476 let ceiling = clear_position.unwrap_or_else(|| {
1477 sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
1478 });
1479
1480 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1482 let available_block_size = containing_block
1483 .size
1484 .block
1485 .to_definite()
1486 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1487 let is_table = self.is_table();
1488 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
1489
1490 #[derive(Default)]
1491 struct Cache {
1492 min_block_size: Au,
1493 max_block_size: Option<Au>,
1494 tentative_block_size: SizeConstraint,
1495 depends_on_stretch_size: bool,
1496 }
1497 let mut cache = Cache {
1498 depends_on_stretch_size: true,
1499 ..Default::default()
1500 };
1501
1502 let update_cache = |cache: &mut Cache, stretch_size| {
1503 let tentative_block_content_size = self
1504 .tentative_block_content_size_with_dependency(preferred_aspect_ratio, stretch_size);
1505 let (preferred_block_size, min_block_size, max_block_size, depends_on_stretch_size) =
1506 if let Some(result) = tentative_block_content_size {
1507 let (block_content_size, depends_on_stretch_size) = result;
1508 let (preferred, min, max) = content_box_sizes.block.resolve_each(
1509 Size::FitContent,
1510 Au::zero,
1511 available_block_size,
1512 || block_content_size,
1513 is_table,
1514 );
1515 (Some(preferred), min, max, depends_on_stretch_size)
1516 } else {
1517 let (preferred, min, max) = content_box_sizes.block.resolve_each_extrinsic(
1518 Size::FitContent,
1519 Au::zero(),
1520 available_block_size,
1521 );
1522 (preferred, min, max, false)
1523 };
1524 cache.min_block_size = min_block_size;
1525 cache.max_block_size = max_block_size;
1526 cache.tentative_block_size =
1527 SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
1528 cache.depends_on_stretch_size = depends_on_stretch_size;
1529 };
1530
1531 let get_inline_content_sizes = |cache: &Cache| {
1533 let constraint_space =
1534 ConstraintSpace::new(cache.tentative_block_size, style, preferred_aspect_ratio);
1535 self.inline_content_sizes(layout_context, &constraint_space)
1536 .sizes
1537 };
1538
1539 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1540 let automatic_inline_size = automatic_inline_size(justify_self, Some(self));
1541 let compute_inline_size = |cache: &mut Cache, stretch_size| {
1542 if cache.depends_on_stretch_size {
1543 update_cache(cache, stretch_size);
1544 }
1545 content_box_sizes.inline.resolve(
1546 Direction::Inline,
1547 automatic_inline_size,
1548 Au::zero,
1549 Some(stretch_size),
1550 || get_inline_content_sizes(cache),
1551 is_table,
1552 )
1553 };
1554
1555 let get_lazy_block_size = || {
1556 LazySize::new(
1557 &content_box_sizes.block,
1558 Direction::Block,
1559 Size::FitContent,
1560 Au::zero,
1561 available_block_size,
1562 is_table,
1563 )
1564 };
1565
1566 let inline_size_with_max_available_space = compute_inline_size(&mut cache, MAX_AU);
1575 let inline_size_with_no_available_space = compute_inline_size(&mut cache, Au::zero());
1576 if inline_size_with_no_available_space == inline_size_with_max_available_space {
1577 let inline_size = inline_size_with_no_available_space;
1581 let lazy_block_size = get_lazy_block_size();
1582 layout = self.layout(
1583 layout_context,
1584 positioning_context,
1585 &ContainingBlock {
1586 size: ContainingBlockSize {
1587 inline: inline_size,
1588 block: cache.tentative_block_size,
1592 },
1593 style,
1594 },
1595 containing_block,
1596 preferred_aspect_ratio,
1597 &lazy_block_size,
1598 );
1599
1600 content_size = LogicalVec2 {
1601 block: lazy_block_size.resolve(|| layout.content_block_size),
1602 inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
1603 };
1604
1605 let mut placement = PlacementAmongFloats::new(
1606 &sequential_layout_state.floats,
1607 ceiling,
1608 content_size + pbm.padding_border_sums,
1609 &pbm,
1610 );
1611 placement_rect = placement.place();
1612 } else {
1613 let minimum_size_of_block = LogicalVec2 {
1619 inline: inline_size_with_no_available_space,
1623 block: match cache.tentative_block_size {
1631 SizeConstraint::Definite(size) if cache.max_block_size.is_some() => size,
1634 _ => cache.min_block_size,
1637 },
1638 } + pbm.padding_border_sums;
1639 let mut placement = PlacementAmongFloats::new(
1640 &sequential_layout_state.floats,
1641 ceiling,
1642 minimum_size_of_block,
1643 &pbm,
1644 );
1645
1646 loop {
1647 placement_rect = placement.place();
1649 let available_inline_size =
1650 placement_rect.size.inline - pbm.padding_border_sums.inline;
1651 let proposed_inline_size = compute_inline_size(&mut cache, available_inline_size);
1652
1653 let positioning_context_length = positioning_context.len();
1657 let lazy_block_size = get_lazy_block_size();
1658 layout = self.layout(
1659 layout_context,
1660 positioning_context,
1661 &ContainingBlock {
1662 size: ContainingBlockSize {
1663 inline: proposed_inline_size,
1664 block: cache.tentative_block_size,
1665 },
1666 style,
1667 },
1668 containing_block,
1669 preferred_aspect_ratio,
1670 &lazy_block_size,
1671 );
1672
1673 let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
1674 debug_assert!(inline_size < proposed_inline_size);
1679 inline_size
1680 } else {
1681 proposed_inline_size
1682 };
1683 content_size = LogicalVec2 {
1684 block: lazy_block_size.resolve(|| layout.content_block_size),
1685 inline: inline_size,
1686 };
1687
1688 if placement.try_to_expand_for_auto_block_size(
1692 content_size.block + pbm.padding_border_sums.block,
1693 &placement_rect.size,
1694 ) {
1695 break;
1696 }
1697
1698 positioning_context.truncate(&positioning_context_length);
1702 }
1703 }
1704
1705 let has_clearance = clear_position.is_some() || placement_rect.start_corner.block > ceiling;
1709 let clearance = has_clearance.then(|| {
1710 placement_rect.start_corner.block -
1711 sequential_layout_state
1712 .position_with_zero_clearance(&collapsed_margin_block_start)
1713 });
1714
1715 let ((margin_inline_start, margin_inline_end), effective_margin_inline_start) =
1716 solve_inline_margins_avoiding_floats(
1717 sequential_layout_state,
1718 containing_block,
1719 &pbm,
1720 content_size.inline + pbm.padding_border_sums.inline,
1721 placement_rect,
1722 justify_self,
1723 );
1724
1725 let margin = LogicalSides {
1726 inline_start: margin_inline_start,
1727 inline_end: margin_inline_end,
1728 block_start: margin_block_start,
1729 block_end: margin_block_end,
1730 };
1731
1732 if clearance.is_some() {
1735 sequential_layout_state.commit_margin();
1736 }
1737 sequential_layout_state.adjoin_assign(&collapsed_margin_block_start);
1738
1739 sequential_layout_state.commit_margin();
1741 sequential_layout_state.advance_block_position(
1742 pbm.padding_border_sums.block + content_size.block + clearance.unwrap_or_else(Au::zero),
1743 );
1744 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1745
1746 let content_rect = LogicalRect {
1747 start_corner: LogicalVec2 {
1748 block: pbm.padding.block_start +
1749 pbm.border.block_start +
1750 clearance.unwrap_or_else(Au::zero),
1751 inline: pbm.padding.inline_start +
1752 pbm.border.inline_start +
1753 effective_margin_inline_start,
1754 },
1755 size: content_size,
1756 };
1757
1758 let mut base_fragment_info = self.base.base_fragment_info;
1759 if depends_on_block_constraints {
1760 base_fragment_info.flags.insert(
1761 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1762 );
1763 }
1764
1765 BoxFragment::new(
1766 base_fragment_info,
1767 style.clone(),
1768 layout.fragments,
1769 content_rect.as_physical(Some(containing_block)),
1770 pbm.padding.to_physical(containing_block_writing_mode),
1771 pbm.border.to_physical(containing_block_writing_mode),
1772 margin.to_physical(containing_block_writing_mode),
1773 layout.specific_layout_info,
1774 )
1775 .with_baselines(layout.baselines)
1776 .with_block_level_layout_info(CollapsedBlockMargins::from_margin(&margin), clearance)
1777 }
1778}
1779
1780struct ContainingBlockPaddingAndBorder<'a> {
1781 containing_block: ContainingBlock<'a>,
1782 pbm: PaddingBorderMargin,
1783 block_sizes: Sizes,
1784 depends_on_block_constraints: bool,
1785 available_block_size: Option<Au>,
1786 justify_self: AlignFlags,
1787 preferred_aspect_ratio: Option<AspectRatio>,
1788}
1789
1790struct ResolvedMargins {
1791 pub margin: LogicalSides<Au>,
1793
1794 pub effective_margin_inline_start: Au,
1801}
1802
1803fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
1809 containing_block: &ContainingBlock<'_>,
1810 layout_style: &'a LayoutStyle,
1811 get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
1812 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1813 context: Option<&IndependentFormattingContext>,
1814 has_inline_parent: bool,
1815) -> ContainingBlockPaddingAndBorder<'a> {
1816 let style = layout_style.style();
1817 if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
1818 let containing_block_for_children = ContainingBlock {
1822 size: ContainingBlockSize {
1823 inline: containing_block.size.inline,
1824 block: containing_block.size.block,
1825 },
1826 style,
1827 };
1828 return ContainingBlockPaddingAndBorder {
1831 containing_block: containing_block_for_children,
1832 pbm: PaddingBorderMargin::zero(),
1833 block_sizes: Sizes::default(),
1834 depends_on_block_constraints: false,
1835 available_block_size: None,
1838 justify_self: AlignFlags::NORMAL,
1841 preferred_aspect_ratio: None,
1842 };
1843 }
1844
1845 let ContentBoxSizesAndPBM {
1846 content_box_sizes,
1847 pbm,
1848 depends_on_block_constraints,
1849 ..
1850 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
1851
1852 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1853 let available_inline_size = Au::zero().max(containing_block.size.inline - pbm_sums.inline);
1854 let available_block_size = containing_block
1855 .size
1856 .block
1857 .to_definite()
1858 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1859
1860 let preferred_aspect_ratio =
1863 context.and_then(|context| context.preferred_aspect_ratio(&pbm.padding_border_sums));
1864 let is_table = layout_style.is_table();
1865
1866 let tentative_block_content_size = context.and_then(|context| {
1869 context.tentative_block_content_size(preferred_aspect_ratio, available_inline_size)
1870 });
1871 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
1872 SizeConstraint::Definite(content_box_sizes.block.resolve(
1873 Direction::Block,
1874 Size::FitContent,
1875 Au::zero,
1876 available_block_size,
1877 || block_content_size,
1878 is_table,
1879 ))
1880 } else {
1881 content_box_sizes.block.resolve_extrinsic(
1882 Size::FitContent,
1883 Au::zero(),
1884 available_block_size,
1885 )
1886 };
1887
1888 let get_inline_content_sizes = || {
1891 get_inline_content_sizes(&ConstraintSpace::new(
1892 tentative_block_size,
1893 style,
1894 preferred_aspect_ratio,
1895 ))
1896 };
1897 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1898 let inline_size = content_box_sizes.inline.resolve(
1899 Direction::Inline,
1900 automatic_inline_size(justify_self, context),
1901 Au::zero,
1902 Some(available_inline_size),
1903 get_inline_content_sizes,
1904 is_table,
1905 );
1906
1907 let containing_block_for_children = ContainingBlock {
1908 size: ContainingBlockSize {
1909 inline: inline_size,
1910 block: tentative_block_size,
1911 },
1912 style,
1913 };
1914 assert_eq!(
1916 containing_block.style.writing_mode.is_horizontal(),
1917 containing_block_for_children
1918 .style
1919 .writing_mode
1920 .is_horizontal(),
1921 "Vertical writing modes are not supported yet"
1922 );
1923 ContainingBlockPaddingAndBorder {
1924 containing_block: containing_block_for_children,
1925 pbm,
1926 block_sizes: content_box_sizes.block,
1927 depends_on_block_constraints,
1928 available_block_size,
1929 justify_self,
1930 preferred_aspect_ratio,
1931 }
1932}
1933
1934fn solve_margins(
1939 containing_block: &ContainingBlock<'_>,
1940 pbm: &PaddingBorderMargin,
1941 inline_size: Au,
1942 justify_self: AlignFlags,
1943) -> ResolvedMargins {
1944 let (inline_margins, effective_margin_inline_start) =
1945 solve_inline_margins_for_in_flow_block_level(
1946 containing_block,
1947 pbm,
1948 inline_size,
1949 justify_self,
1950 );
1951 let block_margins = solve_block_margins_for_in_flow_block_level(pbm);
1952 ResolvedMargins {
1953 margin: LogicalSides {
1954 inline_start: inline_margins.0,
1955 inline_end: inline_margins.1,
1956 block_start: block_margins.0,
1957 block_end: block_margins.1,
1958 },
1959 effective_margin_inline_start,
1960 }
1961}
1962
1963fn solve_block_margins_for_in_flow_block_level(pbm: &PaddingBorderMargin) -> (Au, Au) {
1967 (
1968 pbm.margin.block_start.auto_is(Au::zero),
1969 pbm.margin.block_end.auto_is(Au::zero),
1970 )
1971}
1972
1973fn resolve_justify_self(
1975 style: &ComputedValues,
1976 containing_block_style: &ComputedValues,
1977 has_inline_parent: bool,
1978) -> AlignFlags {
1979 let alignment = match style.clone_justify_self().0 {
1985 AlignFlags::AUTO if has_inline_parent => AlignFlags::NORMAL,
1986 AlignFlags::AUTO => containing_block_style.clone_justify_items().computed.0.0,
1987 alignment => alignment,
1988 };
1989 let is_ltr = |style: &ComputedValues| style.writing_mode.line_left_is_inline_start();
1990 let alignment_value = match alignment.value() {
1991 AlignFlags::LEFT if is_ltr(containing_block_style) => AlignFlags::START,
1992 AlignFlags::LEFT => AlignFlags::END,
1993 AlignFlags::RIGHT if is_ltr(containing_block_style) => AlignFlags::END,
1994 AlignFlags::RIGHT => AlignFlags::START,
1995 AlignFlags::SELF_START if is_ltr(containing_block_style) == is_ltr(style) => {
1996 AlignFlags::START
1997 },
1998 AlignFlags::SELF_START => AlignFlags::END,
1999 AlignFlags::SELF_END if is_ltr(containing_block_style) == is_ltr(style) => AlignFlags::END,
2000 AlignFlags::SELF_END => AlignFlags::START,
2001 alignment_value => alignment_value,
2002 };
2003 alignment.flags() | alignment_value
2004}
2005
2006#[inline]
2009fn automatic_inline_size<T>(
2010 justify_self: AlignFlags,
2011 context: Option<&IndependentFormattingContext>,
2012) -> Size<T> {
2013 let normal_stretches = || {
2014 !context.is_some_and(|context| {
2015 context
2016 .base
2017 .base_fragment_info
2018 .flags
2019 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET) ||
2020 context.is_table()
2021 })
2022 };
2023 match justify_self {
2024 AlignFlags::STRETCH => Size::Stretch,
2025 AlignFlags::NORMAL if normal_stretches() => Size::Stretch,
2026 _ => Size::FitContent,
2027 }
2028}
2029
2030fn justify_self_alignment(
2037 containing_block: &ContainingBlock,
2038 free_space: Au,
2039 justify_self: AlignFlags,
2040) -> Au {
2041 let mut alignment = justify_self.value();
2042 let is_safe = justify_self.flags() == AlignFlags::SAFE || alignment == AlignFlags::NORMAL;
2043 if is_safe && free_space <= Au::zero() {
2044 alignment = AlignFlags::START
2045 }
2046 match alignment {
2047 AlignFlags::NORMAL => {},
2048 AlignFlags::CENTER => return free_space / 2,
2049 AlignFlags::END => return free_space,
2050 _ => return Au::zero(),
2051 }
2052
2053 let style = containing_block.style;
2055 match style.clone_text_align() {
2056 TextAlignKeyword::MozCenter => free_space / 2,
2057 TextAlignKeyword::MozLeft if !style.writing_mode.line_left_is_inline_start() => free_space,
2058 TextAlignKeyword::MozRight if style.writing_mode.line_left_is_inline_start() => free_space,
2059 _ => Au::zero(),
2060 }
2061}
2062
2063fn solve_inline_margins_for_in_flow_block_level(
2076 containing_block: &ContainingBlock,
2077 pbm: &PaddingBorderMargin,
2078 inline_size: Au,
2079 justify_self: AlignFlags,
2080) -> ((Au, Au), Au) {
2081 let free_space = containing_block.size.inline - pbm.padding_border_sums.inline - inline_size;
2082 let mut justification = Au::zero();
2083 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
2084 (AuOrAuto::Auto, AuOrAuto::Auto) => {
2085 let start = Au::zero().max(free_space / 2);
2086 (start, free_space - start)
2087 },
2088 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
2089 (Au::zero().max(free_space - end), end)
2090 },
2091 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, free_space - start),
2092 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
2093 justification =
2098 justify_self_alignment(containing_block, free_space - start - end, justify_self);
2099 (start, end)
2100 },
2101 };
2102 let effective_margin_inline_start = inline_margins.0 + justification;
2103 (inline_margins, effective_margin_inline_start)
2104}
2105
2106fn solve_inline_margins_avoiding_floats(
2116 sequential_layout_state: &SequentialLayoutState,
2117 containing_block: &ContainingBlock,
2118 pbm: &PaddingBorderMargin,
2119 inline_size: Au,
2120 placement_rect: LogicalRect<Au>,
2121 justify_self: AlignFlags,
2122) -> ((Au, Au), Au) {
2123 let free_space = Au::zero().max(placement_rect.size.inline - inline_size);
2127 let cb_info = &sequential_layout_state.floats.containing_block_info;
2128 let start_adjustment = placement_rect.start_corner.inline - cb_info.inline_start;
2129 let end_adjustment = cb_info.inline_end - placement_rect.max_inline_position();
2130 let mut justification = Au::zero();
2131 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
2132 (AuOrAuto::Auto, AuOrAuto::Auto) => {
2133 let half = free_space / 2;
2134 (start_adjustment + half, end_adjustment + free_space - half)
2135 },
2136 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (start_adjustment + free_space, end),
2137 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, end_adjustment + free_space),
2138 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
2139 justification = justify_self_alignment(containing_block, free_space, justify_self);
2145 (start, end)
2146 },
2147 };
2148 let effective_margin_inline_start = inline_margins.0.max(start_adjustment) + justification;
2149 (inline_margins, effective_margin_inline_start)
2150}
2151
2152struct PlacementState<'container> {
2157 next_in_flow_margin_collapses_with_parent_start_margin: bool,
2158 last_in_flow_margin_collapses_with_parent_end_margin: bool,
2159 start_margin: CollapsedMargin,
2160 current_margin: CollapsedMargin,
2161 current_block_direction_position: Au,
2162 inflow_baselines: Baselines,
2163 is_inline_block_context: bool,
2164
2165 marker_block_size: Option<Au>,
2170
2171 containing_block: &'container ContainingBlock<'container>,
2174}
2175
2176impl<'container> PlacementState<'container> {
2177 fn new(
2178 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2179 containing_block: &'container ContainingBlock<'container>,
2180 ) -> PlacementState<'container> {
2181 let is_inline_block_context =
2182 containing_block.style.get_box().clone_display() == Display::InlineBlock;
2183 PlacementState {
2184 next_in_flow_margin_collapses_with_parent_start_margin:
2185 collapsible_with_parent_start_margin.0,
2186 last_in_flow_margin_collapses_with_parent_end_margin: true,
2187 start_margin: CollapsedMargin::zero(),
2188 current_margin: CollapsedMargin::zero(),
2189 current_block_direction_position: Au::zero(),
2190 inflow_baselines: Baselines::default(),
2191 is_inline_block_context,
2192 marker_block_size: None,
2193 containing_block,
2194 }
2195 }
2196
2197 fn place_fragment_and_update_baseline(
2198 &mut self,
2199 fragment: &mut Fragment,
2200 sequential_layout_state: Option<&mut SequentialLayoutState>,
2201 ) {
2202 self.place_fragment(fragment, sequential_layout_state);
2203
2204 let box_fragment = match fragment {
2205 Fragment::Box(box_fragment) => box_fragment,
2206 _ => return,
2207 };
2208 let box_fragment = box_fragment.borrow();
2209
2210 if self.is_inline_block_context && box_fragment.is_table_wrapper() {
2215 return;
2216 }
2217
2218 let box_block_offset = box_fragment
2219 .content_rect()
2220 .origin
2221 .to_logical(self.containing_block)
2222 .block;
2223 let box_fragment_baselines =
2224 box_fragment.baselines(self.containing_block.style.writing_mode);
2225 if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) {
2226 self.inflow_baselines.first = Some(first + box_block_offset);
2227 }
2228 if let Some(last) = box_fragment_baselines.last {
2229 self.inflow_baselines.last = Some(last + box_block_offset);
2230 }
2231 }
2232
2233 fn place_fragment(
2236 &mut self,
2237 fragment: &mut Fragment,
2238 sequential_layout_state: Option<&mut SequentialLayoutState>,
2239 ) {
2240 match fragment {
2241 Fragment::Box(fragment) => {
2242 let fragment = &mut *fragment.borrow_mut();
2251 let is_outside_marker = fragment
2252 .base
2253 .flags
2254 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER);
2255 if is_outside_marker {
2256 assert!(self.marker_block_size.is_none());
2257 self.marker_block_size = Some(
2258 fragment
2259 .content_rect()
2260 .size
2261 .to_logical(self.containing_block.style.writing_mode)
2262 .block,
2263 );
2264 return;
2265 }
2266
2267 let BlockLevelLayoutInfo {
2268 clearance,
2269 block_margins_collapsed_with_children: fragment_block_margins,
2270 } = &**fragment
2271 .block_level_layout_info
2272 .as_ref()
2273 .expect("A block-level fragment should have a BlockLevelLayoutInfo.");
2274 let mut fragment_block_size = fragment
2275 .border_rect()
2276 .size
2277 .to_logical(self.containing_block.style.writing_mode)
2278 .block;
2279
2280 if let Some(clearance) = *clearance {
2286 fragment_block_size += clearance;
2287 self.current_block_direction_position += self.current_margin.solve();
2292 self.current_margin = CollapsedMargin::zero();
2293 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2294 if fragment_block_margins.collapsed_through {
2295 self.last_in_flow_margin_collapses_with_parent_end_margin = false;
2296 }
2297 } else if !fragment_block_margins.collapsed_through {
2298 self.last_in_flow_margin_collapses_with_parent_end_margin = true;
2299 }
2300
2301 if self.next_in_flow_margin_collapses_with_parent_start_margin {
2302 debug_assert!(self.current_margin.solve().is_zero());
2303 self.start_margin
2304 .adjoin_assign(&fragment_block_margins.start);
2305 if fragment_block_margins.collapsed_through {
2306 self.start_margin.adjoin_assign(&fragment_block_margins.end);
2307 return;
2308 }
2309 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2310 } else {
2311 self.current_margin
2312 .adjoin_assign(&fragment_block_margins.start);
2313 }
2314
2315 fragment.base.rect.origin += LogicalVec2 {
2316 inline: Au::zero(),
2317 block: self.current_margin.solve() + self.current_block_direction_position,
2318 }
2319 .to_physical_size(self.containing_block.style.writing_mode);
2320
2321 if fragment_block_margins.collapsed_through {
2322 self.current_block_direction_position += fragment_block_size;
2325 self.current_margin
2326 .adjoin_assign(&fragment_block_margins.end);
2327 } else {
2328 self.current_block_direction_position +=
2329 self.current_margin.solve() + fragment_block_size;
2330 self.current_margin = fragment_block_margins.end;
2331 }
2332 },
2333 Fragment::AbsoluteOrFixedPositioned(fragment) => {
2334 fragment.borrow_mut().original_static_position_rect = LogicalRect {
2337 start_corner: LogicalVec2 {
2338 block: (self.current_margin.solve() +
2339 self.current_block_direction_position),
2340 inline: Au::zero(),
2341 },
2342 size: LogicalVec2::zero(),
2343 }
2344 .as_physical(Some(self.containing_block));
2345 },
2346 Fragment::Float(box_fragment) => {
2347 let sequential_layout_state = sequential_layout_state
2348 .expect("Found float fragment without SequentialLayoutState");
2349 let block_offset_from_containing_block_top =
2350 self.current_block_direction_position + self.current_margin.solve();
2351 let box_fragment = &mut *box_fragment.borrow_mut();
2352 sequential_layout_state.place_float_fragment(
2353 box_fragment,
2354 self.containing_block,
2355 self.start_margin,
2356 block_offset_from_containing_block_top,
2357 );
2358 },
2359 Fragment::Positioning(_) => {},
2360 _ => unreachable!(),
2361 }
2362 }
2363
2364 fn finish(mut self) -> (Au, CollapsedBlockMargins, Baselines) {
2365 if !self.last_in_flow_margin_collapses_with_parent_end_margin {
2366 self.current_block_direction_position += self.current_margin.solve();
2367 self.current_margin = CollapsedMargin::zero();
2368 }
2369 let (total_block_size, collapsed_through) = match self.marker_block_size {
2370 Some(marker_block_size) => (
2371 self.current_block_direction_position.max(marker_block_size),
2372 false,
2375 ),
2376 None => (
2377 self.current_block_direction_position,
2378 self.next_in_flow_margin_collapses_with_parent_start_margin,
2379 ),
2380 };
2381
2382 (
2383 total_block_size,
2384 CollapsedBlockMargins {
2385 collapsed_through,
2386 start: self.start_margin,
2387 end: self.current_margin,
2388 },
2389 self.inflow_baselines,
2390 )
2391 }
2392}
2393
2394pub(crate) struct IndependentFloatOrAtomicLayoutResult {
2395 pub fragment: BoxFragment,
2396 pub baselines: Baselines,
2397 pub pbm_sums: LogicalSides<Au>,
2398}
2399
2400impl IndependentFormattingContext {
2401 pub(crate) fn layout_float_or_atomic_inline(
2402 &self,
2403 layout_context: &LayoutContext,
2404 child_positioning_context: &mut PositioningContext,
2405 containing_block: &ContainingBlock,
2406 ) -> IndependentFloatOrAtomicLayoutResult {
2407 let style = self.style();
2408 let container_writing_mode = containing_block.style.writing_mode;
2409 let layout_style = self.layout_style();
2410 let content_box_sizes_and_pbm =
2411 layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
2412 let pbm = &content_box_sizes_and_pbm.pbm;
2413 let margin = pbm.margin.auto_is(Au::zero);
2414 let pbm_sums = pbm.padding + pbm.border + margin;
2415 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
2416 let is_table = self.is_table();
2417
2418 let available_inline_size =
2419 Au::zero().max(containing_block.size.inline - pbm_sums.inline_sum());
2420 let available_block_size = containing_block
2421 .size
2422 .block
2423 .to_definite()
2424 .map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum()));
2425
2426 let tentative_block_content_size =
2427 self.tentative_block_content_size(preferred_aspect_ratio, available_inline_size);
2428 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
2429 SizeConstraint::Definite(content_box_sizes_and_pbm.content_box_sizes.block.resolve(
2430 Direction::Block,
2431 Size::FitContent,
2432 Au::zero,
2433 available_block_size,
2434 || block_content_size,
2435 is_table,
2436 ))
2437 } else {
2438 content_box_sizes_and_pbm
2439 .content_box_sizes
2440 .block
2441 .resolve_extrinsic(Size::FitContent, Au::zero(), available_block_size)
2442 };
2443
2444 let get_content_size = || {
2445 let constraint_space =
2446 ConstraintSpace::new(tentative_block_size, style, preferred_aspect_ratio);
2447 self.inline_content_sizes(layout_context, &constraint_space)
2448 .sizes
2449 };
2450
2451 let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
2452 Direction::Inline,
2453 Size::FitContent,
2454 Au::zero,
2455 Some(available_inline_size),
2456 get_content_size,
2457 is_table,
2458 );
2459
2460 let containing_block_for_children = ContainingBlock {
2461 size: ContainingBlockSize {
2462 inline: inline_size,
2463 block: tentative_block_size,
2464 },
2465 style,
2466 };
2467 assert_eq!(
2468 container_writing_mode.is_horizontal(),
2469 style.writing_mode.is_horizontal(),
2470 "Mixed horizontal and vertical writing modes are not supported yet"
2471 );
2472
2473 let lazy_block_size = LazySize::new(
2474 &content_box_sizes_and_pbm.content_box_sizes.block,
2475 Direction::Block,
2476 Size::FitContent,
2477 Au::zero,
2478 available_block_size,
2479 is_table,
2480 );
2481
2482 let IndependentFormattingContextLayoutResult {
2483 content_inline_size_for_table,
2484 content_block_size,
2485 fragments,
2486 baselines,
2487 specific_layout_info,
2488 ..
2489 } = self.layout(
2490 layout_context,
2491 child_positioning_context,
2492 &containing_block_for_children,
2493 containing_block,
2494 preferred_aspect_ratio,
2495 &lazy_block_size,
2496 );
2497
2498 let content_size = LogicalVec2 {
2499 inline: content_inline_size_for_table.unwrap_or(inline_size),
2500 block: lazy_block_size.resolve(|| content_block_size),
2501 }
2502 .to_physical_size(container_writing_mode);
2503 let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size);
2504
2505 let mut base_fragment_info = self.base_fragment_info();
2506 if content_box_sizes_and_pbm.depends_on_block_constraints {
2507 base_fragment_info.flags.insert(
2508 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2509 );
2510 }
2511
2512 let fragment = BoxFragment::new(
2516 base_fragment_info,
2517 style.clone(),
2518 fragments,
2519 content_rect,
2520 pbm.padding.to_physical(container_writing_mode),
2521 pbm.border.to_physical(container_writing_mode),
2522 margin.to_physical(container_writing_mode),
2523 specific_layout_info,
2524 );
2525
2526 IndependentFloatOrAtomicLayoutResult {
2527 fragment,
2528 baselines,
2529 pbm_sums,
2530 }
2531 }
2532}