1#![allow(rustdoc::private_intra_doc_links)]
5
6use std::sync::Arc;
9
10use app_units::{Au, MAX_AU};
11use inline::InlineFormattingContext;
12use layout_api::LayoutNode;
13use malloc_size_of_derive::MallocSizeOf;
14use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
15use script::layout_dom::ServoLayoutNode;
16use servo_arc::Arc as ServoArc;
17use style::Zero;
18use style::computed_values::clear::T as StyleClear;
19use style::context::SharedStyleContext;
20use style::logical_geometry::Direction;
21use style::properties::ComputedValues;
22use style::servo::selector_parser::PseudoElement;
23use style::values::specified::align::AlignFlags;
24use style::values::specified::{Display, TextAlignKeyword};
25
26use crate::cell::ArcRefCell;
27use crate::context::LayoutContext;
28use crate::dom::WeakLayoutBox;
29use crate::flow::float::{
30 Clear, ContainingBlockPositionInfo, FloatBox, FloatSide, PlacementAmongFloats,
31 SequentialLayoutState,
32};
33use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
34use crate::fragment_tree::{
35 BaseFragmentInfo, BlockLevelLayoutInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin,
36 Fragment, FragmentFlags,
37};
38use crate::geom::{
39 AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
40 PhysicalSides, ToLogical, ToLogicalWithContainingBlock,
41};
42use crate::layout_box_base::{IndependentFormattingContextLayoutResult, LayoutBoxBase};
43use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
44use crate::sizing::{
45 self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize, Size,
46 SizeConstraint, Sizes,
47};
48use crate::style_ext::{AspectRatio, ContentBoxSizesAndPBM, LayoutStyle, PaddingBorderMargin};
49use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
50
51mod construct;
52pub mod float;
53pub mod inline;
54mod root;
55
56pub(crate) use construct::{BlockContainerBuilder, BlockLevelCreator};
57pub(crate) use root::BoxTree;
58
59#[derive(Debug, MallocSizeOf)]
60pub(crate) struct BlockFormattingContext {
61 pub contents: BlockContainer,
62 pub contains_floats: bool,
63}
64
65#[derive(Debug, MallocSizeOf)]
66pub(crate) enum BlockContainer {
67 BlockLevelBoxes(Vec<ArcRefCell<BlockLevelBox>>),
68 InlineFormattingContext(InlineFormattingContext),
69}
70
71impl BlockContainer {
72 fn contains_floats(&self) -> bool {
73 match self {
74 BlockContainer::BlockLevelBoxes(boxes) => boxes
75 .iter()
76 .any(|block_level_box| block_level_box.borrow().contains_floats()),
77 BlockContainer::InlineFormattingContext(context) => context.contains_floats,
78 }
79 }
80
81 pub(crate) fn repair_style(
82 &mut self,
83 context: &SharedStyleContext,
84 node: &ServoLayoutNode,
85 new_style: &ServoArc<ComputedValues>,
86 ) {
87 match self {
88 BlockContainer::BlockLevelBoxes(..) => {},
89 BlockContainer::InlineFormattingContext(inline_formatting_context) => {
90 inline_formatting_context.repair_style(context, node, new_style)
91 },
92 }
93 }
94}
95
96#[derive(Debug, MallocSizeOf)]
97pub(crate) enum BlockLevelBox {
98 Independent(IndependentFormattingContext),
99 OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
100 OutOfFlowFloatBox(FloatBox),
101 OutsideMarker(OutsideMarker),
102 SameFormattingContextBlock {
103 base: LayoutBoxBase,
104 contents: BlockContainer,
105 contains_floats: bool,
106 },
107}
108
109impl BlockLevelBox {
110 pub(crate) fn repair_style(
111 &mut self,
112 context: &SharedStyleContext,
113 node: &ServoLayoutNode,
114 new_style: &ServoArc<ComputedValues>,
115 ) {
116 match self {
117 BlockLevelBox::Independent(independent_formatting_context) => {
118 independent_formatting_context.repair_style(context, node, new_style)
119 },
120 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
121 .borrow_mut()
122 .context
123 .repair_style(context, node, new_style),
124 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
125 float_box.contents.repair_style(context, node, new_style)
126 },
127 BlockLevelBox::OutsideMarker(outside_marker) => {
128 outside_marker.repair_style(context, node, new_style)
129 },
130 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
131 base.repair_style(new_style);
132 contents.repair_style(context, node, new_style);
133 },
134 }
135 }
136
137 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
138 match self {
139 BlockLevelBox::Independent(independent_formatting_context) => {
140 callback(&independent_formatting_context.base)
141 },
142 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
143 callback(&positioned_box.borrow().context.base)
144 },
145 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&float_box.contents.base),
146 BlockLevelBox::OutsideMarker(outside_marker) => callback(&outside_marker.context.base),
147 BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
148 }
149 }
150
151 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
152 match self {
153 BlockLevelBox::Independent(independent_formatting_context) => {
154 callback(&mut independent_formatting_context.base)
155 },
156 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
157 callback(&mut positioned_box.borrow_mut().context.base)
158 },
159 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base),
160 BlockLevelBox::OutsideMarker(outside_marker) => {
161 callback(&mut outside_marker.context.base)
162 },
163 BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
164 }
165 }
166
167 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
168 match self {
169 Self::Independent(independent_formatting_context) => {
170 independent_formatting_context.attached_to_tree(layout_box)
171 },
172 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
173 positioned_box.borrow().context.attached_to_tree(layout_box)
174 },
175 Self::OutOfFlowFloatBox(float_box) => float_box.contents.attached_to_tree(layout_box),
176 Self::OutsideMarker(outside_marker) => {
177 outside_marker.context.attached_to_tree(layout_box)
178 },
179 Self::SameFormattingContextBlock { contents, .. } => {
180 contents.attached_to_tree(layout_box)
181 },
182 }
183 }
184
185 fn contains_floats(&self) -> bool {
186 match self {
187 BlockLevelBox::SameFormattingContextBlock {
188 contains_floats, ..
189 } => *contains_floats,
190 BlockLevelBox::OutOfFlowFloatBox { .. } => true,
191 _ => false,
192 }
193 }
194
195 fn find_block_margin_collapsing_with_parent(
196 &self,
197 layout_context: &LayoutContext,
198 collected_margin: &mut CollapsedMargin,
199 containing_block: &ContainingBlock,
200 ) -> bool {
201 let layout_style = match self {
202 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
203 contents.layout_style(base)
204 },
205 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
206 BlockLevelBox::OutOfFlowFloatBox(_) => return true,
207 BlockLevelBox::OutsideMarker(_) => return false,
208 BlockLevelBox::Independent(context) => {
209 context.layout_style()
212 },
213 };
214
215 let style = layout_style.style();
217 if style.get_box().clear != StyleClear::None {
218 return false;
219 }
220
221 let ContentBoxSizesAndPBM {
222 content_box_sizes,
223 pbm,
224 ..
225 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
226 let margin = pbm.margin.auto_is(Au::zero);
227 collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_start));
228
229 let BlockLevelBox::SameFormattingContextBlock { contents, .. } = self else {
230 return false;
231 };
232
233 if !pbm.padding.block_start.is_zero() || !pbm.border.block_start.is_zero() {
234 return false;
235 }
236
237 let available_inline_size =
238 containing_block.size.inline - pbm.padding_border_sums.inline - margin.inline_sum();
239 let available_block_size = containing_block.size.block.to_definite().map(|block_size| {
240 Au::zero().max(block_size - pbm.padding_border_sums.block - margin.block_sum())
241 });
242
243 let tentative_block_size = content_box_sizes.block.resolve_extrinsic(
244 Size::FitContent,
245 Au::zero(),
246 available_block_size,
247 );
248
249 let get_inline_content_sizes = || {
250 let constraint_space = ConstraintSpace::new(
251 tentative_block_size,
252 style,
253 None, );
255 self.inline_content_sizes(layout_context, &constraint_space)
256 .sizes
257 };
258 let inline_size = content_box_sizes.inline.resolve(
259 Direction::Inline,
260 Size::Stretch,
261 Au::zero,
262 Some(available_inline_size),
263 get_inline_content_sizes,
264 false, );
266
267 let containing_block_for_children = ContainingBlock {
268 size: ContainingBlockSize {
269 inline: inline_size,
270 block: tentative_block_size,
271 },
272 style,
273 };
274
275 if !contents.find_block_margin_collapsing_with_parent(
276 layout_context,
277 collected_margin,
278 &containing_block_for_children,
279 ) {
280 return false;
281 }
282
283 if !tentative_block_size.definite_or_min().is_zero() ||
284 !pbm.padding_border_sums.block.is_zero()
285 {
286 return false;
287 }
288
289 collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_end));
290
291 true
292 }
293}
294
295#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
296pub(crate) struct CollapsibleWithParentStartMargin(bool);
297
298#[derive(Debug, MallocSizeOf)]
301pub(crate) struct OutsideMarker {
302 pub list_item_style: ServoArc<ComputedValues>,
303 pub context: IndependentFormattingContext,
304}
305
306impl OutsideMarker {
307 fn layout(
308 &self,
309 layout_context: &LayoutContext<'_>,
310 containing_block: &ContainingBlock<'_>,
311 positioning_context: &mut PositioningContext,
312 ) -> Fragment {
313 let style = &self.context.base.style;
314 let preferred_aspect_ratio = self.context.preferred_aspect_ratio(&LogicalVec2::zero());
315 let constraint_space =
316 ConstraintSpace::new(SizeConstraint::default(), style, preferred_aspect_ratio);
317 let content_sizes = self
318 .context
319 .inline_content_sizes(layout_context, &constraint_space);
320 let containing_block_for_children = ContainingBlock {
321 size: ContainingBlockSize {
322 inline: content_sizes.sizes.max_content,
323 block: SizeConstraint::default(),
324 },
325 style,
326 };
327
328 let layout = self.context.layout(
329 layout_context,
330 positioning_context,
331 &containing_block_for_children,
332 containing_block,
333 preferred_aspect_ratio,
334 &LazySize::intrinsic(),
335 );
336
337 let max_inline_size = layout
338 .fragments
339 .iter()
340 .map(|fragment| {
341 fragment
342 .base()
343 .map(|base| base.rect())
344 .unwrap_or_default()
345 .to_logical(&containing_block_for_children)
346 .max_inline_position()
347 })
348 .max()
349 .unwrap_or_default();
350
351 let pbm_of_list_item =
361 LayoutStyle::Default(&self.list_item_style).padding_border_margin(containing_block);
362 let content_rect = LogicalRect {
363 start_corner: LogicalVec2 {
364 inline: -max_inline_size -
365 (pbm_of_list_item.border.inline_start +
366 pbm_of_list_item.padding.inline_start),
367 block: Zero::zero(),
368 },
369 size: LogicalVec2 {
370 inline: max_inline_size,
371 block: layout.content_block_size,
372 },
373 };
374
375 let mut base_fragment_info = BaseFragmentInfo::anonymous();
376 base_fragment_info.flags |= FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER;
377
378 Fragment::Box(
379 BoxFragment::new(
380 base_fragment_info,
381 style.clone(),
382 layout.fragments,
383 content_rect.as_physical(Some(containing_block)),
384 PhysicalSides::zero(),
385 PhysicalSides::zero(),
386 PhysicalSides::zero(),
387 layout.specific_layout_info,
388 )
389 .into(),
390 )
391 }
392
393 fn repair_style(
394 &mut self,
395 context: &SharedStyleContext,
396 node: &ServoLayoutNode,
397 new_style: &ServoArc<ComputedValues>,
398 ) {
399 self.list_item_style = node.parent_style(context);
400 self.context.repair_style(context, node, new_style);
401 }
402}
403
404impl BlockFormattingContext {
405 pub(super) fn layout(
406 &self,
407 layout_context: &LayoutContext,
408 positioning_context: &mut PositioningContext,
409 containing_block: &ContainingBlock,
410 ) -> IndependentFormattingContextLayoutResult {
411 let mut sequential_layout_state = if self.contains_floats || !layout_context.use_rayon {
412 Some(SequentialLayoutState::new(containing_block.size.inline))
413 } else {
414 None
415 };
416
417 let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
421
422 let flow_layout = self.contents.layout(
423 layout_context,
424 positioning_context,
425 containing_block,
426 sequential_layout_state.as_mut(),
427 CollapsibleWithParentStartMargin(false),
428 ignore_block_margins_for_stretch,
429 );
430 debug_assert!(
431 !flow_layout
432 .collapsible_margins_in_children
433 .collapsed_through
434 );
435
436 let clearance = sequential_layout_state.and_then(|sequential_layout_state| {
440 sequential_layout_state.calculate_clearance(Clear::Both, &CollapsedMargin::zero())
441 });
442
443 IndependentFormattingContextLayoutResult {
444 fragments: flow_layout.fragments,
445 content_block_size: flow_layout.content_block_size +
446 flow_layout.collapsible_margins_in_children.end.solve() +
447 clearance.unwrap_or_default(),
448 content_inline_size_for_table: None,
449 baselines: flow_layout.baselines,
450 depends_on_block_constraints: flow_layout.depends_on_block_constraints,
451 specific_layout_info: None,
452 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
453 }
454 }
455
456 #[inline]
457 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
458 LayoutStyle::Default(&base.style)
459 }
460
461 pub(crate) fn repair_style(
462 &mut self,
463 context: &SharedStyleContext,
464 node: &ServoLayoutNode,
465 new_style: &ServoArc<ComputedValues>,
466 ) {
467 self.contents.repair_style(context, node, new_style);
468 }
469
470 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
471 self.contents.attached_to_tree(layout_box);
472 }
473}
474
475fn compute_inline_content_sizes_for_block_level_boxes(
481 boxes: &[ArcRefCell<BlockLevelBox>],
482 layout_context: &LayoutContext,
483 containing_block: &IndefiniteContainingBlock,
484) -> InlineContentSizesResult {
485 let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
486 match &*box_.borrow() {
487 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
488 BlockLevelBox::OutsideMarker { .. } => None,
489 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
490 let inline_content_sizes_result = float_box.contents.outer_inline_content_sizes(
491 layout_context,
492 containing_block,
493 &LogicalVec2::zero(),
494 false, );
496 let style = &float_box.contents.style();
497 let container_writing_mode = containing_block.style.writing_mode;
498 Some((
499 inline_content_sizes_result,
500 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode),
501 Clear::from_style_and_container_writing_mode(style, container_writing_mode),
502 ))
503 },
504 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
505 let is_anonymous_block =
506 matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
507 let inline_content_sizes_result = sizing::outer_inline(
508 base,
509 &contents.layout_style(base),
510 containing_block,
511 &LogicalVec2::zero(),
512 false, false, !is_anonymous_block, |_| None, |constraint_space| {
517 base.inline_content_sizes(layout_context, constraint_space, contents)
518 },
519 |_aspect_ratio| None,
520 );
521 let clear = if is_anonymous_block {
530 Clear::None
531 } else {
532 Clear::Both
533 };
534 Some((inline_content_sizes_result, None, clear))
535 },
536 BlockLevelBox::Independent(independent) => {
537 let inline_content_sizes_result = independent.outer_inline_content_sizes(
538 layout_context,
539 containing_block,
540 &LogicalVec2::zero(),
541 false, );
543 Some((
544 inline_content_sizes_result,
545 None,
546 Clear::from_style_and_container_writing_mode(
547 independent.style(),
548 containing_block.style.writing_mode,
549 ),
550 ))
551 },
552 }
553 };
554
555 #[derive(Default)]
558 struct AccumulatedData {
559 depends_on_block_constraints: bool,
561 max_size: ContentSizes,
563 floats: LogicalSides1D<ContentSizes>,
566 }
567
568 impl AccumulatedData {
569 fn max_size_including_uncleared_floats(&self) -> ContentSizes {
570 self.max_size.max(self.floats.start.union(&self.floats.end))
571 }
572 fn clear_floats(&mut self, clear: Clear) {
573 match clear {
574 Clear::InlineStart => {
575 self.max_size = self.max_size_including_uncleared_floats();
576 self.floats.start = ContentSizes::default();
577 },
578 Clear::InlineEnd => {
579 self.max_size = self.max_size_including_uncleared_floats();
580 self.floats.end = ContentSizes::default();
581 },
582 Clear::Both => {
583 self.max_size = self.max_size_including_uncleared_floats();
584 self.floats = LogicalSides1D::default();
585 },
586 Clear::None => {},
587 };
588 }
589 }
590
591 let accumulate =
592 |mut data: AccumulatedData,
593 (inline_content_sizes_result, float, clear): (InlineContentSizesResult, _, _)| {
594 let size = inline_content_sizes_result.sizes.max(ContentSizes::zero());
595 let depends_on_block_constraints =
596 inline_content_sizes_result.depends_on_block_constraints;
597 data.depends_on_block_constraints |= depends_on_block_constraints;
598 data.clear_floats(clear);
599 match float {
600 Some(FloatSide::InlineStart) => data.floats.start.union_assign(&size),
601 Some(FloatSide::InlineEnd) => data.floats.end.union_assign(&size),
602 None => {
603 data.max_size
604 .max_assign(data.floats.start.union(&data.floats.end).union(&size));
605 data.floats = LogicalSides1D::default();
606 },
607 }
608 data
609 };
610 let data = if layout_context.use_rayon {
611 boxes
612 .par_iter()
613 .filter_map(get_box_info)
614 .collect::<Vec<_>>()
615 .into_iter()
616 .fold(AccumulatedData::default(), accumulate)
617 } else {
618 boxes
619 .iter()
620 .filter_map(get_box_info)
621 .fold(AccumulatedData::default(), accumulate)
622 };
623 InlineContentSizesResult {
624 depends_on_block_constraints: data.depends_on_block_constraints,
625 sizes: data.max_size_including_uncleared_floats(),
626 }
627}
628
629impl BlockContainer {
630 fn layout(
631 &self,
632 layout_context: &LayoutContext,
633 positioning_context: &mut PositioningContext,
634 containing_block: &ContainingBlock,
635 sequential_layout_state: Option<&mut SequentialLayoutState>,
636 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
637 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
638 ) -> IndependentFormattingContextLayoutResult {
639 match self {
640 BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children(
641 layout_context,
642 positioning_context,
643 child_boxes,
644 containing_block,
645 sequential_layout_state,
646 collapsible_with_parent_start_margin,
647 ignore_block_margins_for_stretch,
648 ),
649 BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
650 layout_context,
651 positioning_context,
652 containing_block,
653 sequential_layout_state,
654 collapsible_with_parent_start_margin,
655 ),
656 }
657 }
658
659 #[inline]
660 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
661 LayoutStyle::Default(&base.style)
662 }
663
664 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
665 match self {
666 Self::BlockLevelBoxes(child_boxes) => {
667 for child_box in child_boxes {
668 child_box.borrow_mut().with_base_mut(|base| {
669 base.parent_box.replace(layout_box.clone());
670 });
671 }
672 },
673 Self::InlineFormattingContext(ifc) => ifc.attached_to_tree(layout_box),
674 }
675 }
676
677 fn find_block_margin_collapsing_with_parent(
678 &self,
679 layout_context: &LayoutContext,
680 collected_margin: &mut CollapsedMargin,
681 containing_block_for_children: &ContainingBlock,
682 ) -> bool {
683 match self {
684 BlockContainer::BlockLevelBoxes(boxes) => boxes.iter().all(|block_level_box| {
685 block_level_box
686 .borrow()
687 .find_block_margin_collapsing_with_parent(
688 layout_context,
689 collected_margin,
690 containing_block_for_children,
691 )
692 }),
693 BlockContainer::InlineFormattingContext(context) => context
694 .find_block_margin_collapsing_with_parent(
695 layout_context,
696 collected_margin,
697 containing_block_for_children,
698 ),
699 }
700 }
701}
702
703impl ComputeInlineContentSizes for BlockContainer {
704 fn compute_inline_content_sizes(
705 &self,
706 layout_context: &LayoutContext,
707 constraint_space: &ConstraintSpace,
708 ) -> InlineContentSizesResult {
709 match &self {
710 Self::BlockLevelBoxes(boxes) => compute_inline_content_sizes_for_block_level_boxes(
711 boxes,
712 layout_context,
713 &constraint_space.into(),
714 ),
715 Self::InlineFormattingContext(context) => {
716 context.compute_inline_content_sizes(layout_context, constraint_space)
717 },
718 }
719 }
720}
721
722fn layout_block_level_children(
723 layout_context: &LayoutContext,
724 positioning_context: &mut PositioningContext,
725 child_boxes: &[ArcRefCell<BlockLevelBox>],
726 containing_block: &ContainingBlock,
727 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
728 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
729 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
730) -> IndependentFormattingContextLayoutResult {
731 let mut placement_state =
732 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
733
734 let fragments = match sequential_layout_state {
735 Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
736 layout_context,
737 positioning_context,
738 child_boxes,
739 sequential_layout_state,
740 &mut placement_state,
741 ignore_block_margins_for_stretch,
742 ),
743 None => layout_block_level_children_in_parallel(
744 layout_context,
745 positioning_context,
746 child_boxes,
747 &mut placement_state,
748 ignore_block_margins_for_stretch,
749 ),
750 };
751
752 let depends_on_block_constraints = fragments.iter().any(|fragment| {
753 fragment.base().is_some_and(|base| {
754 base.flags.contains(
755 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
756 )
757 })
758 });
759
760 let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
761 IndependentFormattingContextLayoutResult {
762 fragments,
763 content_block_size,
764 collapsible_margins_in_children,
765 baselines,
766 depends_on_block_constraints,
767 content_inline_size_for_table: None,
768 specific_layout_info: None,
769 }
770}
771
772fn layout_block_level_children_in_parallel(
773 layout_context: &LayoutContext,
774 positioning_context: &mut PositioningContext,
775 child_boxes: &[ArcRefCell<BlockLevelBox>],
776 placement_state: &mut PlacementState,
777 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
778) -> Vec<Fragment> {
779 let mut layout_results: Vec<(Fragment, PositioningContext)> =
780 Vec::with_capacity(child_boxes.len());
781
782 child_boxes
783 .par_iter()
784 .map(|child_box| {
785 let mut child_positioning_context = PositioningContext::default();
786 let fragment = child_box.borrow().layout(
787 layout_context,
788 &mut child_positioning_context,
789 placement_state.containing_block,
790 None,
791 None,
792 ignore_block_margins_for_stretch,
793 false, );
795 (fragment, child_positioning_context)
796 })
797 .collect_into_vec(&mut layout_results);
798
799 layout_results
800 .into_iter()
801 .map(|(mut fragment, mut child_positioning_context)| {
802 placement_state.place_fragment_and_update_baseline(&mut fragment, None);
803 child_positioning_context.adjust_static_position_of_hoisted_fragments(
804 &fragment,
805 PositioningContextLength::zero(),
806 );
807 positioning_context.append(child_positioning_context);
808 fragment
809 })
810 .collect()
811}
812
813fn layout_block_level_children_sequentially(
814 layout_context: &LayoutContext,
815 positioning_context: &mut PositioningContext,
816 child_boxes: &[ArcRefCell<BlockLevelBox>],
817 sequential_layout_state: &mut SequentialLayoutState,
818 placement_state: &mut PlacementState,
819 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
820) -> Vec<Fragment> {
821 child_boxes
825 .iter()
826 .map(|child_box| {
827 layout_block_level_child(
828 layout_context,
829 positioning_context,
830 &child_box.borrow(),
831 Some(sequential_layout_state),
832 placement_state,
833 ignore_block_margins_for_stretch,
834 false, )
836 })
837 .collect()
838}
839
840fn layout_block_level_child(
841 layout_context: &LayoutContext,
842 positioning_context: &mut PositioningContext,
843 child_box: &BlockLevelBox,
844 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
845 placement_state: &mut PlacementState,
846 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
847 has_inline_parent: bool,
848) -> Fragment {
849 let positioning_context_length_before_layout = positioning_context.len();
850 let mut fragment = child_box.layout(
851 layout_context,
852 positioning_context,
853 placement_state.containing_block,
854 sequential_layout_state.as_deref_mut(),
855 Some(CollapsibleWithParentStartMargin(
856 placement_state.next_in_flow_margin_collapses_with_parent_start_margin,
857 )),
858 ignore_block_margins_for_stretch,
859 has_inline_parent,
860 );
861
862 placement_state.place_fragment_and_update_baseline(&mut fragment, sequential_layout_state);
863 positioning_context.adjust_static_position_of_hoisted_fragments(
864 &fragment,
865 positioning_context_length_before_layout,
866 );
867
868 fragment
869}
870
871impl BlockLevelBox {
872 #[allow(clippy::too_many_arguments)]
873 fn layout(
874 &self,
875 layout_context: &LayoutContext,
876 positioning_context: &mut PositioningContext,
877 containing_block: &ContainingBlock,
878 sequential_layout_state: Option<&mut SequentialLayoutState>,
879 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
880 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
881 has_inline_parent: bool,
882 ) -> Fragment {
883 let fragment = match self {
884 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => Fragment::Box(
885 layout_in_flow_non_replaced_block_level_same_formatting_context_cached(
886 layout_context,
887 positioning_context,
888 containing_block,
889 sequential_layout_state,
890 collapsible_with_parent_start_margin,
891 ignore_block_margins_for_stretch,
892 has_inline_parent,
893 base,
894 contents,
895 ),
896 ),
897 BlockLevelBox::Independent(independent) => Fragment::Box(
898 positioning_context
899 .layout_maybe_position_relative_fragment(
900 layout_context,
901 containing_block,
902 &independent.base,
903 |positioning_context| {
904 independent.layout_in_flow_block_level(
905 layout_context,
906 positioning_context,
907 containing_block,
908 sequential_layout_state,
909 ignore_block_margins_for_stretch,
910 has_inline_parent,
911 )
912 },
913 )
914 .into(),
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(
937 float_box
938 .layout(layout_context, positioning_context, containing_block)
939 .into(),
940 ),
941 BlockLevelBox::OutsideMarker(outside_marker) => {
942 outside_marker.layout(layout_context, containing_block, positioning_context)
943 },
944 };
945
946 self.with_base(|base| base.set_fragment(fragment.clone()));
947
948 fragment
949 }
950
951 fn inline_content_sizes(
952 &self,
953 layout_context: &LayoutContext,
954 constraint_space: &ConstraintSpace,
955 ) -> InlineContentSizesResult {
956 let independent_formatting_context = match self {
957 BlockLevelBox::Independent(independent) => independent,
958 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => &box_.borrow().context,
959 BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents,
960 BlockLevelBox::OutsideMarker(outside_marker) => &outside_marker.context,
961 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
962 return base.inline_content_sizes(layout_context, constraint_space, contents);
963 },
964 };
965 independent_formatting_context.inline_content_sizes(layout_context, constraint_space)
966 }
967}
968
969#[allow(clippy::too_many_arguments)]
976fn layout_in_flow_non_replaced_block_level_same_formatting_context_cached(
977 layout_context: &LayoutContext<'_>,
978 positioning_context: &mut PositioningContext,
979 containing_block: &ContainingBlock<'_>,
980 sequential_layout_state: Option<&mut SequentialLayoutState>,
981 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
982 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
983 has_inline_parent: bool,
984 base: &LayoutBoxBase,
985 contents: &BlockContainer,
986) -> Arc<BoxFragment> {
987 let mut allows_caching = sequential_layout_state.is_none();
988
989 if allows_caching &&
990 let Some(cached_result) = base.cached_same_formatting_context_block_if_applicable(
991 containing_block,
992 collapsible_with_parent_start_margin,
993 ignore_block_margins_for_stretch,
994 has_inline_parent,
995 )
996 {
997 return cached_result;
998 };
999
1000 let positioning_context_length = positioning_context.len();
1001 let fragment = Arc::new(positioning_context.layout_maybe_position_relative_fragment(
1002 layout_context,
1003 containing_block,
1004 base,
1005 |positioning_context| {
1006 layout_in_flow_non_replaced_block_level_same_formatting_context(
1007 layout_context,
1008 positioning_context,
1009 containing_block,
1010 base,
1011 contents,
1012 sequential_layout_state,
1013 collapsible_with_parent_start_margin,
1014 ignore_block_margins_for_stretch,
1015 has_inline_parent,
1016 )
1017 },
1018 ));
1019
1020 allows_caching = allows_caching && positioning_context_length == positioning_context.len();
1026
1027 if !allows_caching {
1028 base.clear_fragments_and_dirty_fragment_cache();
1029 } else {
1030 base.cache_same_formatting_context_block_layout(
1031 containing_block,
1032 collapsible_with_parent_start_margin,
1033 ignore_block_margins_for_stretch,
1034 has_inline_parent,
1035 fragment.clone(),
1036 );
1037 }
1038
1039 fragment
1040}
1041
1042#[allow(clippy::too_many_arguments)]
1048fn layout_in_flow_non_replaced_block_level_same_formatting_context(
1049 layout_context: &LayoutContext,
1050 positioning_context: &mut PositioningContext,
1051 containing_block: &ContainingBlock,
1052 base: &LayoutBoxBase,
1053 contents: &BlockContainer,
1054 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
1055 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
1056 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1057 has_inline_parent: bool,
1058) -> BoxFragment {
1059 let style = &base.style;
1060 let layout_style = contents.layout_style(base);
1061 let containing_block_writing_mode = containing_block.style.writing_mode;
1062 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1063 base.inline_content_sizes(layout_context, constraint_space, contents)
1064 .sizes
1065 };
1066 let ContainingBlockPaddingAndBorder {
1067 containing_block: containing_block_for_children,
1068 pbm,
1069 block_sizes,
1070 depends_on_block_constraints,
1071 available_block_size,
1072 justify_self,
1073 ..
1074 } = solve_containing_block_padding_and_border_for_in_flow_box(
1075 containing_block,
1076 &layout_style,
1077 get_inline_content_sizes,
1078 ignore_block_margins_for_stretch,
1079 None,
1080 has_inline_parent,
1081 );
1082 let ResolvedMargins {
1083 margin,
1084 effective_margin_inline_start,
1085 } = solve_margins(
1086 containing_block,
1087 &pbm,
1088 containing_block_for_children.size.inline,
1089 justify_self,
1090 );
1091
1092 let start_margin_can_collapse_with_children =
1093 pbm.padding.block_start.is_zero() && pbm.border.block_start.is_zero();
1094
1095 let mut clearance = None;
1096 let parent_containing_block_position_info;
1097 match sequential_layout_state {
1098 None => parent_containing_block_position_info = None,
1099 Some(ref mut sequential_layout_state) => {
1100 let clear =
1101 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode);
1102 let mut block_start_margin = CollapsedMargin::new(margin.block_start);
1103
1104 let collapsible_with_parent_start_margin = collapsible_with_parent_start_margin.expect(
1116 "We should know whether we are collapsing the block start margin with the parent \
1117 when laying out sequentially",
1118 ).0 && clear == Clear::None;
1119 if !collapsible_with_parent_start_margin && start_margin_can_collapse_with_children {
1120 contents.find_block_margin_collapsing_with_parent(
1121 layout_context,
1122 &mut block_start_margin,
1123 &containing_block_for_children,
1124 );
1125 }
1126
1127 clearance = sequential_layout_state.calculate_clearance(clear, &block_start_margin);
1129 if clearance.is_some() {
1130 sequential_layout_state.commit_margin();
1131 }
1132 sequential_layout_state.adjoin_assign(&block_start_margin);
1133 if !start_margin_can_collapse_with_children {
1134 sequential_layout_state.commit_margin();
1135 }
1136
1137 sequential_layout_state.advance_block_position(
1140 pbm.padding.block_start +
1141 pbm.border.block_start +
1142 clearance.unwrap_or_else(Au::zero),
1143 );
1144
1145 let inline_start = sequential_layout_state
1151 .floats
1152 .containing_block_info
1153 .inline_start +
1154 pbm.padding.inline_start +
1155 pbm.border.inline_start +
1156 effective_margin_inline_start;
1157 let new_cb_offsets = ContainingBlockPositionInfo {
1158 block_start: sequential_layout_state.bfc_relative_block_position,
1159 block_start_margins_not_collapsed: sequential_layout_state.current_margin,
1160 inline_start,
1161 inline_end: inline_start + containing_block_for_children.size.inline,
1162 };
1163 parent_containing_block_position_info = Some(
1164 sequential_layout_state.replace_containing_block_position_info(new_cb_offsets),
1165 );
1166 },
1167 };
1168
1169 let ignore_block_margins_for_stretch = LogicalSides1D::new(
1175 pbm.border.block_start.is_zero() && pbm.padding.block_start.is_zero(),
1176 pbm.border.block_end.is_zero() && pbm.padding.block_end.is_zero(),
1177 );
1178
1179 let flow_layout = contents.layout(
1180 layout_context,
1181 positioning_context,
1182 &containing_block_for_children,
1183 sequential_layout_state.as_deref_mut(),
1184 CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
1185 ignore_block_margins_for_stretch,
1186 );
1187 let mut content_block_size = flow_layout.content_block_size;
1188
1189 let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1191 let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
1192 if start_margin_can_collapse_with_children {
1193 block_margins_collapsed_with_children
1194 .start
1195 .adjoin_assign(&collapsible_margins_in_children.start);
1196 if collapsible_margins_in_children.collapsed_through {
1197 block_margins_collapsed_with_children
1198 .start
1199 .adjoin_assign(&std::mem::replace(
1200 &mut collapsible_margins_in_children.end,
1201 CollapsedMargin::zero(),
1202 ));
1203 }
1204 }
1205
1206 let is_anonymous = matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
1207 let tentative_block_size = if is_anonymous {
1208 &Default::default()
1212 } else {
1213 &containing_block_for_children.size.block
1214 };
1215 let collapsed_through = collapsible_margins_in_children.collapsed_through &&
1216 pbm.padding_border_sums.block.is_zero() &&
1217 tentative_block_size.definite_or_min().is_zero();
1218 block_margins_collapsed_with_children.collapsed_through = collapsed_through;
1219
1220 let end_margin_can_collapse_with_children =
1221 pbm.padding.block_end.is_zero() && pbm.border.block_end.is_zero();
1222 if !end_margin_can_collapse_with_children {
1223 content_block_size += collapsible_margins_in_children.end.solve();
1224 }
1225
1226 let block_size = block_sizes.resolve(
1227 Direction::Block,
1228 Size::FitContent,
1229 Au::zero,
1230 available_block_size,
1231 || content_block_size.into(),
1232 false, );
1234
1235 let end_margin_can_collapse_with_children = end_margin_can_collapse_with_children &&
1250 block_size == content_block_size &&
1251 (collapsed_through || !tentative_block_size.is_definite());
1252 if end_margin_can_collapse_with_children {
1253 block_margins_collapsed_with_children
1254 .end
1255 .adjoin_assign(&collapsible_margins_in_children.end);
1256 }
1257
1258 if let Some(ref mut sequential_layout_state) = sequential_layout_state {
1259 sequential_layout_state
1262 .replace_containing_block_position_info(parent_containing_block_position_info.unwrap());
1263
1264 sequential_layout_state.advance_block_position(
1274 block_size - content_block_size + pbm.padding.block_end + pbm.border.block_end,
1275 );
1276
1277 if !end_margin_can_collapse_with_children {
1278 sequential_layout_state.commit_margin();
1279 }
1280 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1281 }
1282
1283 let content_rect = LogicalRect {
1284 start_corner: LogicalVec2 {
1285 block: (pbm.padding.block_start +
1286 pbm.border.block_start +
1287 clearance.unwrap_or_else(Au::zero)),
1288 inline: pbm.padding.inline_start +
1289 pbm.border.inline_start +
1290 effective_margin_inline_start,
1291 },
1292 size: LogicalVec2 {
1293 block: block_size,
1294 inline: containing_block_for_children.size.inline,
1295 },
1296 };
1297
1298 let mut base_fragment_info = base.base_fragment_info;
1299
1300 if depends_on_block_constraints || (is_anonymous && flow_layout.depends_on_block_constraints) {
1304 base_fragment_info
1305 .flags
1306 .insert(FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM);
1307 }
1308
1309 BoxFragment::new(
1310 base_fragment_info,
1311 style.clone(),
1312 flow_layout.fragments,
1313 content_rect.as_physical(Some(containing_block)),
1314 pbm.padding.to_physical(containing_block_writing_mode),
1315 pbm.border.to_physical(containing_block_writing_mode),
1316 margin.to_physical(containing_block_writing_mode),
1317 flow_layout.specific_layout_info,
1318 )
1319 .with_baselines(flow_layout.baselines)
1320 .with_block_level_layout_info(block_margins_collapsed_with_children, clearance)
1321}
1322
1323impl IndependentFormattingContext {
1324 pub(crate) fn layout_in_flow_block_level(
1332 &self,
1333 layout_context: &LayoutContext,
1334 positioning_context: &mut PositioningContext,
1335 containing_block: &ContainingBlock,
1336 sequential_layout_state: Option<&mut SequentialLayoutState>,
1337 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1338 has_inline_parent: bool,
1339 ) -> BoxFragment {
1340 if let Some(sequential_layout_state) = sequential_layout_state {
1341 return self.layout_in_flow_block_level_sequentially(
1342 layout_context,
1343 positioning_context,
1344 containing_block,
1345 sequential_layout_state,
1346 ignore_block_margins_for_stretch,
1347 has_inline_parent,
1348 );
1349 }
1350
1351 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1352 self.inline_content_sizes(layout_context, constraint_space)
1353 .sizes
1354 };
1355 let layout_style = self.layout_style();
1356 let ContainingBlockPaddingAndBorder {
1357 containing_block: containing_block_for_children,
1358 pbm,
1359 block_sizes,
1360 depends_on_block_constraints,
1361 available_block_size,
1362 justify_self,
1363 preferred_aspect_ratio,
1364 } = solve_containing_block_padding_and_border_for_in_flow_box(
1365 containing_block,
1366 &layout_style,
1367 get_inline_content_sizes,
1368 ignore_block_margins_for_stretch,
1369 Some(self),
1370 has_inline_parent,
1371 );
1372
1373 let lazy_block_size = LazySize::new(
1374 &block_sizes,
1375 Direction::Block,
1376 Size::FitContent,
1377 Au::zero,
1378 available_block_size,
1379 layout_style.is_table(),
1380 );
1381
1382 let layout = self.layout(
1383 layout_context,
1384 positioning_context,
1385 &containing_block_for_children,
1386 containing_block,
1387 preferred_aspect_ratio,
1388 &lazy_block_size,
1389 );
1390
1391 let inline_size = layout
1392 .content_inline_size_for_table
1393 .unwrap_or(containing_block_for_children.size.inline);
1394 let block_size = lazy_block_size.resolve(|| layout.content_block_size);
1395
1396 let ResolvedMargins {
1397 margin,
1398 effective_margin_inline_start,
1399 } = solve_margins(containing_block, &pbm, inline_size, justify_self);
1400
1401 let content_rect = LogicalRect {
1402 start_corner: LogicalVec2 {
1403 block: pbm.padding.block_start + pbm.border.block_start,
1404 inline: pbm.padding.inline_start +
1405 pbm.border.inline_start +
1406 effective_margin_inline_start,
1407 },
1408 size: LogicalVec2 {
1409 block: block_size,
1410 inline: inline_size,
1411 },
1412 };
1413
1414 let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1415 let containing_block_writing_mode = containing_block.style.writing_mode;
1416
1417 let mut base_fragment_info = self.base.base_fragment_info;
1418 if depends_on_block_constraints {
1419 base_fragment_info.flags.insert(
1420 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1421 );
1422 }
1423 BoxFragment::new(
1424 base_fragment_info,
1425 self.base.style.clone(),
1426 layout.fragments,
1427 content_rect.as_physical(Some(containing_block)),
1428 pbm.padding.to_physical(containing_block_writing_mode),
1429 pbm.border.to_physical(containing_block_writing_mode),
1430 margin.to_physical(containing_block_writing_mode),
1431 layout.specific_layout_info,
1432 )
1433 .with_baselines(layout.baselines)
1434 .with_block_level_layout_info(block_margins_collapsed_with_children, None)
1435 }
1436
1437 fn layout_in_flow_block_level_sequentially(
1441 &self,
1442 layout_context: &LayoutContext<'_>,
1443 positioning_context: &mut PositioningContext,
1444 containing_block: &ContainingBlock<'_>,
1445 sequential_layout_state: &mut SequentialLayoutState,
1446 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1447 has_inline_parent: bool,
1448 ) -> BoxFragment {
1449 let style = &self.base.style;
1450 let containing_block_writing_mode = containing_block.style.writing_mode;
1451 let ContentBoxSizesAndPBM {
1452 content_box_sizes,
1453 pbm,
1454 depends_on_block_constraints,
1455 ..
1456 } = self
1457 .layout_style()
1458 .content_box_sizes_and_padding_border_margin(&containing_block.into());
1459
1460 let (margin_block_start, margin_block_end) =
1461 solve_block_margins_for_in_flow_block_level(&pbm);
1462 let collapsed_margin_block_start = CollapsedMargin::new(margin_block_start);
1463
1464 let mut content_size;
1475 let mut layout;
1476 let mut placement_rect;
1477
1478 let clear_position = sequential_layout_state.calculate_clear_position(
1482 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode),
1483 &collapsed_margin_block_start,
1484 );
1485 let ceiling = clear_position.unwrap_or_else(|| {
1486 sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
1487 });
1488
1489 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1491 let available_block_size = containing_block
1492 .size
1493 .block
1494 .to_definite()
1495 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1496 let is_table = self.is_table();
1497 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
1498
1499 #[derive(Default)]
1500 struct Cache {
1501 min_block_size: Au,
1502 max_block_size: Option<Au>,
1503 tentative_block_size: SizeConstraint,
1504 depends_on_stretch_size: bool,
1505 }
1506 let mut cache = Cache {
1507 depends_on_stretch_size: true,
1508 ..Default::default()
1509 };
1510
1511 let update_cache = |cache: &mut Cache, stretch_size| {
1512 let tentative_block_content_size = self
1513 .tentative_block_content_size_with_dependency(preferred_aspect_ratio, stretch_size);
1514 let (preferred_block_size, min_block_size, max_block_size, depends_on_stretch_size) =
1515 if let Some(result) = tentative_block_content_size {
1516 let (block_content_size, depends_on_stretch_size) = result;
1517 let (preferred, min, max) = content_box_sizes.block.resolve_each(
1518 Size::FitContent,
1519 Au::zero,
1520 available_block_size,
1521 || block_content_size,
1522 is_table,
1523 );
1524 (Some(preferred), min, max, depends_on_stretch_size)
1525 } else {
1526 let (preferred, min, max) = content_box_sizes.block.resolve_each_extrinsic(
1527 Size::FitContent,
1528 Au::zero(),
1529 available_block_size,
1530 );
1531 (preferred, min, max, false)
1532 };
1533 cache.min_block_size = min_block_size;
1534 cache.max_block_size = max_block_size;
1535 cache.tentative_block_size =
1536 SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
1537 cache.depends_on_stretch_size = depends_on_stretch_size;
1538 };
1539
1540 let get_inline_content_sizes = |cache: &Cache| {
1542 let constraint_space =
1543 ConstraintSpace::new(cache.tentative_block_size, style, preferred_aspect_ratio);
1544 self.inline_content_sizes(layout_context, &constraint_space)
1545 .sizes
1546 };
1547
1548 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1549 let automatic_inline_size = automatic_inline_size(justify_self, Some(self));
1550 let compute_inline_size = |cache: &mut Cache, stretch_size| {
1551 if cache.depends_on_stretch_size {
1552 update_cache(cache, stretch_size);
1553 }
1554 content_box_sizes.inline.resolve(
1555 Direction::Inline,
1556 automatic_inline_size,
1557 Au::zero,
1558 Some(stretch_size),
1559 || get_inline_content_sizes(cache),
1560 is_table,
1561 )
1562 };
1563
1564 let get_lazy_block_size = || {
1565 LazySize::new(
1566 &content_box_sizes.block,
1567 Direction::Block,
1568 Size::FitContent,
1569 Au::zero,
1570 available_block_size,
1571 is_table,
1572 )
1573 };
1574
1575 let inline_size_with_max_available_space = compute_inline_size(&mut cache, MAX_AU);
1584 let inline_size_with_no_available_space = compute_inline_size(&mut cache, Au::zero());
1585 if inline_size_with_no_available_space == inline_size_with_max_available_space {
1586 let inline_size = inline_size_with_no_available_space;
1590 let lazy_block_size = get_lazy_block_size();
1591 layout = self.layout(
1592 layout_context,
1593 positioning_context,
1594 &ContainingBlock {
1595 size: ContainingBlockSize {
1596 inline: inline_size,
1597 block: cache.tentative_block_size,
1601 },
1602 style,
1603 },
1604 containing_block,
1605 preferred_aspect_ratio,
1606 &lazy_block_size,
1607 );
1608
1609 content_size = LogicalVec2 {
1610 block: lazy_block_size.resolve(|| layout.content_block_size),
1611 inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
1612 };
1613
1614 let mut placement = PlacementAmongFloats::new(
1615 &sequential_layout_state.floats,
1616 ceiling,
1617 content_size + pbm.padding_border_sums,
1618 &pbm,
1619 );
1620 placement_rect = placement.place();
1621 } else {
1622 let minimum_size_of_block = LogicalVec2 {
1628 inline: inline_size_with_no_available_space,
1632 block: match cache.tentative_block_size {
1640 SizeConstraint::Definite(size) if cache.max_block_size.is_some() => size,
1643 _ => cache.min_block_size,
1646 },
1647 } + pbm.padding_border_sums;
1648 let mut placement = PlacementAmongFloats::new(
1649 &sequential_layout_state.floats,
1650 ceiling,
1651 minimum_size_of_block,
1652 &pbm,
1653 );
1654
1655 loop {
1656 placement_rect = placement.place();
1658 let available_inline_size =
1659 placement_rect.size.inline - pbm.padding_border_sums.inline;
1660 let proposed_inline_size = compute_inline_size(&mut cache, available_inline_size);
1661
1662 let positioning_context_length = positioning_context.len();
1666 let lazy_block_size = get_lazy_block_size();
1667 layout = self.layout(
1668 layout_context,
1669 positioning_context,
1670 &ContainingBlock {
1671 size: ContainingBlockSize {
1672 inline: proposed_inline_size,
1673 block: cache.tentative_block_size,
1674 },
1675 style,
1676 },
1677 containing_block,
1678 preferred_aspect_ratio,
1679 &lazy_block_size,
1680 );
1681
1682 let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
1683 debug_assert!(inline_size < proposed_inline_size);
1688 inline_size
1689 } else {
1690 proposed_inline_size
1691 };
1692 content_size = LogicalVec2 {
1693 block: lazy_block_size.resolve(|| layout.content_block_size),
1694 inline: inline_size,
1695 };
1696
1697 if placement.try_to_expand_for_auto_block_size(
1701 content_size.block + pbm.padding_border_sums.block,
1702 &placement_rect.size,
1703 ) {
1704 break;
1705 }
1706
1707 positioning_context.truncate(&positioning_context_length);
1711 }
1712 }
1713
1714 let has_clearance = clear_position.is_some() || placement_rect.start_corner.block > ceiling;
1718 let clearance = has_clearance.then(|| {
1719 placement_rect.start_corner.block -
1720 sequential_layout_state
1721 .position_with_zero_clearance(&collapsed_margin_block_start)
1722 });
1723
1724 let ((margin_inline_start, margin_inline_end), effective_margin_inline_start) =
1725 solve_inline_margins_avoiding_floats(
1726 sequential_layout_state,
1727 containing_block,
1728 &pbm,
1729 content_size.inline + pbm.padding_border_sums.inline,
1730 placement_rect,
1731 justify_self,
1732 );
1733
1734 let margin = LogicalSides {
1735 inline_start: margin_inline_start,
1736 inline_end: margin_inline_end,
1737 block_start: margin_block_start,
1738 block_end: margin_block_end,
1739 };
1740
1741 if clearance.is_some() {
1744 sequential_layout_state.commit_margin();
1745 }
1746 sequential_layout_state.adjoin_assign(&collapsed_margin_block_start);
1747
1748 sequential_layout_state.commit_margin();
1750 sequential_layout_state.advance_block_position(
1751 pbm.padding_border_sums.block + content_size.block + clearance.unwrap_or_else(Au::zero),
1752 );
1753 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1754
1755 let content_rect = LogicalRect {
1756 start_corner: LogicalVec2 {
1757 block: pbm.padding.block_start +
1758 pbm.border.block_start +
1759 clearance.unwrap_or_else(Au::zero),
1760 inline: pbm.padding.inline_start +
1761 pbm.border.inline_start +
1762 effective_margin_inline_start,
1763 },
1764 size: content_size,
1765 };
1766
1767 let mut base_fragment_info = self.base.base_fragment_info;
1768 if depends_on_block_constraints {
1769 base_fragment_info.flags.insert(
1770 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1771 );
1772 }
1773
1774 BoxFragment::new(
1775 base_fragment_info,
1776 style.clone(),
1777 layout.fragments,
1778 content_rect.as_physical(Some(containing_block)),
1779 pbm.padding.to_physical(containing_block_writing_mode),
1780 pbm.border.to_physical(containing_block_writing_mode),
1781 margin.to_physical(containing_block_writing_mode),
1782 layout.specific_layout_info,
1783 )
1784 .with_baselines(layout.baselines)
1785 .with_block_level_layout_info(CollapsedBlockMargins::from_margin(&margin), clearance)
1786 }
1787}
1788
1789struct ContainingBlockPaddingAndBorder<'a> {
1790 containing_block: ContainingBlock<'a>,
1791 pbm: PaddingBorderMargin,
1792 block_sizes: Sizes,
1793 depends_on_block_constraints: bool,
1794 available_block_size: Option<Au>,
1795 justify_self: AlignFlags,
1796 preferred_aspect_ratio: Option<AspectRatio>,
1797}
1798
1799struct ResolvedMargins {
1800 pub margin: LogicalSides<Au>,
1802
1803 pub effective_margin_inline_start: Au,
1810}
1811
1812fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
1818 containing_block: &ContainingBlock<'_>,
1819 layout_style: &'a LayoutStyle,
1820 get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
1821 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1822 context: Option<&IndependentFormattingContext>,
1823 has_inline_parent: bool,
1824) -> ContainingBlockPaddingAndBorder<'a> {
1825 let style = layout_style.style();
1826 if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
1827 let containing_block_for_children = ContainingBlock {
1831 size: ContainingBlockSize {
1832 inline: containing_block.size.inline,
1833 block: containing_block.size.block,
1834 },
1835 style,
1836 };
1837 return ContainingBlockPaddingAndBorder {
1840 containing_block: containing_block_for_children,
1841 pbm: PaddingBorderMargin::zero(),
1842 block_sizes: Sizes::default(),
1843 depends_on_block_constraints: false,
1844 available_block_size: None,
1847 justify_self: AlignFlags::NORMAL,
1850 preferred_aspect_ratio: None,
1851 };
1852 }
1853
1854 let ContentBoxSizesAndPBM {
1855 content_box_sizes,
1856 pbm,
1857 depends_on_block_constraints,
1858 ..
1859 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
1860
1861 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1862 let available_inline_size = Au::zero().max(containing_block.size.inline - pbm_sums.inline);
1863 let available_block_size = containing_block
1864 .size
1865 .block
1866 .to_definite()
1867 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1868
1869 let preferred_aspect_ratio =
1872 context.and_then(|context| context.preferred_aspect_ratio(&pbm.padding_border_sums));
1873 let is_table = layout_style.is_table();
1874
1875 let tentative_block_content_size = context.and_then(|context| {
1878 context.tentative_block_content_size(preferred_aspect_ratio, available_inline_size)
1879 });
1880 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
1881 SizeConstraint::Definite(content_box_sizes.block.resolve(
1882 Direction::Block,
1883 Size::FitContent,
1884 Au::zero,
1885 available_block_size,
1886 || block_content_size,
1887 is_table,
1888 ))
1889 } else {
1890 content_box_sizes.block.resolve_extrinsic(
1891 Size::FitContent,
1892 Au::zero(),
1893 available_block_size,
1894 )
1895 };
1896
1897 let get_inline_content_sizes = || {
1900 get_inline_content_sizes(&ConstraintSpace::new(
1901 tentative_block_size,
1902 style,
1903 preferred_aspect_ratio,
1904 ))
1905 };
1906 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1907 let inline_size = content_box_sizes.inline.resolve(
1908 Direction::Inline,
1909 automatic_inline_size(justify_self, context),
1910 Au::zero,
1911 Some(available_inline_size),
1912 get_inline_content_sizes,
1913 is_table,
1914 );
1915
1916 let containing_block_for_children = ContainingBlock {
1917 size: ContainingBlockSize {
1918 inline: inline_size,
1919 block: tentative_block_size,
1920 },
1921 style,
1922 };
1923 assert_eq!(
1925 containing_block.style.writing_mode.is_horizontal(),
1926 containing_block_for_children
1927 .style
1928 .writing_mode
1929 .is_horizontal(),
1930 "Vertical writing modes are not supported yet"
1931 );
1932 ContainingBlockPaddingAndBorder {
1933 containing_block: containing_block_for_children,
1934 pbm,
1935 block_sizes: content_box_sizes.block,
1936 depends_on_block_constraints,
1937 available_block_size,
1938 justify_self,
1939 preferred_aspect_ratio,
1940 }
1941}
1942
1943fn solve_margins(
1948 containing_block: &ContainingBlock<'_>,
1949 pbm: &PaddingBorderMargin,
1950 inline_size: Au,
1951 justify_self: AlignFlags,
1952) -> ResolvedMargins {
1953 let (inline_margins, effective_margin_inline_start) =
1954 solve_inline_margins_for_in_flow_block_level(
1955 containing_block,
1956 pbm,
1957 inline_size,
1958 justify_self,
1959 );
1960 let block_margins = solve_block_margins_for_in_flow_block_level(pbm);
1961 ResolvedMargins {
1962 margin: LogicalSides {
1963 inline_start: inline_margins.0,
1964 inline_end: inline_margins.1,
1965 block_start: block_margins.0,
1966 block_end: block_margins.1,
1967 },
1968 effective_margin_inline_start,
1969 }
1970}
1971
1972fn solve_block_margins_for_in_flow_block_level(pbm: &PaddingBorderMargin) -> (Au, Au) {
1976 (
1977 pbm.margin.block_start.auto_is(Au::zero),
1978 pbm.margin.block_end.auto_is(Au::zero),
1979 )
1980}
1981
1982fn resolve_justify_self(
1984 style: &ComputedValues,
1985 containing_block_style: &ComputedValues,
1986 has_inline_parent: bool,
1987) -> AlignFlags {
1988 let alignment = match style.clone_justify_self().0 {
1994 AlignFlags::AUTO if has_inline_parent => AlignFlags::NORMAL,
1995 AlignFlags::AUTO => containing_block_style.clone_justify_items().computed.0.0,
1996 alignment => alignment,
1997 };
1998 let is_ltr = |style: &ComputedValues| style.writing_mode.line_left_is_inline_start();
1999 let alignment_value = match alignment.value() {
2000 AlignFlags::LEFT if is_ltr(containing_block_style) => AlignFlags::START,
2001 AlignFlags::LEFT => AlignFlags::END,
2002 AlignFlags::RIGHT if is_ltr(containing_block_style) => AlignFlags::END,
2003 AlignFlags::RIGHT => AlignFlags::START,
2004 AlignFlags::SELF_START if is_ltr(containing_block_style) == is_ltr(style) => {
2005 AlignFlags::START
2006 },
2007 AlignFlags::SELF_START => AlignFlags::END,
2008 AlignFlags::SELF_END if is_ltr(containing_block_style) == is_ltr(style) => AlignFlags::END,
2009 AlignFlags::SELF_END => AlignFlags::START,
2010 alignment_value => alignment_value,
2011 };
2012 alignment.flags() | alignment_value
2013}
2014
2015#[inline]
2018fn automatic_inline_size<T>(
2019 justify_self: AlignFlags,
2020 context: Option<&IndependentFormattingContext>,
2021) -> Size<T> {
2022 let normal_stretches = || {
2023 !context.is_some_and(|context| {
2024 context
2025 .base
2026 .base_fragment_info
2027 .flags
2028 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET) ||
2029 context.is_table()
2030 })
2031 };
2032 match justify_self {
2033 AlignFlags::STRETCH => Size::Stretch,
2034 AlignFlags::NORMAL if normal_stretches() => Size::Stretch,
2035 _ => Size::FitContent,
2036 }
2037}
2038
2039fn justify_self_alignment(
2046 containing_block: &ContainingBlock,
2047 free_space: Au,
2048 justify_self: AlignFlags,
2049) -> Au {
2050 let mut alignment = justify_self.value();
2051 let is_safe = justify_self.flags() == AlignFlags::SAFE || alignment == AlignFlags::NORMAL;
2052 if is_safe && free_space <= Au::zero() {
2053 alignment = AlignFlags::START
2054 }
2055 match alignment {
2056 AlignFlags::NORMAL => {},
2057 AlignFlags::CENTER => return free_space / 2,
2058 AlignFlags::END => return free_space,
2059 _ => return Au::zero(),
2060 }
2061
2062 let style = containing_block.style;
2064 match style.clone_text_align() {
2065 TextAlignKeyword::MozCenter => free_space / 2,
2066 TextAlignKeyword::MozLeft if !style.writing_mode.line_left_is_inline_start() => free_space,
2067 TextAlignKeyword::MozRight if style.writing_mode.line_left_is_inline_start() => free_space,
2068 _ => Au::zero(),
2069 }
2070}
2071
2072fn solve_inline_margins_for_in_flow_block_level(
2085 containing_block: &ContainingBlock,
2086 pbm: &PaddingBorderMargin,
2087 inline_size: Au,
2088 justify_self: AlignFlags,
2089) -> ((Au, Au), Au) {
2090 let free_space = containing_block.size.inline - pbm.padding_border_sums.inline - inline_size;
2091 let mut justification = Au::zero();
2092 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
2093 (AuOrAuto::Auto, AuOrAuto::Auto) => {
2094 let start = Au::zero().max(free_space / 2);
2095 (start, free_space - start)
2096 },
2097 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
2098 (Au::zero().max(free_space - end), end)
2099 },
2100 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, free_space - start),
2101 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
2102 justification =
2107 justify_self_alignment(containing_block, free_space - start - end, justify_self);
2108 (start, end)
2109 },
2110 };
2111 let effective_margin_inline_start = inline_margins.0 + justification;
2112 (inline_margins, effective_margin_inline_start)
2113}
2114
2115fn solve_inline_margins_avoiding_floats(
2125 sequential_layout_state: &SequentialLayoutState,
2126 containing_block: &ContainingBlock,
2127 pbm: &PaddingBorderMargin,
2128 inline_size: Au,
2129 placement_rect: LogicalRect<Au>,
2130 justify_self: AlignFlags,
2131) -> ((Au, Au), Au) {
2132 let free_space = Au::zero().max(placement_rect.size.inline - inline_size);
2136 let cb_info = &sequential_layout_state.floats.containing_block_info;
2137 let start_adjustment = placement_rect.start_corner.inline - cb_info.inline_start;
2138 let end_adjustment = cb_info.inline_end - placement_rect.max_inline_position();
2139 let mut justification = Au::zero();
2140 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
2141 (AuOrAuto::Auto, AuOrAuto::Auto) => {
2142 let half = free_space / 2;
2143 (start_adjustment + half, end_adjustment + free_space - half)
2144 },
2145 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (start_adjustment + free_space, end),
2146 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, end_adjustment + free_space),
2147 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
2148 justification = justify_self_alignment(containing_block, free_space, justify_self);
2154 (start, end)
2155 },
2156 };
2157 let effective_margin_inline_start = inline_margins.0.max(start_adjustment) + justification;
2158 (inline_margins, effective_margin_inline_start)
2159}
2160
2161struct PlacementState<'container> {
2166 next_in_flow_margin_collapses_with_parent_start_margin: bool,
2167 last_in_flow_margin_collapses_with_parent_end_margin: bool,
2168 start_margin: CollapsedMargin,
2169 current_margin: CollapsedMargin,
2170 current_block_direction_position: Au,
2171 inflow_baselines: Baselines,
2172 is_inline_block_context: bool,
2173
2174 marker_block_size: Option<Au>,
2179
2180 containing_block: &'container ContainingBlock<'container>,
2183}
2184
2185impl<'container> PlacementState<'container> {
2186 fn new(
2187 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2188 containing_block: &'container ContainingBlock<'container>,
2189 ) -> PlacementState<'container> {
2190 let is_inline_block_context =
2191 containing_block.style.get_box().clone_display() == Display::InlineBlock;
2192 PlacementState {
2193 next_in_flow_margin_collapses_with_parent_start_margin:
2194 collapsible_with_parent_start_margin.0,
2195 last_in_flow_margin_collapses_with_parent_end_margin: true,
2196 start_margin: CollapsedMargin::zero(),
2197 current_margin: CollapsedMargin::zero(),
2198 current_block_direction_position: Au::zero(),
2199 inflow_baselines: Baselines::default(),
2200 is_inline_block_context,
2201 marker_block_size: None,
2202 containing_block,
2203 }
2204 }
2205
2206 fn place_fragment_and_update_baseline(
2207 &mut self,
2208 fragment: &mut Fragment,
2209 sequential_layout_state: Option<&mut SequentialLayoutState>,
2210 ) {
2211 self.place_fragment(fragment, sequential_layout_state);
2212
2213 let box_fragment = match fragment {
2214 Fragment::Box(box_fragment) => box_fragment,
2215 _ => return,
2216 };
2217
2218 if self.is_inline_block_context && box_fragment.is_table_wrapper() {
2223 return;
2224 }
2225
2226 let box_block_offset = box_fragment
2227 .content_rect()
2228 .origin
2229 .to_logical(self.containing_block)
2230 .block;
2231 let box_fragment_baselines =
2232 box_fragment.baselines(self.containing_block.style.writing_mode);
2233 if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) {
2234 self.inflow_baselines.first = Some(first + box_block_offset);
2235 }
2236 if let Some(last) = box_fragment_baselines.last {
2237 self.inflow_baselines.last = Some(last + box_block_offset);
2238 }
2239 }
2240
2241 fn place_fragment(
2244 &mut self,
2245 fragment: &mut Fragment,
2246 sequential_layout_state: Option<&mut SequentialLayoutState>,
2247 ) {
2248 match fragment {
2249 Fragment::Box(fragment) => {
2250 let is_outside_marker = fragment
2259 .base
2260 .flags
2261 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER);
2262 if is_outside_marker {
2263 assert!(self.marker_block_size.is_none());
2264 self.marker_block_size = Some(
2265 fragment
2266 .content_rect()
2267 .size
2268 .to_logical(self.containing_block.style.writing_mode)
2269 .block,
2270 );
2271 return;
2272 }
2273
2274 let BlockLevelLayoutInfo {
2275 clearance,
2276 block_margins_collapsed_with_children: fragment_block_margins,
2277 } = *fragment
2278 .block_level_layout_info
2279 .clone()
2280 .expect("A block-level fragment should have a BlockLevelLayoutInfo.");
2281 let mut fragment_block_size = fragment
2282 .border_rect()
2283 .size
2284 .to_logical(self.containing_block.style.writing_mode)
2285 .block;
2286
2287 if let Some(clearance) = clearance {
2293 fragment_block_size += clearance;
2294 self.current_block_direction_position += self.current_margin.solve();
2299 self.current_margin = CollapsedMargin::zero();
2300 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2301 if fragment_block_margins.collapsed_through {
2302 self.last_in_flow_margin_collapses_with_parent_end_margin = false;
2303 }
2304 } else if !fragment_block_margins.collapsed_through {
2305 self.last_in_flow_margin_collapses_with_parent_end_margin = true;
2306 }
2307
2308 if self.next_in_flow_margin_collapses_with_parent_start_margin {
2309 debug_assert!(self.current_margin.solve().is_zero());
2310 self.start_margin
2311 .adjoin_assign(&fragment_block_margins.start);
2312 if fragment_block_margins.collapsed_through {
2313 self.start_margin.adjoin_assign(&fragment_block_margins.end);
2314 return;
2315 }
2316 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2317 } else {
2318 self.current_margin
2319 .adjoin_assign(&fragment_block_margins.start);
2320 }
2321
2322 fragment.base.translate_rect(
2323 LogicalVec2 {
2324 inline: Au::zero(),
2325 block: self.current_margin.solve() + self.current_block_direction_position,
2326 }
2327 .to_physical_size(self.containing_block.style.writing_mode),
2328 );
2329
2330 if fragment_block_margins.collapsed_through {
2331 self.current_block_direction_position += fragment_block_size;
2334 self.current_margin
2335 .adjoin_assign(&fragment_block_margins.end);
2336 } else {
2337 self.current_block_direction_position +=
2338 self.current_margin.solve() + fragment_block_size;
2339 self.current_margin = fragment_block_margins.end;
2340 }
2341 },
2342 Fragment::AbsoluteOrFixedPositioned(fragment) => {
2343 fragment.borrow_mut().original_static_position_rect = LogicalRect {
2346 start_corner: LogicalVec2 {
2347 block: (self.current_margin.solve() +
2348 self.current_block_direction_position),
2349 inline: Au::zero(),
2350 },
2351 size: LogicalVec2::zero(),
2352 }
2353 .as_physical(Some(self.containing_block));
2354 },
2355 Fragment::Float(box_fragment) => {
2356 let sequential_layout_state = sequential_layout_state
2357 .expect("Found float fragment without SequentialLayoutState");
2358 let block_offset_from_containing_block_top =
2359 self.current_block_direction_position + self.current_margin.solve();
2360 sequential_layout_state.place_float_fragment(
2361 box_fragment,
2362 self.containing_block,
2363 self.start_margin,
2364 block_offset_from_containing_block_top,
2365 );
2366 },
2367 Fragment::Positioning(_) => {},
2368 _ => unreachable!(),
2369 }
2370 }
2371
2372 fn finish(mut self) -> (Au, CollapsedBlockMargins, Baselines) {
2373 if !self.last_in_flow_margin_collapses_with_parent_end_margin {
2374 self.current_block_direction_position += self.current_margin.solve();
2375 self.current_margin = CollapsedMargin::zero();
2376 }
2377 let (total_block_size, collapsed_through) = match self.marker_block_size {
2378 Some(marker_block_size) => (
2379 self.current_block_direction_position.max(marker_block_size),
2380 false,
2383 ),
2384 None => (
2385 self.current_block_direction_position,
2386 self.next_in_flow_margin_collapses_with_parent_start_margin,
2387 ),
2388 };
2389
2390 (
2391 total_block_size,
2392 CollapsedBlockMargins {
2393 collapsed_through,
2394 start: self.start_margin,
2395 end: self.current_margin,
2396 },
2397 self.inflow_baselines,
2398 )
2399 }
2400}
2401
2402pub(crate) struct IndependentFloatOrAtomicLayoutResult {
2403 pub fragment: BoxFragment,
2404 pub baselines: Baselines,
2405 pub pbm_sums: LogicalSides<Au>,
2406}
2407
2408impl IndependentFormattingContext {
2409 pub(crate) fn layout_float_or_atomic_inline(
2410 &self,
2411 layout_context: &LayoutContext,
2412 child_positioning_context: &mut PositioningContext,
2413 containing_block: &ContainingBlock,
2414 ) -> IndependentFloatOrAtomicLayoutResult {
2415 let style = self.style();
2416 let container_writing_mode = containing_block.style.writing_mode;
2417 let layout_style = self.layout_style();
2418 let content_box_sizes_and_pbm =
2419 layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
2420 let pbm = &content_box_sizes_and_pbm.pbm;
2421 let margin = pbm.margin.auto_is(Au::zero);
2422 let pbm_sums = pbm.padding + pbm.border + margin;
2423 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
2424 let is_table = self.is_table();
2425
2426 let available_inline_size =
2427 Au::zero().max(containing_block.size.inline - pbm_sums.inline_sum());
2428 let available_block_size = containing_block
2429 .size
2430 .block
2431 .to_definite()
2432 .map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum()));
2433
2434 let tentative_block_content_size =
2435 self.tentative_block_content_size(preferred_aspect_ratio, available_inline_size);
2436 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
2437 SizeConstraint::Definite(content_box_sizes_and_pbm.content_box_sizes.block.resolve(
2438 Direction::Block,
2439 Size::FitContent,
2440 Au::zero,
2441 available_block_size,
2442 || block_content_size,
2443 is_table,
2444 ))
2445 } else {
2446 content_box_sizes_and_pbm
2447 .content_box_sizes
2448 .block
2449 .resolve_extrinsic(Size::FitContent, Au::zero(), available_block_size)
2450 };
2451
2452 let get_content_size = || {
2453 let constraint_space =
2454 ConstraintSpace::new(tentative_block_size, style, preferred_aspect_ratio);
2455 self.inline_content_sizes(layout_context, &constraint_space)
2456 .sizes
2457 };
2458
2459 let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
2460 Direction::Inline,
2461 Size::FitContent,
2462 Au::zero,
2463 Some(available_inline_size),
2464 get_content_size,
2465 is_table,
2466 );
2467
2468 let containing_block_for_children = ContainingBlock {
2469 size: ContainingBlockSize {
2470 inline: inline_size,
2471 block: tentative_block_size,
2472 },
2473 style,
2474 };
2475 assert_eq!(
2476 container_writing_mode.is_horizontal(),
2477 style.writing_mode.is_horizontal(),
2478 "Mixed horizontal and vertical writing modes are not supported yet"
2479 );
2480
2481 let lazy_block_size = LazySize::new(
2482 &content_box_sizes_and_pbm.content_box_sizes.block,
2483 Direction::Block,
2484 Size::FitContent,
2485 Au::zero,
2486 available_block_size,
2487 is_table,
2488 );
2489
2490 let IndependentFormattingContextLayoutResult {
2491 content_inline_size_for_table,
2492 content_block_size,
2493 fragments,
2494 baselines,
2495 specific_layout_info,
2496 ..
2497 } = self.layout(
2498 layout_context,
2499 child_positioning_context,
2500 &containing_block_for_children,
2501 containing_block,
2502 preferred_aspect_ratio,
2503 &lazy_block_size,
2504 );
2505
2506 let content_size = LogicalVec2 {
2507 inline: content_inline_size_for_table.unwrap_or(inline_size),
2508 block: lazy_block_size.resolve(|| content_block_size),
2509 }
2510 .to_physical_size(container_writing_mode);
2511 let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size);
2512
2513 let mut base_fragment_info = self.base_fragment_info();
2514 if content_box_sizes_and_pbm.depends_on_block_constraints {
2515 base_fragment_info.flags.insert(
2516 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2517 );
2518 }
2519
2520 let fragment = BoxFragment::new(
2524 base_fragment_info,
2525 style.clone(),
2526 fragments,
2527 content_rect,
2528 pbm.padding.to_physical(container_writing_mode),
2529 pbm.border.to_physical(container_writing_mode),
2530 margin.to_physical(container_writing_mode),
2531 specific_layout_info,
2532 );
2533
2534 IndependentFloatOrAtomicLayoutResult {
2535 fragment,
2536 baselines,
2537 pbm_sums,
2538 }
2539 }
2540}