1#![allow(rustdoc::private_intra_doc_links)]
5
6use app_units::{Au, MAX_AU};
9use inline::InlineFormattingContext;
10use layout_api::wrapper_traits::ThreadSafeLayoutNode;
11use malloc_size_of_derive::MallocSizeOf;
12use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
13use script::layout_dom::ServoThreadSafeLayoutNode;
14use servo_arc::Arc;
15use style::Zero;
16use style::computed_values::clear::T as StyleClear;
17use style::context::SharedStyleContext;
18use style::logical_geometry::Direction;
19use style::properties::ComputedValues;
20use style::servo::selector_parser::PseudoElement;
21use style::values::specified::align::AlignFlags;
22use style::values::specified::{Display, TextAlignKeyword};
23
24use crate::cell::ArcRefCell;
25use crate::context::LayoutContext;
26use crate::dom::WeakLayoutBox;
27use crate::flow::float::{
28 Clear, ContainingBlockPositionInfo, FloatBox, FloatSide, PlacementAmongFloats,
29 SequentialLayoutState,
30};
31use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
32use crate::fragment_tree::{
33 BaseFragmentInfo, BlockLevelLayoutInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin,
34 Fragment, FragmentFlags,
35};
36use crate::geom::{
37 AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
38 PhysicalSides, ToLogical, ToLogicalWithContainingBlock,
39};
40use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
41use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
42use crate::sizing::{
43 self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize, Size,
44 SizeConstraint, Sizes,
45};
46use crate::style_ext::{AspectRatio, ContentBoxSizesAndPBM, LayoutStyle, PaddingBorderMargin};
47use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
48
49mod construct;
50pub mod float;
51pub mod inline;
52mod root;
53
54pub(crate) use construct::BlockContainerBuilder;
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 node: &ServoThreadSafeLayoutNode,
82 new_style: &Arc<ComputedValues>,
83 ) {
84 match self {
85 BlockContainer::BlockLevelBoxes(..) => {},
86 BlockContainer::InlineFormattingContext(inline_formatting_context) => {
87 inline_formatting_context.repair_style(node, new_style)
88 },
89 }
90 }
91}
92
93#[derive(Debug, MallocSizeOf)]
94pub(crate) enum BlockLevelBox {
95 Independent(IndependentFormattingContext),
96 OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
97 OutOfFlowFloatBox(FloatBox),
98 OutsideMarker(OutsideMarker),
99 SameFormattingContextBlock {
100 base: LayoutBoxBase,
101 contents: BlockContainer,
102 contains_floats: bool,
103 },
104}
105
106impl BlockLevelBox {
107 pub(crate) fn repair_style(
108 &mut self,
109 context: &SharedStyleContext,
110 node: &ServoThreadSafeLayoutNode,
111 new_style: &Arc<ComputedValues>,
112 ) {
113 self.with_base_mut(|base| {
114 base.repair_style(new_style);
115 });
116
117 match self {
118 BlockLevelBox::Independent(independent_formatting_context) => {
119 independent_formatting_context.repair_style(context, node, new_style)
120 },
121 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
122 .borrow_mut()
123 .context
124 .repair_style(context, node, new_style),
125 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
126 float_box.contents.repair_style(context, node, new_style)
127 },
128 BlockLevelBox::OutsideMarker(outside_marker) => {
129 outside_marker.repair_style(context, node, new_style)
130 },
131 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
132 base.repair_style(new_style);
133 contents.repair_style(node, new_style);
134 },
135 }
136 }
137
138 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
139 match self {
140 BlockLevelBox::Independent(independent_formatting_context) => {
141 callback(&independent_formatting_context.base)
142 },
143 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
144 callback(&positioned_box.borrow().context.base)
145 },
146 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&float_box.contents.base),
147 BlockLevelBox::OutsideMarker(outside_marker) => callback(&outside_marker.base),
148 BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
149 }
150 }
151
152 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
153 match self {
154 BlockLevelBox::Independent(independent_formatting_context) => {
155 callback(&mut independent_formatting_context.base)
156 },
157 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
158 callback(&mut positioned_box.borrow_mut().context.base)
159 },
160 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base),
161 BlockLevelBox::OutsideMarker(outside_marker) => callback(&mut outside_marker.base),
162 BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
163 }
164 }
165
166 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
167 match self {
168 Self::Independent(independent_formatting_context) => {
169 independent_formatting_context.attached_to_tree(layout_box)
170 },
171 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
172 positioned_box.borrow().context.attached_to_tree(layout_box)
173 },
174 Self::OutOfFlowFloatBox(float_box) => float_box.contents.attached_to_tree(layout_box),
175 Self::OutsideMarker(outside_marker) => outside_marker
176 .block_formatting_context
177 .contents
178 .attached_to_tree(layout_box),
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)]
296pub(crate) struct CollapsibleWithParentStartMargin(bool);
297
298#[derive(Debug, MallocSizeOf)]
301pub(crate) struct OutsideMarker {
302 pub list_item_style: Arc<ComputedValues>,
303 pub base: LayoutBoxBase,
304 pub block_formatting_context: BlockFormattingContext,
305}
306
307impl OutsideMarker {
308 fn inline_content_sizes(
309 &self,
310 layout_context: &LayoutContext,
311 constraint_space: &ConstraintSpace,
312 ) -> InlineContentSizesResult {
313 self.base.inline_content_sizes(
314 layout_context,
315 constraint_space,
316 &self.block_formatting_context.contents,
317 )
318 }
319
320 fn layout(
321 &self,
322 layout_context: &LayoutContext<'_>,
323 containing_block: &ContainingBlock<'_>,
324 positioning_context: &mut PositioningContext,
325 ) -> Fragment {
326 let constraint_space = ConstraintSpace::new_for_style_and_ratio(
327 &self.base.style,
328 None, );
330 let content_sizes = self.inline_content_sizes(layout_context, &constraint_space);
331 let containing_block_for_children = ContainingBlock {
332 size: ContainingBlockSize {
333 inline: content_sizes.sizes.max_content,
334 block: SizeConstraint::default(),
335 },
336 style: &self.base.style,
337 };
338
339 let flow_layout = self.block_formatting_context.layout(
340 layout_context,
341 positioning_context,
342 &containing_block_for_children,
343 );
344
345 let max_inline_size = flow_layout
346 .fragments
347 .iter()
348 .map(|fragment| {
349 fragment
350 .base()
351 .map(|base| base.rect)
352 .unwrap_or_default()
353 .to_logical(&containing_block_for_children)
354 .max_inline_position()
355 })
356 .max()
357 .unwrap_or_default();
358
359 let pbm_of_list_item =
369 LayoutStyle::Default(&self.list_item_style).padding_border_margin(containing_block);
370 let content_rect = LogicalRect {
371 start_corner: LogicalVec2 {
372 inline: -max_inline_size -
373 (pbm_of_list_item.border.inline_start +
374 pbm_of_list_item.padding.inline_start),
375 block: Zero::zero(),
376 },
377 size: LogicalVec2 {
378 inline: max_inline_size,
379 block: flow_layout.content_block_size,
380 },
381 };
382
383 let mut base_fragment_info = BaseFragmentInfo::anonymous();
384 base_fragment_info.flags |= FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER;
385
386 Fragment::Box(ArcRefCell::new(BoxFragment::new(
387 base_fragment_info,
388 self.base.style.clone(),
389 flow_layout.fragments,
390 content_rect.as_physical(Some(containing_block)),
391 PhysicalSides::zero(),
392 PhysicalSides::zero(),
393 PhysicalSides::zero(),
394 flow_layout.specific_layout_info,
395 )))
396 }
397
398 fn repair_style(
399 &mut self,
400 context: &SharedStyleContext,
401 node: &ServoThreadSafeLayoutNode,
402 new_style: &Arc<ComputedValues>,
403 ) {
404 self.list_item_style = node.style(context);
405 self.base.repair_style(new_style);
406 }
407}
408
409impl BlockFormattingContext {
410 pub(super) fn layout(
411 &self,
412 layout_context: &LayoutContext,
413 positioning_context: &mut PositioningContext,
414 containing_block: &ContainingBlock,
415 ) -> CacheableLayoutResult {
416 let mut sequential_layout_state = if self.contains_floats || !layout_context.use_rayon {
417 Some(SequentialLayoutState::new(containing_block.size.inline))
418 } else {
419 None
420 };
421
422 let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
426
427 let flow_layout = self.contents.layout(
428 layout_context,
429 positioning_context,
430 containing_block,
431 sequential_layout_state.as_mut(),
432 CollapsibleWithParentStartMargin(false),
433 ignore_block_margins_for_stretch,
434 );
435 debug_assert!(
436 !flow_layout
437 .collapsible_margins_in_children
438 .collapsed_through
439 );
440
441 let clearance = sequential_layout_state.and_then(|sequential_layout_state| {
445 sequential_layout_state.calculate_clearance(Clear::Both, &CollapsedMargin::zero())
446 });
447
448 CacheableLayoutResult {
449 fragments: flow_layout.fragments,
450 content_block_size: flow_layout.content_block_size +
451 flow_layout.collapsible_margins_in_children.end.solve() +
452 clearance.unwrap_or_default(),
453 content_inline_size_for_table: None,
454 baselines: flow_layout.baselines,
455 depends_on_block_constraints: flow_layout.depends_on_block_constraints,
456 specific_layout_info: None,
457 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
458 }
459 }
460
461 #[inline]
462 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
463 LayoutStyle::Default(&base.style)
464 }
465
466 pub(crate) fn repair_style(
467 &mut self,
468 node: &ServoThreadSafeLayoutNode,
469 new_style: &Arc<ComputedValues>,
470 ) {
471 self.contents.repair_style(node, new_style);
472 }
473
474 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
475 self.contents.attached_to_tree(layout_box);
476 }
477}
478
479fn compute_inline_content_sizes_for_block_level_boxes(
485 boxes: &[ArcRefCell<BlockLevelBox>],
486 layout_context: &LayoutContext,
487 containing_block: &IndefiniteContainingBlock,
488) -> InlineContentSizesResult {
489 let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
490 match &*box_.borrow() {
491 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
492 BlockLevelBox::OutsideMarker { .. } => None,
493 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
494 let inline_content_sizes_result = float_box.contents.outer_inline_content_sizes(
495 layout_context,
496 containing_block,
497 &LogicalVec2::zero(),
498 false, );
500 let style = &float_box.contents.style();
501 let container_writing_mode = containing_block.style.writing_mode;
502 Some((
503 inline_content_sizes_result,
504 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode),
505 Clear::from_style_and_container_writing_mode(style, container_writing_mode),
506 ))
507 },
508 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
509 let is_anonymous_block =
510 matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
511 let inline_content_sizes_result = sizing::outer_inline(
512 base,
513 &contents.layout_style(base),
514 containing_block,
515 &LogicalVec2::zero(),
516 false, false, !is_anonymous_block, |_| None, |constraint_space| {
521 base.inline_content_sizes(layout_context, constraint_space, contents)
522 },
523 |_aspect_ratio| None,
524 );
525 let clear = if is_anonymous_block {
534 Clear::None
535 } else {
536 Clear::Both
537 };
538 Some((inline_content_sizes_result, None, clear))
539 },
540 BlockLevelBox::Independent(independent) => {
541 let inline_content_sizes_result = independent.outer_inline_content_sizes(
542 layout_context,
543 containing_block,
544 &LogicalVec2::zero(),
545 false, );
547 Some((
548 inline_content_sizes_result,
549 None,
550 Clear::from_style_and_container_writing_mode(
551 independent.style(),
552 containing_block.style.writing_mode,
553 ),
554 ))
555 },
556 }
557 };
558
559 #[derive(Default)]
562 struct AccumulatedData {
563 depends_on_block_constraints: bool,
565 max_size: ContentSizes,
567 floats: LogicalSides1D<ContentSizes>,
570 }
571
572 impl AccumulatedData {
573 fn max_size_including_uncleared_floats(&self) -> ContentSizes {
574 self.max_size.max(self.floats.start.union(&self.floats.end))
575 }
576 fn clear_floats(&mut self, clear: Clear) {
577 match clear {
578 Clear::InlineStart => {
579 self.max_size = self.max_size_including_uncleared_floats();
580 self.floats.start = ContentSizes::default();
581 },
582 Clear::InlineEnd => {
583 self.max_size = self.max_size_including_uncleared_floats();
584 self.floats.end = ContentSizes::default();
585 },
586 Clear::Both => {
587 self.max_size = self.max_size_including_uncleared_floats();
588 self.floats = LogicalSides1D::default();
589 },
590 Clear::None => {},
591 };
592 }
593 }
594
595 let accumulate =
596 |mut data: AccumulatedData,
597 (inline_content_sizes_result, float, clear): (InlineContentSizesResult, _, _)| {
598 let size = inline_content_sizes_result.sizes.max(ContentSizes::zero());
599 let depends_on_block_constraints =
600 inline_content_sizes_result.depends_on_block_constraints;
601 data.depends_on_block_constraints |= depends_on_block_constraints;
602 data.clear_floats(clear);
603 match float {
604 Some(FloatSide::InlineStart) => data.floats.start.union_assign(&size),
605 Some(FloatSide::InlineEnd) => data.floats.end.union_assign(&size),
606 None => {
607 data.max_size
608 .max_assign(data.floats.start.union(&data.floats.end).union(&size));
609 data.floats = LogicalSides1D::default();
610 },
611 }
612 data
613 };
614 let data = if layout_context.use_rayon {
615 boxes
616 .par_iter()
617 .filter_map(get_box_info)
618 .collect::<Vec<_>>()
619 .into_iter()
620 .fold(AccumulatedData::default(), accumulate)
621 } else {
622 boxes
623 .iter()
624 .filter_map(get_box_info)
625 .fold(AccumulatedData::default(), accumulate)
626 };
627 InlineContentSizesResult {
628 depends_on_block_constraints: data.depends_on_block_constraints,
629 sizes: data.max_size_including_uncleared_floats(),
630 }
631}
632
633impl BlockContainer {
634 fn layout(
635 &self,
636 layout_context: &LayoutContext,
637 positioning_context: &mut PositioningContext,
638 containing_block: &ContainingBlock,
639 sequential_layout_state: Option<&mut SequentialLayoutState>,
640 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
641 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
642 ) -> CacheableLayoutResult {
643 match self {
644 BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children(
645 layout_context,
646 positioning_context,
647 child_boxes,
648 containing_block,
649 sequential_layout_state,
650 collapsible_with_parent_start_margin,
651 ignore_block_margins_for_stretch,
652 ),
653 BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
654 layout_context,
655 positioning_context,
656 containing_block,
657 sequential_layout_state,
658 collapsible_with_parent_start_margin,
659 ),
660 }
661 }
662
663 #[inline]
664 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
665 LayoutStyle::Default(&base.style)
666 }
667
668 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
669 match self {
670 Self::BlockLevelBoxes(child_boxes) => {
671 for child_box in child_boxes {
672 child_box.borrow_mut().with_base_mut(|base| {
673 base.parent_box.replace(layout_box.clone());
674 });
675 }
676 },
677 Self::InlineFormattingContext(ifc) => ifc.attached_to_tree(layout_box),
678 }
679 }
680
681 fn find_block_margin_collapsing_with_parent(
682 &self,
683 layout_context: &LayoutContext,
684 collected_margin: &mut CollapsedMargin,
685 containing_block_for_children: &ContainingBlock,
686 ) -> bool {
687 match self {
688 BlockContainer::BlockLevelBoxes(boxes) => boxes.iter().all(|block_level_box| {
689 block_level_box
690 .borrow()
691 .find_block_margin_collapsing_with_parent(
692 layout_context,
693 collected_margin,
694 containing_block_for_children,
695 )
696 }),
697 BlockContainer::InlineFormattingContext(context) => context
698 .find_block_margin_collapsing_with_parent(
699 layout_context,
700 collected_margin,
701 containing_block_for_children,
702 ),
703 }
704 }
705}
706
707impl ComputeInlineContentSizes for BlockContainer {
708 fn compute_inline_content_sizes(
709 &self,
710 layout_context: &LayoutContext,
711 constraint_space: &ConstraintSpace,
712 ) -> InlineContentSizesResult {
713 match &self {
714 Self::BlockLevelBoxes(boxes) => compute_inline_content_sizes_for_block_level_boxes(
715 boxes,
716 layout_context,
717 &constraint_space.into(),
718 ),
719 Self::InlineFormattingContext(context) => {
720 context.compute_inline_content_sizes(layout_context, constraint_space)
721 },
722 }
723 }
724}
725
726fn layout_block_level_children(
727 layout_context: &LayoutContext,
728 positioning_context: &mut PositioningContext,
729 child_boxes: &[ArcRefCell<BlockLevelBox>],
730 containing_block: &ContainingBlock,
731 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
732 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
733 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
734) -> CacheableLayoutResult {
735 let mut placement_state =
736 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
737
738 let fragments = match sequential_layout_state {
739 Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
740 layout_context,
741 positioning_context,
742 child_boxes,
743 containing_block,
744 sequential_layout_state,
745 &mut placement_state,
746 ignore_block_margins_for_stretch,
747 ),
748 None => layout_block_level_children_in_parallel(
749 layout_context,
750 positioning_context,
751 child_boxes,
752 containing_block,
753 &mut placement_state,
754 ignore_block_margins_for_stretch,
755 ),
756 };
757
758 let depends_on_block_constraints = fragments.iter().any(|fragment| {
759 fragment.base().is_some_and(|base| {
760 base.flags.contains(
761 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
762 )
763 })
764 });
765
766 let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
767 CacheableLayoutResult {
768 fragments,
769 content_block_size,
770 collapsible_margins_in_children,
771 baselines,
772 depends_on_block_constraints,
773 content_inline_size_for_table: None,
774 specific_layout_info: None,
775 }
776}
777
778fn layout_block_level_children_in_parallel(
779 layout_context: &LayoutContext,
780 positioning_context: &mut PositioningContext,
781 child_boxes: &[ArcRefCell<BlockLevelBox>],
782 containing_block: &ContainingBlock,
783 placement_state: &mut PlacementState,
784 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
785) -> Vec<Fragment> {
786 let mut layout_results: Vec<(Fragment, PositioningContext)> =
787 Vec::with_capacity(child_boxes.len());
788
789 child_boxes
790 .par_iter()
791 .map(|child_box| {
792 let mut child_positioning_context = PositioningContext::default();
793 let fragment = child_box.borrow().layout(
794 layout_context,
795 &mut child_positioning_context,
796 containing_block,
797 None,
798 None,
799 ignore_block_margins_for_stretch,
800 );
801 (fragment, child_positioning_context)
802 })
803 .collect_into_vec(&mut layout_results);
804
805 layout_results
806 .into_iter()
807 .map(|(mut fragment, mut child_positioning_context)| {
808 placement_state.place_fragment_and_update_baseline(&mut fragment, None);
809 child_positioning_context.adjust_static_position_of_hoisted_fragments(
810 &fragment,
811 PositioningContextLength::zero(),
812 );
813 positioning_context.append(child_positioning_context);
814 fragment
815 })
816 .collect()
817}
818
819fn layout_block_level_children_sequentially(
820 layout_context: &LayoutContext,
821 positioning_context: &mut PositioningContext,
822 child_boxes: &[ArcRefCell<BlockLevelBox>],
823 containing_block: &ContainingBlock,
824 sequential_layout_state: &mut SequentialLayoutState,
825 placement_state: &mut PlacementState,
826 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
827) -> Vec<Fragment> {
828 child_boxes
832 .iter()
833 .map(|child_box| {
834 let positioning_context_length_before_layout = positioning_context.len();
835 let mut fragment = child_box.borrow().layout(
836 layout_context,
837 positioning_context,
838 containing_block,
839 Some(&mut *sequential_layout_state),
840 Some(CollapsibleWithParentStartMargin(
841 placement_state.next_in_flow_margin_collapses_with_parent_start_margin,
842 )),
843 ignore_block_margins_for_stretch,
844 );
845
846 placement_state
847 .place_fragment_and_update_baseline(&mut fragment, Some(sequential_layout_state));
848 positioning_context.adjust_static_position_of_hoisted_fragments(
849 &fragment,
850 positioning_context_length_before_layout,
851 );
852
853 fragment
854 })
855 .collect()
856}
857
858impl BlockLevelBox {
859 fn layout(
860 &self,
861 layout_context: &LayoutContext,
862 positioning_context: &mut PositioningContext,
863 containing_block: &ContainingBlock,
864 sequential_layout_state: Option<&mut SequentialLayoutState>,
865 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
866 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
867 ) -> Fragment {
868 let fragment = match self {
869 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => Fragment::Box(
870 ArcRefCell::new(positioning_context.layout_maybe_position_relative_fragment(
871 layout_context,
872 containing_block,
873 base,
874 |positioning_context| {
875 layout_in_flow_non_replaced_block_level_same_formatting_context(
876 layout_context,
877 positioning_context,
878 containing_block,
879 base,
880 contents,
881 sequential_layout_state,
882 collapsible_with_parent_start_margin,
883 ignore_block_margins_for_stretch,
884 )
885 },
886 )),
887 ),
888 BlockLevelBox::Independent(independent) => Fragment::Box(ArcRefCell::new(
889 positioning_context.layout_maybe_position_relative_fragment(
890 layout_context,
891 containing_block,
892 &independent.base,
893 |positioning_context| {
894 independent.layout_in_flow_block_level(
895 layout_context,
896 positioning_context,
897 containing_block,
898 sequential_layout_state,
899 ignore_block_margins_for_stretch,
900 )
901 },
902 ),
903 )),
904 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
905 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
909 box_.clone(),
910 PhysicalRect::zero(),
914 LogicalVec2 {
915 inline: AlignFlags::START,
916 block: AlignFlags::START,
917 },
918 containing_block.style.writing_mode,
919 );
920 let hoisted_fragment = hoisted_box.fragment.clone();
921 positioning_context.push(hoisted_box);
922 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
923 },
924 BlockLevelBox::OutOfFlowFloatBox(float_box) => Fragment::Float(ArcRefCell::new(
925 float_box.layout(layout_context, positioning_context, containing_block),
926 )),
927 BlockLevelBox::OutsideMarker(outside_marker) => {
928 outside_marker.layout(layout_context, containing_block, positioning_context)
929 },
930 };
931
932 self.with_base(|base| base.set_fragment(fragment.clone()));
933
934 fragment
935 }
936
937 fn inline_content_sizes(
938 &self,
939 layout_context: &LayoutContext,
940 constraint_space: &ConstraintSpace,
941 ) -> InlineContentSizesResult {
942 let independent_formatting_context = match self {
943 BlockLevelBox::Independent(independent) => independent,
944 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => &box_.borrow().context,
945 BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents,
946 BlockLevelBox::OutsideMarker(outside_marker) => {
947 return outside_marker.inline_content_sizes(layout_context, constraint_space);
948 },
949 BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
950 return base.inline_content_sizes(layout_context, constraint_space, contents);
951 },
952 };
953 independent_formatting_context.inline_content_sizes(layout_context, constraint_space)
954 }
955}
956
957#[allow(clippy::too_many_arguments)]
963pub(crate) fn layout_in_flow_non_replaced_block_level_same_formatting_context(
964 layout_context: &LayoutContext,
965 positioning_context: &mut PositioningContext,
966 containing_block: &ContainingBlock,
967 base: &LayoutBoxBase,
968 contents: &BlockContainer,
969 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
970 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
971 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
972) -> BoxFragment {
973 let style = &base.style;
974 let layout_style = contents.layout_style(base);
975 let containing_block_writing_mode = containing_block.style.writing_mode;
976 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
977 base.inline_content_sizes(layout_context, constraint_space, contents)
978 .sizes
979 };
980 let ContainingBlockPaddingAndBorder {
981 containing_block: containing_block_for_children,
982 pbm,
983 block_sizes,
984 depends_on_block_constraints,
985 available_block_size,
986 justify_self,
987 ..
988 } = solve_containing_block_padding_and_border_for_in_flow_box(
989 containing_block,
990 &layout_style,
991 get_inline_content_sizes,
992 ignore_block_margins_for_stretch,
993 None,
994 );
995 let ResolvedMargins {
996 margin,
997 effective_margin_inline_start,
998 } = solve_margins(
999 containing_block,
1000 &pbm,
1001 containing_block_for_children.size.inline,
1002 justify_self,
1003 );
1004
1005 let start_margin_can_collapse_with_children =
1006 pbm.padding.block_start.is_zero() && pbm.border.block_start.is_zero();
1007
1008 let mut clearance = None;
1009 let parent_containing_block_position_info;
1010 match sequential_layout_state {
1011 None => parent_containing_block_position_info = None,
1012 Some(ref mut sequential_layout_state) => {
1013 let clear =
1014 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode);
1015 let mut block_start_margin = CollapsedMargin::new(margin.block_start);
1016
1017 let collapsible_with_parent_start_margin = collapsible_with_parent_start_margin.expect(
1029 "We should know whether we are collapsing the block start margin with the parent \
1030 when laying out sequentially",
1031 ).0 && clear == Clear::None;
1032 if !collapsible_with_parent_start_margin && start_margin_can_collapse_with_children {
1033 contents.find_block_margin_collapsing_with_parent(
1034 layout_context,
1035 &mut block_start_margin,
1036 &containing_block_for_children,
1037 );
1038 }
1039
1040 clearance = sequential_layout_state.calculate_clearance(clear, &block_start_margin);
1042 if clearance.is_some() {
1043 sequential_layout_state.collapse_margins();
1044 }
1045 sequential_layout_state.adjoin_assign(&block_start_margin);
1046 if !start_margin_can_collapse_with_children {
1047 sequential_layout_state.collapse_margins();
1048 }
1049
1050 sequential_layout_state.advance_block_position(
1053 pbm.padding.block_start +
1054 pbm.border.block_start +
1055 clearance.unwrap_or_else(Au::zero),
1056 );
1057
1058 let inline_start = sequential_layout_state
1064 .floats
1065 .containing_block_info
1066 .inline_start +
1067 pbm.padding.inline_start +
1068 pbm.border.inline_start +
1069 effective_margin_inline_start;
1070 let new_cb_offsets = ContainingBlockPositionInfo {
1071 block_start: sequential_layout_state.bfc_relative_block_position,
1072 block_start_margins_not_collapsed: sequential_layout_state.current_margin,
1073 inline_start,
1074 inline_end: inline_start + containing_block_for_children.size.inline,
1075 };
1076 parent_containing_block_position_info = Some(
1077 sequential_layout_state.replace_containing_block_position_info(new_cb_offsets),
1078 );
1079 },
1080 };
1081
1082 let ignore_block_margins_for_stretch = LogicalSides1D::new(
1088 pbm.border.block_start.is_zero() && pbm.padding.block_start.is_zero(),
1089 pbm.border.block_end.is_zero() && pbm.padding.block_end.is_zero(),
1090 );
1091
1092 let flow_layout = contents.layout(
1093 layout_context,
1094 positioning_context,
1095 &containing_block_for_children,
1096 sequential_layout_state.as_deref_mut(),
1097 CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
1098 ignore_block_margins_for_stretch,
1099 );
1100 let mut content_block_size = flow_layout.content_block_size;
1101
1102 let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1104 let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
1105 if start_margin_can_collapse_with_children {
1106 block_margins_collapsed_with_children
1107 .start
1108 .adjoin_assign(&collapsible_margins_in_children.start);
1109 if collapsible_margins_in_children.collapsed_through {
1110 block_margins_collapsed_with_children
1111 .start
1112 .adjoin_assign(&std::mem::replace(
1113 &mut collapsible_margins_in_children.end,
1114 CollapsedMargin::zero(),
1115 ));
1116 }
1117 }
1118
1119 let tentative_block_size = &containing_block_for_children.size.block;
1120 let collapsed_through = collapsible_margins_in_children.collapsed_through &&
1121 pbm.padding_border_sums.block.is_zero() &&
1122 tentative_block_size.definite_or_min().is_zero();
1123 block_margins_collapsed_with_children.collapsed_through = collapsed_through;
1124
1125 let end_margin_can_collapse_with_children =
1126 pbm.padding.block_end.is_zero() && pbm.border.block_end.is_zero();
1127 if !end_margin_can_collapse_with_children {
1128 content_block_size += collapsible_margins_in_children.end.solve();
1129 }
1130
1131 let block_size = block_sizes.resolve(
1132 Direction::Block,
1133 Size::FitContent,
1134 Au::zero,
1135 available_block_size,
1136 || content_block_size.into(),
1137 false, );
1139
1140 let end_margin_can_collapse_with_children = end_margin_can_collapse_with_children &&
1155 block_size == content_block_size &&
1156 (collapsed_through || !tentative_block_size.is_definite());
1157 if end_margin_can_collapse_with_children {
1158 block_margins_collapsed_with_children
1159 .end
1160 .adjoin_assign(&collapsible_margins_in_children.end);
1161 }
1162
1163 if let Some(ref mut sequential_layout_state) = sequential_layout_state {
1164 sequential_layout_state
1167 .replace_containing_block_position_info(parent_containing_block_position_info.unwrap());
1168
1169 sequential_layout_state.advance_block_position(
1179 block_size - content_block_size + pbm.padding.block_end + pbm.border.block_end,
1180 );
1181
1182 if !end_margin_can_collapse_with_children {
1183 sequential_layout_state.collapse_margins();
1184 }
1185 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1186 }
1187
1188 let content_rect = LogicalRect {
1189 start_corner: LogicalVec2 {
1190 block: (pbm.padding.block_start +
1191 pbm.border.block_start +
1192 clearance.unwrap_or_else(Au::zero)),
1193 inline: pbm.padding.inline_start +
1194 pbm.border.inline_start +
1195 effective_margin_inline_start,
1196 },
1197 size: LogicalVec2 {
1198 block: block_size,
1199 inline: containing_block_for_children.size.inline,
1200 },
1201 };
1202
1203 let mut base_fragment_info = base.base_fragment_info;
1204
1205 let is_anonymous = matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
1209 if depends_on_block_constraints || (is_anonymous && flow_layout.depends_on_block_constraints) {
1210 base_fragment_info
1211 .flags
1212 .insert(FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM);
1213 }
1214
1215 BoxFragment::new(
1216 base_fragment_info,
1217 style.clone(),
1218 flow_layout.fragments,
1219 content_rect.as_physical(Some(containing_block)),
1220 pbm.padding.to_physical(containing_block_writing_mode),
1221 pbm.border.to_physical(containing_block_writing_mode),
1222 margin.to_physical(containing_block_writing_mode),
1223 flow_layout.specific_layout_info,
1224 )
1225 .with_baselines(flow_layout.baselines)
1226 .with_block_level_layout_info(block_margins_collapsed_with_children, clearance)
1227}
1228
1229impl IndependentFormattingContext {
1230 pub(crate) fn layout_in_flow_block_level(
1238 &self,
1239 layout_context: &LayoutContext,
1240 positioning_context: &mut PositioningContext,
1241 containing_block: &ContainingBlock,
1242 sequential_layout_state: Option<&mut SequentialLayoutState>,
1243 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1244 ) -> BoxFragment {
1245 if let Some(sequential_layout_state) = sequential_layout_state {
1246 return self.layout_in_flow_block_level_sequentially(
1247 layout_context,
1248 positioning_context,
1249 containing_block,
1250 sequential_layout_state,
1251 ignore_block_margins_for_stretch,
1252 );
1253 }
1254
1255 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1256 self.inline_content_sizes(layout_context, constraint_space)
1257 .sizes
1258 };
1259 let layout_style = self.layout_style();
1260 let ContainingBlockPaddingAndBorder {
1261 containing_block: containing_block_for_children,
1262 pbm,
1263 block_sizes,
1264 depends_on_block_constraints,
1265 available_block_size,
1266 justify_self,
1267 preferred_aspect_ratio,
1268 } = solve_containing_block_padding_and_border_for_in_flow_box(
1269 containing_block,
1270 &layout_style,
1271 get_inline_content_sizes,
1272 ignore_block_margins_for_stretch,
1273 Some(self),
1274 );
1275
1276 let lazy_block_size = LazySize::new(
1277 &block_sizes,
1278 Direction::Block,
1279 Size::FitContent,
1280 Au::zero,
1281 available_block_size,
1282 layout_style.is_table(),
1283 );
1284
1285 let layout = self.layout(
1286 layout_context,
1287 positioning_context,
1288 &containing_block_for_children,
1289 containing_block,
1290 preferred_aspect_ratio,
1291 &lazy_block_size,
1292 );
1293
1294 let inline_size = layout
1295 .content_inline_size_for_table
1296 .unwrap_or(containing_block_for_children.size.inline);
1297 let block_size = lazy_block_size.resolve(|| layout.content_block_size);
1298
1299 let ResolvedMargins {
1300 margin,
1301 effective_margin_inline_start,
1302 } = solve_margins(containing_block, &pbm, inline_size, justify_self);
1303
1304 let content_rect = LogicalRect {
1305 start_corner: LogicalVec2 {
1306 block: pbm.padding.block_start + pbm.border.block_start,
1307 inline: pbm.padding.inline_start +
1308 pbm.border.inline_start +
1309 effective_margin_inline_start,
1310 },
1311 size: LogicalVec2 {
1312 block: block_size,
1313 inline: inline_size,
1314 },
1315 };
1316
1317 let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1318 let containing_block_writing_mode = containing_block.style.writing_mode;
1319
1320 let mut base_fragment_info = self.base.base_fragment_info;
1321 if depends_on_block_constraints {
1322 base_fragment_info.flags.insert(
1323 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1324 );
1325 }
1326 BoxFragment::new(
1327 base_fragment_info,
1328 self.base.style.clone(),
1329 layout.fragments,
1330 content_rect.as_physical(Some(containing_block)),
1331 pbm.padding.to_physical(containing_block_writing_mode),
1332 pbm.border.to_physical(containing_block_writing_mode),
1333 margin.to_physical(containing_block_writing_mode),
1334 layout.specific_layout_info,
1335 )
1336 .with_baselines(layout.baselines)
1337 .with_block_level_layout_info(block_margins_collapsed_with_children, None)
1338 }
1339
1340 fn layout_in_flow_block_level_sequentially(
1344 &self,
1345 layout_context: &LayoutContext<'_>,
1346 positioning_context: &mut PositioningContext,
1347 containing_block: &ContainingBlock<'_>,
1348 sequential_layout_state: &mut SequentialLayoutState,
1349 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1350 ) -> BoxFragment {
1351 let style = &self.base.style;
1352 let containing_block_writing_mode = containing_block.style.writing_mode;
1353 let ContentBoxSizesAndPBM {
1354 content_box_sizes,
1355 pbm,
1356 depends_on_block_constraints,
1357 ..
1358 } = self
1359 .layout_style()
1360 .content_box_sizes_and_padding_border_margin(&containing_block.into());
1361
1362 let (margin_block_start, margin_block_end) =
1363 solve_block_margins_for_in_flow_block_level(&pbm);
1364 let collapsed_margin_block_start = CollapsedMargin::new(margin_block_start);
1365
1366 let mut content_size;
1377 let mut layout;
1378 let mut placement_rect;
1379
1380 let clear_position = sequential_layout_state.calculate_clear_position(
1384 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode),
1385 &collapsed_margin_block_start,
1386 );
1387 let ceiling = clear_position.unwrap_or_else(|| {
1388 sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
1389 });
1390
1391 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1393 let available_block_size = containing_block
1394 .size
1395 .block
1396 .to_definite()
1397 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1398 let is_table = self.is_table();
1399 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
1400 let tentative_block_content_size =
1401 self.tentative_block_content_size(preferred_aspect_ratio);
1402 let (preferred_block_size, min_block_size, max_block_size) =
1403 if let Some(block_content_size) = tentative_block_content_size {
1404 let (preferred, min, max) = content_box_sizes.block.resolve_each(
1405 Size::FitContent,
1406 Au::zero,
1407 available_block_size,
1408 || block_content_size,
1409 is_table,
1410 );
1411 (Some(preferred), min, max)
1412 } else {
1413 content_box_sizes.block.resolve_each_extrinsic(
1414 Size::FitContent,
1415 Au::zero(),
1416 available_block_size,
1417 )
1418 };
1419 let tentative_block_size =
1420 SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
1421
1422 let get_inline_content_sizes = || {
1424 let constraint_space =
1425 ConstraintSpace::new(tentative_block_size, style, preferred_aspect_ratio);
1426 self.inline_content_sizes(layout_context, &constraint_space)
1427 .sizes
1428 };
1429
1430 let justify_self = resolve_justify_self(style, containing_block.style);
1431 let automatic_inline_size = automatic_inline_size(justify_self, Some(self));
1432 let compute_inline_size = |stretch_size| {
1433 content_box_sizes.inline.resolve(
1434 Direction::Inline,
1435 automatic_inline_size,
1436 Au::zero,
1437 Some(stretch_size),
1438 get_inline_content_sizes,
1439 is_table,
1440 )
1441 };
1442
1443 let get_lazy_block_size = || {
1444 LazySize::new(
1445 &content_box_sizes.block,
1446 Direction::Block,
1447 Size::FitContent,
1448 Au::zero,
1449 available_block_size,
1450 is_table,
1451 )
1452 };
1453
1454 let inline_size_with_no_available_space = compute_inline_size(Au::zero());
1463 if inline_size_with_no_available_space == compute_inline_size(MAX_AU) {
1464 let inline_size = inline_size_with_no_available_space;
1468 let lazy_block_size = get_lazy_block_size();
1469 layout = self.layout(
1470 layout_context,
1471 positioning_context,
1472 &ContainingBlock {
1473 size: ContainingBlockSize {
1474 inline: inline_size,
1475 block: tentative_block_size,
1476 },
1477 style,
1478 },
1479 containing_block,
1480 preferred_aspect_ratio,
1481 &lazy_block_size,
1482 );
1483
1484 content_size = LogicalVec2 {
1485 block: lazy_block_size.resolve(|| layout.content_block_size),
1486 inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
1487 };
1488
1489 let mut placement = PlacementAmongFloats::new(
1490 &sequential_layout_state.floats,
1491 ceiling,
1492 content_size + pbm.padding_border_sums,
1493 &pbm,
1494 );
1495 placement_rect = placement.place();
1496 } else {
1497 let minimum_size_of_block = LogicalVec2 {
1503 inline: inline_size_with_no_available_space,
1507 block: match tentative_block_size {
1508 SizeConstraint::Definite(size) if max_block_size.is_some() => size,
1511 _ => min_block_size,
1514 },
1515 } + pbm.padding_border_sums;
1516 let mut placement = PlacementAmongFloats::new(
1517 &sequential_layout_state.floats,
1518 ceiling,
1519 minimum_size_of_block,
1520 &pbm,
1521 );
1522
1523 loop {
1524 placement_rect = placement.place();
1526 let available_inline_size =
1527 placement_rect.size.inline - pbm.padding_border_sums.inline;
1528 let proposed_inline_size = compute_inline_size(available_inline_size);
1529
1530 let positioning_context_length = positioning_context.len();
1534 let lazy_block_size = get_lazy_block_size();
1535 layout = self.layout(
1536 layout_context,
1537 positioning_context,
1538 &ContainingBlock {
1539 size: ContainingBlockSize {
1540 inline: proposed_inline_size,
1541 block: tentative_block_size,
1542 },
1543 style,
1544 },
1545 containing_block,
1546 preferred_aspect_ratio,
1547 &lazy_block_size,
1548 );
1549
1550 let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
1551 debug_assert!(inline_size < proposed_inline_size);
1556 inline_size
1557 } else {
1558 proposed_inline_size
1559 };
1560 content_size = LogicalVec2 {
1561 block: lazy_block_size.resolve(|| layout.content_block_size),
1562 inline: inline_size,
1563 };
1564
1565 if placement.try_to_expand_for_auto_block_size(
1569 content_size.block + pbm.padding_border_sums.block,
1570 &placement_rect.size,
1571 ) {
1572 break;
1573 }
1574
1575 positioning_context.truncate(&positioning_context_length);
1579 }
1580 }
1581
1582 let has_clearance = clear_position.is_some() || placement_rect.start_corner.block > ceiling;
1586 let clearance = has_clearance.then(|| {
1587 placement_rect.start_corner.block -
1588 sequential_layout_state
1589 .position_with_zero_clearance(&collapsed_margin_block_start)
1590 });
1591
1592 let ((margin_inline_start, margin_inline_end), effective_margin_inline_start) =
1593 solve_inline_margins_avoiding_floats(
1594 sequential_layout_state,
1595 containing_block,
1596 &pbm,
1597 content_size.inline + pbm.padding_border_sums.inline,
1598 placement_rect,
1599 justify_self,
1600 );
1601
1602 let margin = LogicalSides {
1603 inline_start: margin_inline_start,
1604 inline_end: margin_inline_end,
1605 block_start: margin_block_start,
1606 block_end: margin_block_end,
1607 };
1608
1609 if clearance.is_some() {
1612 sequential_layout_state.collapse_margins();
1613 }
1614 sequential_layout_state.adjoin_assign(&collapsed_margin_block_start);
1615
1616 sequential_layout_state.collapse_margins();
1618 sequential_layout_state.advance_block_position(
1619 pbm.padding_border_sums.block + content_size.block + clearance.unwrap_or_else(Au::zero),
1620 );
1621 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1622
1623 let content_rect = LogicalRect {
1624 start_corner: LogicalVec2 {
1625 block: pbm.padding.block_start +
1626 pbm.border.block_start +
1627 clearance.unwrap_or_else(Au::zero),
1628 inline: pbm.padding.inline_start +
1629 pbm.border.inline_start +
1630 effective_margin_inline_start,
1631 },
1632 size: content_size,
1633 };
1634
1635 let mut base_fragment_info = self.base.base_fragment_info;
1636 if depends_on_block_constraints {
1637 base_fragment_info.flags.insert(
1638 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1639 );
1640 }
1641
1642 BoxFragment::new(
1643 base_fragment_info,
1644 style.clone(),
1645 layout.fragments,
1646 content_rect.as_physical(Some(containing_block)),
1647 pbm.padding.to_physical(containing_block_writing_mode),
1648 pbm.border.to_physical(containing_block_writing_mode),
1649 margin.to_physical(containing_block_writing_mode),
1650 layout.specific_layout_info,
1651 )
1652 .with_baselines(layout.baselines)
1653 .with_block_level_layout_info(CollapsedBlockMargins::from_margin(&margin), clearance)
1654 }
1655}
1656
1657struct ContainingBlockPaddingAndBorder<'a> {
1658 containing_block: ContainingBlock<'a>,
1659 pbm: PaddingBorderMargin,
1660 block_sizes: Sizes,
1661 depends_on_block_constraints: bool,
1662 available_block_size: Option<Au>,
1663 justify_self: AlignFlags,
1664 preferred_aspect_ratio: Option<AspectRatio>,
1665}
1666
1667struct ResolvedMargins {
1668 pub margin: LogicalSides<Au>,
1670
1671 pub effective_margin_inline_start: Au,
1678}
1679
1680fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
1686 containing_block: &ContainingBlock<'_>,
1687 layout_style: &'a LayoutStyle,
1688 get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
1689 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1690 context: Option<&IndependentFormattingContext>,
1691) -> ContainingBlockPaddingAndBorder<'a> {
1692 let style = layout_style.style();
1693 if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
1694 let containing_block_for_children = ContainingBlock {
1698 size: ContainingBlockSize {
1699 inline: containing_block.size.inline,
1700 block: containing_block.size.block,
1701 },
1702 style,
1703 };
1704 return ContainingBlockPaddingAndBorder {
1707 containing_block: containing_block_for_children,
1708 pbm: PaddingBorderMargin::zero(),
1709 block_sizes: Sizes::default(),
1710 depends_on_block_constraints: false,
1711 available_block_size: None,
1714 justify_self: AlignFlags::NORMAL,
1717 preferred_aspect_ratio: None,
1718 };
1719 }
1720
1721 let ContentBoxSizesAndPBM {
1722 content_box_sizes,
1723 pbm,
1724 depends_on_block_constraints,
1725 ..
1726 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
1727
1728 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1729 let available_inline_size = Au::zero().max(containing_block.size.inline - pbm_sums.inline);
1730 let available_block_size = containing_block
1731 .size
1732 .block
1733 .to_definite()
1734 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1735
1736 let preferred_aspect_ratio =
1739 context.and_then(|context| context.preferred_aspect_ratio(&pbm.padding_border_sums));
1740 let is_table = layout_style.is_table();
1741
1742 let tentative_block_content_size =
1745 context.and_then(|context| context.tentative_block_content_size(preferred_aspect_ratio));
1746 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
1747 SizeConstraint::Definite(content_box_sizes.block.resolve(
1748 Direction::Block,
1749 Size::FitContent,
1750 Au::zero,
1751 available_block_size,
1752 || block_content_size,
1753 is_table,
1754 ))
1755 } else {
1756 content_box_sizes.block.resolve_extrinsic(
1757 Size::FitContent,
1758 Au::zero(),
1759 available_block_size,
1760 )
1761 };
1762
1763 let get_inline_content_sizes = || {
1766 get_inline_content_sizes(&ConstraintSpace::new(
1767 tentative_block_size,
1768 style,
1769 preferred_aspect_ratio,
1770 ))
1771 };
1772 let justify_self = resolve_justify_self(style, containing_block.style);
1773 let inline_size = content_box_sizes.inline.resolve(
1774 Direction::Inline,
1775 automatic_inline_size(justify_self, context),
1776 Au::zero,
1777 Some(available_inline_size),
1778 get_inline_content_sizes,
1779 is_table,
1780 );
1781
1782 let containing_block_for_children = ContainingBlock {
1783 size: ContainingBlockSize {
1784 inline: inline_size,
1785 block: tentative_block_size,
1786 },
1787 style,
1788 };
1789 assert_eq!(
1791 containing_block.style.writing_mode.is_horizontal(),
1792 containing_block_for_children
1793 .style
1794 .writing_mode
1795 .is_horizontal(),
1796 "Vertical writing modes are not supported yet"
1797 );
1798 ContainingBlockPaddingAndBorder {
1799 containing_block: containing_block_for_children,
1800 pbm,
1801 block_sizes: content_box_sizes.block,
1802 depends_on_block_constraints,
1803 available_block_size,
1804 justify_self,
1805 preferred_aspect_ratio,
1806 }
1807}
1808
1809fn solve_margins(
1814 containing_block: &ContainingBlock<'_>,
1815 pbm: &PaddingBorderMargin,
1816 inline_size: Au,
1817 justify_self: AlignFlags,
1818) -> ResolvedMargins {
1819 let (inline_margins, effective_margin_inline_start) =
1820 solve_inline_margins_for_in_flow_block_level(
1821 containing_block,
1822 pbm,
1823 inline_size,
1824 justify_self,
1825 );
1826 let block_margins = solve_block_margins_for_in_flow_block_level(pbm);
1827 ResolvedMargins {
1828 margin: LogicalSides {
1829 inline_start: inline_margins.0,
1830 inline_end: inline_margins.1,
1831 block_start: block_margins.0,
1832 block_end: block_margins.1,
1833 },
1834 effective_margin_inline_start,
1835 }
1836}
1837
1838fn solve_block_margins_for_in_flow_block_level(pbm: &PaddingBorderMargin) -> (Au, Au) {
1842 (
1843 pbm.margin.block_start.auto_is(Au::zero),
1844 pbm.margin.block_end.auto_is(Au::zero),
1845 )
1846}
1847
1848fn resolve_justify_self(style: &ComputedValues, parent_style: &ComputedValues) -> AlignFlags {
1850 let is_ltr = |style: &ComputedValues| style.writing_mode.line_left_is_inline_start();
1851 let alignment = match style.clone_justify_self().0 {
1852 AlignFlags::AUTO => parent_style.clone_justify_items().computed.0.0,
1853 alignment => alignment,
1854 };
1855 let alignment_value = match alignment.value() {
1856 AlignFlags::LEFT if is_ltr(parent_style) => AlignFlags::START,
1857 AlignFlags::LEFT => AlignFlags::END,
1858 AlignFlags::RIGHT if is_ltr(parent_style) => AlignFlags::END,
1859 AlignFlags::RIGHT => AlignFlags::START,
1860 AlignFlags::SELF_START if is_ltr(parent_style) == is_ltr(style) => AlignFlags::START,
1861 AlignFlags::SELF_START => AlignFlags::END,
1862 AlignFlags::SELF_END if is_ltr(parent_style) == is_ltr(style) => AlignFlags::END,
1863 AlignFlags::SELF_END => AlignFlags::START,
1864 alignment_value => alignment_value,
1865 };
1866 alignment.flags() | alignment_value
1867}
1868
1869#[inline]
1872fn automatic_inline_size<T>(
1873 justify_self: AlignFlags,
1874 context: Option<&IndependentFormattingContext>,
1875) -> Size<T> {
1876 let normal_stretches = || {
1877 !context.is_some_and(|context| {
1878 context
1879 .base
1880 .base_fragment_info
1881 .flags
1882 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET) ||
1883 context.is_table()
1884 })
1885 };
1886 match justify_self {
1887 AlignFlags::STRETCH => Size::Stretch,
1888 AlignFlags::NORMAL if normal_stretches() => Size::Stretch,
1889 _ => Size::FitContent,
1890 }
1891}
1892
1893fn justify_self_alignment(
1900 containing_block: &ContainingBlock,
1901 free_space: Au,
1902 justify_self: AlignFlags,
1903) -> Au {
1904 let mut alignment = justify_self.value();
1905 let is_safe = justify_self.flags() == AlignFlags::SAFE || alignment == AlignFlags::NORMAL;
1906 if is_safe && free_space <= Au::zero() {
1907 alignment = AlignFlags::START
1908 }
1909 match alignment {
1910 AlignFlags::NORMAL => {},
1911 AlignFlags::CENTER => return free_space / 2,
1912 AlignFlags::END => return free_space,
1913 _ => return Au::zero(),
1914 }
1915
1916 let style = containing_block.style;
1918 match style.clone_text_align() {
1919 TextAlignKeyword::MozCenter => free_space / 2,
1920 TextAlignKeyword::MozLeft if !style.writing_mode.line_left_is_inline_start() => free_space,
1921 TextAlignKeyword::MozRight if style.writing_mode.line_left_is_inline_start() => free_space,
1922 _ => Au::zero(),
1923 }
1924}
1925
1926fn solve_inline_margins_for_in_flow_block_level(
1939 containing_block: &ContainingBlock,
1940 pbm: &PaddingBorderMargin,
1941 inline_size: Au,
1942 justify_self: AlignFlags,
1943) -> ((Au, Au), Au) {
1944 let free_space = containing_block.size.inline - pbm.padding_border_sums.inline - inline_size;
1945 let mut justification = Au::zero();
1946 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
1947 (AuOrAuto::Auto, AuOrAuto::Auto) => {
1948 let start = Au::zero().max(free_space / 2);
1949 (start, free_space - start)
1950 },
1951 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
1952 (Au::zero().max(free_space - end), end)
1953 },
1954 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, free_space - start),
1955 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
1956 justification =
1961 justify_self_alignment(containing_block, free_space - start - end, justify_self);
1962 (start, end)
1963 },
1964 };
1965 let effective_margin_inline_start = inline_margins.0 + justification;
1966 (inline_margins, effective_margin_inline_start)
1967}
1968
1969fn solve_inline_margins_avoiding_floats(
1979 sequential_layout_state: &SequentialLayoutState,
1980 containing_block: &ContainingBlock,
1981 pbm: &PaddingBorderMargin,
1982 inline_size: Au,
1983 placement_rect: LogicalRect<Au>,
1984 justify_self: AlignFlags,
1985) -> ((Au, Au), Au) {
1986 let free_space = Au::zero().max(placement_rect.size.inline - inline_size);
1990 let cb_info = &sequential_layout_state.floats.containing_block_info;
1991 let start_adjustment = placement_rect.start_corner.inline - cb_info.inline_start;
1992 let end_adjustment = cb_info.inline_end - placement_rect.max_inline_position();
1993 let mut justification = Au::zero();
1994 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
1995 (AuOrAuto::Auto, AuOrAuto::Auto) => {
1996 let half = free_space / 2;
1997 (start_adjustment + half, end_adjustment + free_space - half)
1998 },
1999 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (start_adjustment + free_space, end),
2000 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, end_adjustment + free_space),
2001 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
2002 justification = justify_self_alignment(containing_block, free_space, justify_self);
2008 (start, end)
2009 },
2010 };
2011 let effective_margin_inline_start = inline_margins.0.max(start_adjustment) + justification;
2012 (inline_margins, effective_margin_inline_start)
2013}
2014
2015struct PlacementState<'container> {
2020 next_in_flow_margin_collapses_with_parent_start_margin: bool,
2021 last_in_flow_margin_collapses_with_parent_end_margin: bool,
2022 start_margin: CollapsedMargin,
2023 current_margin: CollapsedMargin,
2024 current_block_direction_position: Au,
2025 inflow_baselines: Baselines,
2026 is_inline_block_context: bool,
2027
2028 marker_block_size: Option<Au>,
2033
2034 containing_block: &'container ContainingBlock<'container>,
2037}
2038
2039impl<'container> PlacementState<'container> {
2040 fn new(
2041 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2042 containing_block: &'container ContainingBlock<'container>,
2043 ) -> PlacementState<'container> {
2044 let is_inline_block_context =
2045 containing_block.style.get_box().clone_display() == Display::InlineBlock;
2046 PlacementState {
2047 next_in_flow_margin_collapses_with_parent_start_margin:
2048 collapsible_with_parent_start_margin.0,
2049 last_in_flow_margin_collapses_with_parent_end_margin: true,
2050 start_margin: CollapsedMargin::zero(),
2051 current_margin: CollapsedMargin::zero(),
2052 current_block_direction_position: Au::zero(),
2053 inflow_baselines: Baselines::default(),
2054 is_inline_block_context,
2055 marker_block_size: None,
2056 containing_block,
2057 }
2058 }
2059
2060 fn place_fragment_and_update_baseline(
2061 &mut self,
2062 fragment: &mut Fragment,
2063 sequential_layout_state: Option<&mut SequentialLayoutState>,
2064 ) {
2065 self.place_fragment(fragment, sequential_layout_state);
2066
2067 let box_fragment = match fragment {
2068 Fragment::Box(box_fragment) => box_fragment,
2069 _ => return,
2070 };
2071 let box_fragment = box_fragment.borrow();
2072
2073 if self.is_inline_block_context && box_fragment.is_table_wrapper() {
2078 return;
2079 }
2080
2081 let box_block_offset = box_fragment
2082 .content_rect()
2083 .origin
2084 .to_logical(self.containing_block)
2085 .block;
2086 let box_fragment_baselines =
2087 box_fragment.baselines(self.containing_block.style.writing_mode);
2088 if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) {
2089 self.inflow_baselines.first = Some(first + box_block_offset);
2090 }
2091 if let Some(last) = box_fragment_baselines.last {
2092 self.inflow_baselines.last = Some(last + box_block_offset);
2093 }
2094 }
2095
2096 fn place_fragment(
2099 &mut self,
2100 fragment: &mut Fragment,
2101 sequential_layout_state: Option<&mut SequentialLayoutState>,
2102 ) {
2103 match fragment {
2104 Fragment::Box(fragment) => {
2105 let fragment = &mut *fragment.borrow_mut();
2114 let is_outside_marker = fragment
2115 .base
2116 .flags
2117 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER);
2118 if is_outside_marker {
2119 assert!(self.marker_block_size.is_none());
2120 self.marker_block_size = Some(
2121 fragment
2122 .content_rect()
2123 .size
2124 .to_logical(self.containing_block.style.writing_mode)
2125 .block,
2126 );
2127 return;
2128 }
2129
2130 let BlockLevelLayoutInfo {
2131 clearance,
2132 block_margins_collapsed_with_children: fragment_block_margins,
2133 } = &**fragment
2134 .block_level_layout_info
2135 .as_ref()
2136 .expect("A block-level fragment should have a BlockLevelLayoutInfo.");
2137 let mut fragment_block_size = fragment
2138 .border_rect()
2139 .size
2140 .to_logical(self.containing_block.style.writing_mode)
2141 .block;
2142
2143 if let Some(clearance) = *clearance {
2149 fragment_block_size += clearance;
2150 self.current_block_direction_position += self.current_margin.solve();
2155 self.current_margin = CollapsedMargin::zero();
2156 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2157 if fragment_block_margins.collapsed_through {
2158 self.last_in_flow_margin_collapses_with_parent_end_margin = false;
2159 }
2160 } else if !fragment_block_margins.collapsed_through {
2161 self.last_in_flow_margin_collapses_with_parent_end_margin = true;
2162 }
2163
2164 if self.next_in_flow_margin_collapses_with_parent_start_margin {
2165 debug_assert!(self.current_margin.solve().is_zero());
2166 self.start_margin
2167 .adjoin_assign(&fragment_block_margins.start);
2168 if fragment_block_margins.collapsed_through {
2169 self.start_margin.adjoin_assign(&fragment_block_margins.end);
2170 return;
2171 }
2172 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2173 } else {
2174 self.current_margin
2175 .adjoin_assign(&fragment_block_margins.start);
2176 }
2177
2178 fragment.base.rect.origin += LogicalVec2 {
2179 inline: Au::zero(),
2180 block: self.current_margin.solve() + self.current_block_direction_position,
2181 }
2182 .to_physical_size(self.containing_block.style.writing_mode);
2183
2184 if fragment_block_margins.collapsed_through {
2185 self.current_block_direction_position += fragment_block_size;
2188 self.current_margin
2189 .adjoin_assign(&fragment_block_margins.end);
2190 } else {
2191 self.current_block_direction_position +=
2192 self.current_margin.solve() + fragment_block_size;
2193 self.current_margin = fragment_block_margins.end;
2194 }
2195 },
2196 Fragment::AbsoluteOrFixedPositioned(fragment) => {
2197 fragment.borrow_mut().original_static_position_rect = LogicalRect {
2200 start_corner: LogicalVec2 {
2201 block: (self.current_margin.solve() +
2202 self.current_block_direction_position),
2203 inline: Au::zero(),
2204 },
2205 size: LogicalVec2::zero(),
2206 }
2207 .as_physical(Some(self.containing_block));
2208 },
2209 Fragment::Float(box_fragment) => {
2210 let sequential_layout_state = sequential_layout_state
2211 .expect("Found float fragment without SequentialLayoutState");
2212 let block_offset_from_containing_block_top =
2213 self.current_block_direction_position + self.current_margin.solve();
2214 let box_fragment = &mut *box_fragment.borrow_mut();
2215 sequential_layout_state.place_float_fragment(
2216 box_fragment,
2217 self.containing_block,
2218 self.start_margin,
2219 block_offset_from_containing_block_top,
2220 );
2221 },
2222 Fragment::Positioning(_) => {},
2223 _ => unreachable!(),
2224 }
2225 }
2226
2227 fn finish(mut self) -> (Au, CollapsedBlockMargins, Baselines) {
2228 if !self.last_in_flow_margin_collapses_with_parent_end_margin {
2229 self.current_block_direction_position += self.current_margin.solve();
2230 self.current_margin = CollapsedMargin::zero();
2231 }
2232 let (total_block_size, collapsed_through) = match self.marker_block_size {
2233 Some(marker_block_size) => (
2234 self.current_block_direction_position.max(marker_block_size),
2235 false,
2238 ),
2239 None => (
2240 self.current_block_direction_position,
2241 self.next_in_flow_margin_collapses_with_parent_start_margin,
2242 ),
2243 };
2244
2245 (
2246 total_block_size,
2247 CollapsedBlockMargins {
2248 collapsed_through,
2249 start: self.start_margin,
2250 end: self.current_margin,
2251 },
2252 self.inflow_baselines,
2253 )
2254 }
2255}
2256
2257pub(crate) struct IndependentFloatOrAtomicLayoutResult {
2258 pub fragment: BoxFragment,
2259 pub baselines: Baselines,
2260 pub pbm_sums: LogicalSides<Au>,
2261}
2262
2263impl IndependentFormattingContext {
2264 pub(crate) fn layout_float_or_atomic_inline(
2265 &self,
2266 layout_context: &LayoutContext,
2267 child_positioning_context: &mut PositioningContext,
2268 containing_block: &ContainingBlock,
2269 ) -> IndependentFloatOrAtomicLayoutResult {
2270 let style = self.style();
2271 let container_writing_mode = containing_block.style.writing_mode;
2272 let layout_style = self.layout_style();
2273 let content_box_sizes_and_pbm =
2274 layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
2275 let pbm = &content_box_sizes_and_pbm.pbm;
2276 let margin = pbm.margin.auto_is(Au::zero);
2277 let pbm_sums = pbm.padding + pbm.border + margin;
2278 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
2279 let is_table = self.is_table();
2280
2281 let available_inline_size =
2282 Au::zero().max(containing_block.size.inline - pbm_sums.inline_sum());
2283 let available_block_size = containing_block
2284 .size
2285 .block
2286 .to_definite()
2287 .map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum()));
2288
2289 let tentative_block_content_size =
2290 self.tentative_block_content_size(preferred_aspect_ratio);
2291 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
2292 SizeConstraint::Definite(content_box_sizes_and_pbm.content_box_sizes.block.resolve(
2293 Direction::Block,
2294 Size::FitContent,
2295 Au::zero,
2296 available_block_size,
2297 || block_content_size,
2298 is_table,
2299 ))
2300 } else {
2301 content_box_sizes_and_pbm
2302 .content_box_sizes
2303 .block
2304 .resolve_extrinsic(Size::FitContent, Au::zero(), available_block_size)
2305 };
2306
2307 let get_content_size = || {
2308 let constraint_space =
2309 ConstraintSpace::new(tentative_block_size, style, preferred_aspect_ratio);
2310 self.inline_content_sizes(layout_context, &constraint_space)
2311 .sizes
2312 };
2313
2314 let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
2315 Direction::Inline,
2316 Size::FitContent,
2317 Au::zero,
2318 Some(available_inline_size),
2319 get_content_size,
2320 is_table,
2321 );
2322
2323 let containing_block_for_children = ContainingBlock {
2324 size: ContainingBlockSize {
2325 inline: inline_size,
2326 block: tentative_block_size,
2327 },
2328 style,
2329 };
2330 assert_eq!(
2331 container_writing_mode.is_horizontal(),
2332 style.writing_mode.is_horizontal(),
2333 "Mixed horizontal and vertical writing modes are not supported yet"
2334 );
2335
2336 let lazy_block_size = LazySize::new(
2337 &content_box_sizes_and_pbm.content_box_sizes.block,
2338 Direction::Block,
2339 Size::FitContent,
2340 Au::zero,
2341 available_block_size,
2342 is_table,
2343 );
2344
2345 let CacheableLayoutResult {
2346 content_inline_size_for_table,
2347 content_block_size,
2348 fragments,
2349 baselines,
2350 specific_layout_info,
2351 ..
2352 } = self.layout(
2353 layout_context,
2354 child_positioning_context,
2355 &containing_block_for_children,
2356 containing_block,
2357 preferred_aspect_ratio,
2358 &lazy_block_size,
2359 );
2360
2361 let content_size = LogicalVec2 {
2362 inline: content_inline_size_for_table.unwrap_or(inline_size),
2363 block: lazy_block_size.resolve(|| content_block_size),
2364 }
2365 .to_physical_size(container_writing_mode);
2366 let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size);
2367
2368 let mut base_fragment_info = self.base_fragment_info();
2369 if content_box_sizes_and_pbm.depends_on_block_constraints {
2370 base_fragment_info.flags.insert(
2371 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2372 );
2373 }
2374
2375 let fragment = BoxFragment::new(
2379 base_fragment_info,
2380 style.clone(),
2381 fragments,
2382 content_rect,
2383 pbm.padding.to_physical(container_writing_mode),
2384 pbm.border.to_physical(container_writing_mode),
2385 margin.to_physical(container_writing_mode),
2386 specific_layout_info,
2387 );
2388
2389 IndependentFloatOrAtomicLayoutResult {
2390 fragment,
2391 baselines,
2392 pbm_sums,
2393 }
2394 }
2395}