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