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, PositioningFragment,
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 lazy_block_size: &LazySize,
428 base: Option<&LayoutBoxBase>,
429 ) -> IndependentFormattingContextLayoutResult {
430 let mut sequential_layout_state =
431 if self.contains_floats || !layout_context.allow_parallel_layout {
432 Some(SequentialLayoutState::new(containing_block.size.inline))
433 } else {
434 None
435 };
436
437 let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
441
442 let previous_positioning_context_len = positioning_context.len();
445
446 let mut flow_layout = self.contents.layout(
447 layout_context,
448 positioning_context,
449 containing_block,
450 sequential_layout_state.as_mut(),
451 CollapsibleWithParentStartMargin(false),
452 ignore_block_margins_for_stretch,
453 );
454 debug_assert!(
455 !flow_layout
456 .collapsible_margins_in_children
457 .collapsed_through
458 );
459
460 let clearance = sequential_layout_state.and_then(|sequential_layout_state| {
464 sequential_layout_state.calculate_clearance(Clear::Both, &CollapsedMargin::zero())
465 });
466
467 let content_block_size = flow_layout.content_block_size +
468 flow_layout.collapsible_margins_in_children.end.solve() +
469 clearance.unwrap_or_default();
470
471 if let Some(base) = base &&
475 base.base_fragment_info
476 .flags
477 .contains(FragmentFlags::IS_BUTTON)
478 {
479 flow_layout.depends_on_block_constraints = true;
480 let final_block_size = lazy_block_size.resolve(|| content_block_size);
481 let align_fragment_rect = LogicalRect {
482 start_corner: LogicalVec2 {
483 inline: Au::zero(),
484 block: Au::zero().max((final_block_size - content_block_size) / 2),
485 },
486 size: LogicalVec2 {
487 inline: containing_block.size.inline,
488 block: content_block_size,
489 },
490 }
491 .as_physical(Some(containing_block));
492 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
493 &align_fragment_rect.origin.to_vector(),
494 previous_positioning_context_len,
495 );
496 let align_fragment = PositioningFragment::new_anonymous(
497 base.style.clone(),
498 align_fragment_rect,
499 flow_layout.fragments,
500 false, );
502 flow_layout.fragments = vec![Fragment::Positioning(align_fragment)];
503 }
504
505 IndependentFormattingContextLayoutResult {
506 fragments: flow_layout.fragments,
507 content_block_size,
508 content_inline_size_for_table: None,
509 baselines: flow_layout.baselines,
510 depends_on_block_constraints: flow_layout.depends_on_block_constraints,
511 specific_layout_info: None,
512 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
513 }
514 }
515
516 #[inline]
517 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
518 LayoutStyle::Default(&base.style)
519 }
520
521 pub(crate) fn repair_style(
522 &mut self,
523 context: &SharedStyleContext,
524 node: &ServoLayoutNode,
525 new_style: &ServoArc<ComputedValues>,
526 ) {
527 self.contents.repair_style(context, node, new_style);
528 }
529
530 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
531 self.contents.attached_to_tree(layout_box);
532 }
533}
534
535fn compute_inline_content_sizes_for_block_level_boxes(
541 boxes: &[ArcRefCell<BlockLevelBox>],
542 layout_context: &LayoutContext,
543 containing_block: &IndefiniteContainingBlock,
544) -> InlineContentSizesResult {
545 let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
546 match &*box_.borrow() {
547 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
548 BlockLevelBox::OutsideMarker { .. } => None,
549 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
550 let inline_content_sizes_result = float_box.contents.outer_inline_content_sizes(
551 layout_context,
552 containing_block,
553 &LogicalVec2::zero(),
554 false, );
556 let style = &float_box.contents.style();
557 let container_writing_mode = containing_block.style.writing_mode;
558 Some((
559 inline_content_sizes_result,
560 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode),
561 Clear::from_style_and_container_writing_mode(style, container_writing_mode),
562 ))
563 },
564 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
565 let base = &same_formatting_context_block.base;
566 let contents = &same_formatting_context_block.contents;
567 let is_anonymous_block =
568 matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
569 let inline_content_sizes_result = sizing::outer_inline(
570 base,
571 &contents.layout_style(base),
572 containing_block,
573 &LogicalVec2::zero(),
574 false, false, !is_anonymous_block, |_| None, |constraint_space| {
579 base.inline_content_sizes(layout_context, constraint_space, contents)
580 },
581 |_aspect_ratio| None,
582 );
583 let clear = if is_anonymous_block {
592 Clear::None
593 } else {
594 Clear::Both
595 };
596 Some((inline_content_sizes_result, None, clear))
597 },
598 BlockLevelBox::Independent(independent) => {
599 let inline_content_sizes_result = independent.outer_inline_content_sizes(
600 layout_context,
601 containing_block,
602 &LogicalVec2::zero(),
603 false, );
605 Some((
606 inline_content_sizes_result,
607 None,
608 Clear::from_style_and_container_writing_mode(
609 independent.style(),
610 containing_block.style.writing_mode,
611 ),
612 ))
613 },
614 }
615 };
616
617 #[derive(Default)]
620 struct AccumulatedData {
621 depends_on_block_constraints: bool,
623 max_size: ContentSizes,
625 floats: LogicalSides1D<ContentSizes>,
628 }
629
630 impl AccumulatedData {
631 fn max_size_including_uncleared_floats(&self) -> ContentSizes {
632 self.max_size.max(self.floats.start.union(&self.floats.end))
633 }
634 fn clear_floats(&mut self, clear: Clear) {
635 match clear {
636 Clear::InlineStart => {
637 self.max_size = self.max_size_including_uncleared_floats();
638 self.floats.start = ContentSizes::default();
639 },
640 Clear::InlineEnd => {
641 self.max_size = self.max_size_including_uncleared_floats();
642 self.floats.end = ContentSizes::default();
643 },
644 Clear::Both => {
645 self.max_size = self.max_size_including_uncleared_floats();
646 self.floats = LogicalSides1D::default();
647 },
648 Clear::None => {},
649 };
650 }
651 }
652
653 let accumulate =
654 |mut data: AccumulatedData,
655 (inline_content_sizes_result, float, clear): (InlineContentSizesResult, _, _)| {
656 let size = inline_content_sizes_result.sizes.max(ContentSizes::zero());
657 let depends_on_block_constraints =
658 inline_content_sizes_result.depends_on_block_constraints;
659 data.depends_on_block_constraints |= depends_on_block_constraints;
660 data.clear_floats(clear);
661 match float {
662 Some(FloatSide::InlineStart) => data.floats.start.union_assign(&size),
663 Some(FloatSide::InlineEnd) => data.floats.end.union_assign(&size),
664 None => {
665 data.max_size
666 .max_assign(data.floats.start.union(&data.floats.end).union(&size));
667 data.floats = LogicalSides1D::default();
668 },
669 }
670 data
671 };
672
673 let job_counts = boxes
674 .iter()
675 .map(|block_level_box| block_level_box.borrow().subtree_size());
676 let data = if layout_context.should_parallelize_layout(job_counts) {
677 boxes
678 .par_iter()
679 .filter_map(get_box_info)
680 .collect::<Vec<_>>()
681 .into_iter()
682 .fold(AccumulatedData::default(), accumulate)
683 } else {
684 boxes
685 .iter()
686 .filter_map(get_box_info)
687 .fold(AccumulatedData::default(), accumulate)
688 };
689 InlineContentSizesResult {
690 depends_on_block_constraints: data.depends_on_block_constraints,
691 sizes: data.max_size_including_uncleared_floats(),
692 }
693}
694
695impl BlockContainer {
696 fn layout(
697 &self,
698 layout_context: &LayoutContext,
699 positioning_context: &mut PositioningContext,
700 containing_block: &ContainingBlock,
701 sequential_layout_state: Option<&mut SequentialLayoutState>,
702 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
703 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
704 ) -> IndependentFormattingContextLayoutResult {
705 match self {
706 BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children(
707 layout_context,
708 positioning_context,
709 child_boxes,
710 containing_block,
711 sequential_layout_state,
712 collapsible_with_parent_start_margin,
713 ignore_block_margins_for_stretch,
714 ),
715 BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
716 layout_context,
717 positioning_context,
718 containing_block,
719 sequential_layout_state,
720 collapsible_with_parent_start_margin,
721 ignore_block_margins_for_stretch,
722 ),
723 }
724 }
725
726 #[inline]
727 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
728 LayoutStyle::Default(&base.style)
729 }
730
731 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
732 match self {
733 Self::BlockLevelBoxes(child_boxes) => {
734 for child_box in child_boxes {
735 child_box.borrow_mut().with_base_mut(|base| {
736 base.parent_box.replace(layout_box.clone());
737 });
738 }
739 },
740 Self::InlineFormattingContext(ifc) => ifc.attached_to_tree(layout_box),
741 }
742 }
743
744 fn find_block_margin_collapsing_with_parent(
745 &self,
746 layout_context: &LayoutContext,
747 collected_margin: &mut CollapsedMargin,
748 containing_block_for_children: &ContainingBlock,
749 ) -> bool {
750 match self {
751 BlockContainer::BlockLevelBoxes(boxes) => boxes.iter().all(|block_level_box| {
752 block_level_box
753 .borrow()
754 .find_block_margin_collapsing_with_parent(
755 layout_context,
756 collected_margin,
757 containing_block_for_children,
758 )
759 }),
760 BlockContainer::InlineFormattingContext(context) => context
761 .find_block_margin_collapsing_with_parent(
762 layout_context,
763 collected_margin,
764 containing_block_for_children,
765 ),
766 }
767 }
768}
769
770impl ComputeInlineContentSizes for BlockContainer {
771 fn compute_inline_content_sizes(
772 &self,
773 layout_context: &LayoutContext,
774 constraint_space: &ConstraintSpace,
775 ) -> InlineContentSizesResult {
776 match &self {
777 Self::BlockLevelBoxes(boxes) => compute_inline_content_sizes_for_block_level_boxes(
778 boxes,
779 layout_context,
780 &constraint_space.into(),
781 ),
782 Self::InlineFormattingContext(context) => {
783 context.compute_inline_content_sizes(layout_context, constraint_space)
784 },
785 }
786 }
787}
788
789fn layout_block_level_children(
790 layout_context: &LayoutContext,
791 positioning_context: &mut PositioningContext,
792 child_boxes: &[ArcRefCell<BlockLevelBox>],
793 containing_block: &ContainingBlock,
794 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
795 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
796 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
797) -> IndependentFormattingContextLayoutResult {
798 let mut placement_state =
799 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
800
801 let fragments = match sequential_layout_state {
802 Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
803 layout_context,
804 positioning_context,
805 child_boxes,
806 sequential_layout_state,
807 &mut placement_state,
808 ignore_block_margins_for_stretch,
809 ),
810 None => layout_block_level_children_in_parallel(
811 layout_context,
812 positioning_context,
813 child_boxes,
814 &mut placement_state,
815 ignore_block_margins_for_stretch,
816 ),
817 };
818
819 let depends_on_block_constraints = fragments.iter().any(|fragment| {
820 fragment.base().is_some_and(|base| {
821 base.flags.contains(
822 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
823 )
824 })
825 });
826
827 let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
828 IndependentFormattingContextLayoutResult {
829 fragments,
830 content_block_size,
831 collapsible_margins_in_children,
832 baselines,
833 depends_on_block_constraints,
834 content_inline_size_for_table: None,
835 specific_layout_info: None,
836 }
837}
838
839fn layout_block_level_children_in_parallel(
840 layout_context: &LayoutContext,
841 positioning_context: &mut PositioningContext,
842 child_boxes: &[ArcRefCell<BlockLevelBox>],
843 placement_state: &mut PlacementState,
844 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
845) -> Vec<Fragment> {
846 let mut layout_results: Vec<(Fragment, PositioningContext)> =
847 Vec::with_capacity(child_boxes.len());
848
849 child_boxes
850 .par_iter()
851 .map(|child_box| {
852 let mut child_positioning_context = PositioningContext::default();
853 let fragment = child_box.borrow().layout(
854 layout_context,
855 &mut child_positioning_context,
856 placement_state.containing_block,
857 None,
858 None,
859 ignore_block_margins_for_stretch,
860 false, );
862 (fragment, child_positioning_context)
863 })
864 .collect_into_vec(&mut layout_results);
865
866 layout_results
867 .into_iter()
868 .map(|(mut fragment, mut child_positioning_context)| {
869 placement_state.place_fragment_and_update_baseline(&mut fragment, None);
870 child_positioning_context.adjust_static_position_of_hoisted_fragments(
871 &fragment,
872 PositioningContextLength::zero(),
873 );
874 positioning_context.append(child_positioning_context);
875 fragment
876 })
877 .collect()
878}
879
880fn layout_block_level_children_sequentially(
881 layout_context: &LayoutContext,
882 positioning_context: &mut PositioningContext,
883 child_boxes: &[ArcRefCell<BlockLevelBox>],
884 sequential_layout_state: &mut SequentialLayoutState,
885 placement_state: &mut PlacementState,
886 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
887) -> Vec<Fragment> {
888 child_boxes
892 .iter()
893 .map(|child_box| {
894 layout_block_level_child(
895 layout_context,
896 positioning_context,
897 &child_box.borrow(),
898 Some(sequential_layout_state),
899 placement_state,
900 ignore_block_margins_for_stretch,
901 false, )
903 })
904 .collect()
905}
906
907fn layout_block_level_child(
908 layout_context: &LayoutContext,
909 positioning_context: &mut PositioningContext,
910 child_box: &BlockLevelBox,
911 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
912 placement_state: &mut PlacementState,
913 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
914 has_inline_parent: bool,
915) -> Fragment {
916 let positioning_context_length_before_layout = positioning_context.len();
917 let mut fragment = child_box.layout(
918 layout_context,
919 positioning_context,
920 placement_state.containing_block,
921 sequential_layout_state.as_deref_mut(),
922 Some(CollapsibleWithParentStartMargin(
923 placement_state.next_in_flow_margin_collapses_with_parent_start_margin,
924 )),
925 ignore_block_margins_for_stretch,
926 has_inline_parent,
927 );
928
929 placement_state.place_fragment_and_update_baseline(&mut fragment, sequential_layout_state);
930 positioning_context.adjust_static_position_of_hoisted_fragments(
931 &fragment,
932 positioning_context_length_before_layout,
933 );
934
935 fragment
936}
937
938impl BlockLevelBox {
939 #[allow(clippy::too_many_arguments)]
940 fn layout(
941 &self,
942 layout_context: &LayoutContext,
943 positioning_context: &mut PositioningContext,
944 containing_block: &ContainingBlock,
945 sequential_layout_state: Option<&mut SequentialLayoutState>,
946 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
947 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
948 has_inline_parent: bool,
949 ) -> Fragment {
950 let fragment = match self {
951 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
952 Fragment::Box(
953 same_formatting_context_block.layout_in_flow_non_replaced_block_level_cached(
954 layout_context,
955 positioning_context,
956 containing_block,
957 sequential_layout_state,
958 collapsible_with_parent_start_margin,
959 ignore_block_margins_for_stretch,
960 has_inline_parent,
961 ),
962 )
963 },
964 BlockLevelBox::Independent(independent) => Fragment::Box(
965 positioning_context
966 .layout_maybe_position_relative_fragment(
967 layout_context,
968 containing_block,
969 &independent.base,
970 |positioning_context| {
971 independent.layout_in_flow_block_level(
972 layout_context,
973 positioning_context,
974 containing_block,
975 sequential_layout_state,
976 ignore_block_margins_for_stretch,
977 has_inline_parent,
978 )
979 },
980 )
981 .into(),
982 ),
983 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
984 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
988 box_.clone(),
989 PhysicalRect::zero(),
993 LogicalVec2 {
994 inline: AlignFlags::START,
995 block: AlignFlags::START,
996 },
997 containing_block.style.writing_mode,
998 );
999 let hoisted_fragment = hoisted_box.fragment.clone();
1000 positioning_context.push(hoisted_box);
1001 Fragment::AbsoluteOrFixedPositionedPlaceholder(hoisted_fragment)
1002 },
1003 BlockLevelBox::OutOfFlowFloatBox(float_box) => Fragment::Float(
1004 float_box
1005 .layout(layout_context, positioning_context, containing_block)
1006 .into(),
1007 ),
1008 BlockLevelBox::OutsideMarker(outside_marker) => {
1009 outside_marker.layout(layout_context, containing_block, positioning_context)
1010 },
1011 };
1012
1013 self.with_base(|base| base.set_fragment(fragment.clone()));
1014
1015 fragment
1016 }
1017
1018 fn inline_content_sizes(
1019 &self,
1020 layout_context: &LayoutContext,
1021 constraint_space: &ConstraintSpace,
1022 ) -> InlineContentSizesResult {
1023 let independent_formatting_context = match self {
1024 BlockLevelBox::Independent(independent) => independent,
1025 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => &box_.borrow().context,
1026 BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents,
1027 BlockLevelBox::OutsideMarker(outside_marker) => &outside_marker.context,
1028 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
1029 return same_formatting_context_block
1030 .inline_content_sizes(layout_context, constraint_space);
1031 },
1032 };
1033 independent_formatting_context.inline_content_sizes(layout_context, constraint_space)
1034 }
1035}
1036
1037impl IndependentFormattingContext {
1038 pub(crate) fn layout_in_flow_block_level(
1046 &self,
1047 layout_context: &LayoutContext,
1048 positioning_context: &mut PositioningContext,
1049 containing_block: &ContainingBlock,
1050 sequential_layout_state: Option<&mut SequentialLayoutState>,
1051 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1052 has_inline_parent: bool,
1053 ) -> BoxFragment {
1054 if let Some(sequential_layout_state) = sequential_layout_state {
1055 return self.layout_in_flow_block_level_sequentially(
1056 layout_context,
1057 positioning_context,
1058 containing_block,
1059 sequential_layout_state,
1060 ignore_block_margins_for_stretch,
1061 has_inline_parent,
1062 );
1063 }
1064
1065 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1066 self.inline_content_sizes(layout_context, constraint_space)
1067 .sizes
1068 };
1069 let layout_style = self.layout_style();
1070 let ContainingBlockPaddingAndBorder {
1071 containing_block: containing_block_for_children,
1072 pbm,
1073 block_sizes,
1074 depends_on_block_constraints,
1075 available_block_size,
1076 justify_self,
1077 preferred_aspect_ratio,
1078 } = solve_containing_block_padding_and_border_for_in_flow_box(
1079 containing_block,
1080 &layout_style,
1081 get_inline_content_sizes,
1082 ignore_block_margins_for_stretch,
1083 Some(self),
1084 has_inline_parent,
1085 );
1086
1087 let lazy_block_size = LazySize::new(
1088 &block_sizes,
1089 Direction::Block,
1090 Size::FitContent,
1091 Au::zero,
1092 available_block_size,
1093 layout_style.is_table(),
1094 );
1095
1096 let layout = self.layout(
1097 layout_context,
1098 positioning_context,
1099 &containing_block_for_children,
1100 containing_block,
1101 preferred_aspect_ratio,
1102 &lazy_block_size,
1103 );
1104
1105 let inline_size = layout
1106 .content_inline_size_for_table
1107 .unwrap_or(containing_block_for_children.size.inline);
1108 let block_size = lazy_block_size.resolve(|| layout.content_block_size);
1109
1110 let ResolvedMargins {
1111 margin,
1112 effective_margin_inline_start,
1113 } = solve_margins(containing_block, &pbm, inline_size, justify_self);
1114
1115 let content_rect = LogicalRect {
1116 start_corner: LogicalVec2 {
1117 block: pbm.padding.block_start + pbm.border.block_start,
1118 inline: pbm.padding.inline_start +
1119 pbm.border.inline_start +
1120 effective_margin_inline_start,
1121 },
1122 size: LogicalVec2 {
1123 block: block_size,
1124 inline: inline_size,
1125 },
1126 };
1127
1128 let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1129 let containing_block_writing_mode = containing_block.style.writing_mode;
1130
1131 let mut base_fragment_info = self.base.base_fragment_info;
1132 if depends_on_block_constraints {
1133 base_fragment_info.flags.insert(
1134 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1135 );
1136 }
1137 BoxFragment::new(
1138 base_fragment_info,
1139 self.base.style.clone(),
1140 layout.fragments,
1141 content_rect.as_physical(Some(containing_block)),
1142 pbm.padding.to_physical(containing_block_writing_mode),
1143 pbm.border.to_physical(containing_block_writing_mode),
1144 margin.to_physical(containing_block_writing_mode),
1145 layout.specific_layout_info,
1146 )
1147 .with_baselines(layout.baselines)
1148 .with_block_level_layout_info(block_margins_collapsed_with_children, None)
1149 }
1150
1151 fn layout_in_flow_block_level_sequentially(
1155 &self,
1156 layout_context: &LayoutContext<'_>,
1157 positioning_context: &mut PositioningContext,
1158 containing_block: &ContainingBlock<'_>,
1159 sequential_layout_state: &mut SequentialLayoutState,
1160 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1161 has_inline_parent: bool,
1162 ) -> BoxFragment {
1163 let style = &self.base.style;
1164 let containing_block_writing_mode = containing_block.style.writing_mode;
1165 let ContentBoxSizesAndPBM {
1166 content_box_sizes,
1167 pbm,
1168 depends_on_block_constraints,
1169 ..
1170 } = self
1171 .layout_style()
1172 .content_box_sizes_and_padding_border_margin(&containing_block.into());
1173
1174 let (margin_block_start, margin_block_end) =
1175 solve_block_margins_for_in_flow_block_level(&pbm);
1176 let collapsed_margin_block_start = CollapsedMargin::new(margin_block_start);
1177
1178 let mut content_size;
1189 let mut layout;
1190 let mut placement_rect;
1191
1192 let clear_position = sequential_layout_state.calculate_clear_position(
1196 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode),
1197 &collapsed_margin_block_start,
1198 );
1199 let ceiling = clear_position.unwrap_or_else(|| {
1200 sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
1201 });
1202
1203 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1205 let available_block_size = containing_block
1206 .size
1207 .block
1208 .to_definite()
1209 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1210 let is_table = self.is_table();
1211 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
1212
1213 #[derive(Default)]
1214 struct Cache {
1215 min_block_size: Au,
1216 max_block_size: Option<Au>,
1217 tentative_block_size: SizeConstraint,
1218 depends_on_stretch_size: bool,
1219 }
1220 let mut cache = Cache {
1221 depends_on_stretch_size: true,
1222 ..Default::default()
1223 };
1224
1225 let update_cache = |cache: &mut Cache, stretch_size| {
1226 let tentative_block_content_size = self
1227 .tentative_block_content_size_with_dependency(preferred_aspect_ratio, stretch_size);
1228 let (preferred_block_size, min_block_size, max_block_size, depends_on_stretch_size) =
1229 if let Some(result) = tentative_block_content_size {
1230 let (block_content_size, depends_on_stretch_size) = result;
1231 let (preferred, min, max) = content_box_sizes.block.resolve_each(
1232 Size::FitContent,
1233 Au::zero,
1234 available_block_size,
1235 || block_content_size,
1236 is_table,
1237 );
1238 (Some(preferred), min, max, depends_on_stretch_size)
1239 } else {
1240 let (preferred, min, max) = content_box_sizes.block.resolve_each_extrinsic(
1241 Size::FitContent,
1242 Au::zero(),
1243 available_block_size,
1244 );
1245 (preferred, min, max, false)
1246 };
1247 cache.min_block_size = min_block_size;
1248 cache.max_block_size = max_block_size;
1249 cache.tentative_block_size =
1250 SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
1251 cache.depends_on_stretch_size = depends_on_stretch_size;
1252 };
1253
1254 let get_inline_content_sizes = |cache: &Cache| {
1256 let constraint_space =
1257 ConstraintSpace::new(cache.tentative_block_size, style, preferred_aspect_ratio);
1258 self.inline_content_sizes(layout_context, &constraint_space)
1259 .sizes
1260 };
1261
1262 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1263 let automatic_inline_size = automatic_inline_size(justify_self, Some(self));
1264 let compute_inline_size = |cache: &mut Cache, stretch_size| {
1265 if cache.depends_on_stretch_size {
1266 update_cache(cache, stretch_size);
1267 }
1268 content_box_sizes.inline.resolve(
1269 Direction::Inline,
1270 automatic_inline_size,
1271 Au::zero,
1272 Some(stretch_size),
1273 || get_inline_content_sizes(cache),
1274 is_table,
1275 )
1276 };
1277
1278 let get_lazy_block_size = || {
1279 LazySize::new(
1280 &content_box_sizes.block,
1281 Direction::Block,
1282 Size::FitContent,
1283 Au::zero,
1284 available_block_size,
1285 is_table,
1286 )
1287 };
1288
1289 let inline_size_with_max_available_space = compute_inline_size(&mut cache, MAX_AU);
1298 let inline_size_with_no_available_space = compute_inline_size(&mut cache, Au::zero());
1299 if inline_size_with_no_available_space == inline_size_with_max_available_space {
1300 let inline_size = inline_size_with_no_available_space;
1304 let lazy_block_size = get_lazy_block_size();
1305 layout = self.layout(
1306 layout_context,
1307 positioning_context,
1308 &ContainingBlock {
1309 size: ContainingBlockSize {
1310 inline: inline_size,
1311 block: cache.tentative_block_size,
1315 },
1316 style,
1317 },
1318 containing_block,
1319 preferred_aspect_ratio,
1320 &lazy_block_size,
1321 );
1322
1323 content_size = LogicalVec2 {
1324 block: lazy_block_size.resolve(|| layout.content_block_size),
1325 inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
1326 };
1327
1328 let mut placement = PlacementAmongFloats::new(
1329 &sequential_layout_state.floats,
1330 ceiling,
1331 content_size + pbm.padding_border_sums,
1332 &pbm,
1333 );
1334 placement_rect = placement.place();
1335 } else {
1336 let minimum_size_of_block = LogicalVec2 {
1342 inline: inline_size_with_no_available_space,
1346 block: match cache.tentative_block_size {
1354 SizeConstraint::Definite(size) if cache.max_block_size.is_some() => size,
1357 _ => cache.min_block_size,
1360 },
1361 } + pbm.padding_border_sums;
1362 let mut placement = PlacementAmongFloats::new(
1363 &sequential_layout_state.floats,
1364 ceiling,
1365 minimum_size_of_block,
1366 &pbm,
1367 );
1368
1369 loop {
1370 placement_rect = placement.place();
1372 let available_inline_size =
1373 placement_rect.size.inline - pbm.padding_border_sums.inline;
1374 let proposed_inline_size = compute_inline_size(&mut cache, available_inline_size);
1375
1376 let positioning_context_length = positioning_context.len();
1380 let lazy_block_size = get_lazy_block_size();
1381 layout = self.layout(
1382 layout_context,
1383 positioning_context,
1384 &ContainingBlock {
1385 size: ContainingBlockSize {
1386 inline: proposed_inline_size,
1387 block: cache.tentative_block_size,
1388 },
1389 style,
1390 },
1391 containing_block,
1392 preferred_aspect_ratio,
1393 &lazy_block_size,
1394 );
1395
1396 let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
1397 debug_assert!(inline_size < proposed_inline_size);
1402 inline_size
1403 } else {
1404 proposed_inline_size
1405 };
1406 content_size = LogicalVec2 {
1407 block: lazy_block_size.resolve(|| layout.content_block_size),
1408 inline: inline_size,
1409 };
1410
1411 if placement.try_to_expand_for_auto_block_size(
1415 content_size.block + pbm.padding_border_sums.block,
1416 &placement_rect.size,
1417 ) {
1418 break;
1419 }
1420
1421 positioning_context.truncate(&positioning_context_length);
1425 }
1426 }
1427
1428 let has_clearance = clear_position.is_some() || placement_rect.start_corner.block > ceiling;
1432 let clearance = has_clearance.then(|| {
1433 placement_rect.start_corner.block -
1434 sequential_layout_state
1435 .position_with_zero_clearance(&collapsed_margin_block_start)
1436 });
1437
1438 let ((margin_inline_start, margin_inline_end), effective_margin_inline_start) =
1439 solve_inline_margins_avoiding_floats(
1440 sequential_layout_state,
1441 containing_block,
1442 &pbm,
1443 content_size.inline + pbm.padding_border_sums.inline,
1444 placement_rect,
1445 justify_self,
1446 );
1447
1448 let margin = LogicalSides {
1449 inline_start: margin_inline_start,
1450 inline_end: margin_inline_end,
1451 block_start: margin_block_start,
1452 block_end: margin_block_end,
1453 };
1454
1455 if clearance.is_some() {
1458 sequential_layout_state.commit_margin();
1459 }
1460 sequential_layout_state.adjoin_assign(&collapsed_margin_block_start);
1461
1462 sequential_layout_state.commit_margin();
1464 sequential_layout_state.advance_block_position(
1465 pbm.padding_border_sums.block + content_size.block + clearance.unwrap_or_else(Au::zero),
1466 );
1467 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1468
1469 let content_rect = LogicalRect {
1470 start_corner: LogicalVec2 {
1471 block: pbm.padding.block_start +
1472 pbm.border.block_start +
1473 clearance.unwrap_or_else(Au::zero),
1474 inline: pbm.padding.inline_start +
1475 pbm.border.inline_start +
1476 effective_margin_inline_start,
1477 },
1478 size: content_size,
1479 };
1480
1481 let mut base_fragment_info = self.base.base_fragment_info;
1482 if depends_on_block_constraints {
1483 base_fragment_info.flags.insert(
1484 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1485 );
1486 }
1487
1488 BoxFragment::new(
1489 base_fragment_info,
1490 style.clone(),
1491 layout.fragments,
1492 content_rect.as_physical(Some(containing_block)),
1493 pbm.padding.to_physical(containing_block_writing_mode),
1494 pbm.border.to_physical(containing_block_writing_mode),
1495 margin.to_physical(containing_block_writing_mode),
1496 layout.specific_layout_info,
1497 )
1498 .with_baselines(layout.baselines)
1499 .with_block_level_layout_info(CollapsedBlockMargins::from_margin(&margin), clearance)
1500 }
1501}
1502
1503struct ContainingBlockPaddingAndBorder<'a> {
1504 containing_block: ContainingBlock<'a>,
1505 pbm: PaddingBorderMargin,
1506 block_sizes: Sizes,
1507 depends_on_block_constraints: bool,
1508 available_block_size: Option<Au>,
1509 justify_self: AlignFlags,
1510 preferred_aspect_ratio: Option<AspectRatio>,
1511}
1512
1513struct ResolvedMargins {
1514 pub margin: LogicalSides<Au>,
1516
1517 pub effective_margin_inline_start: Au,
1524}
1525
1526fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
1532 containing_block: &ContainingBlock<'_>,
1533 layout_style: &'a LayoutStyle,
1534 get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
1535 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1536 context: Option<&IndependentFormattingContext>,
1537 has_inline_parent: bool,
1538) -> ContainingBlockPaddingAndBorder<'a> {
1539 let style = layout_style.style();
1540 if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
1541 let containing_block_for_children = ContainingBlock {
1545 size: ContainingBlockSize {
1546 inline: containing_block.size.inline,
1547 block: containing_block.size.block,
1548 },
1549 style,
1550 };
1551 return ContainingBlockPaddingAndBorder {
1554 containing_block: containing_block_for_children,
1555 pbm: PaddingBorderMargin::zero(),
1556 block_sizes: Sizes::default(),
1557 depends_on_block_constraints: false,
1558 available_block_size: None,
1561 justify_self: AlignFlags::NORMAL,
1564 preferred_aspect_ratio: None,
1565 };
1566 }
1567
1568 let ContentBoxSizesAndPBM {
1569 content_box_sizes,
1570 pbm,
1571 depends_on_block_constraints,
1572 ..
1573 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
1574
1575 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1576 let available_inline_size = Au::zero().max(containing_block.size.inline - pbm_sums.inline);
1577 let available_block_size = containing_block
1578 .size
1579 .block
1580 .to_definite()
1581 .map(|block_size| Au::zero().max(block_size - pbm_sums.block));
1582
1583 let preferred_aspect_ratio =
1586 context.and_then(|context| context.preferred_aspect_ratio(&pbm.padding_border_sums));
1587 let is_table = layout_style.is_table();
1588
1589 let tentative_block_content_size = context.and_then(|context| {
1592 context.tentative_block_content_size(preferred_aspect_ratio, available_inline_size)
1593 });
1594 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
1595 SizeConstraint::Definite(content_box_sizes.block.resolve(
1596 Direction::Block,
1597 Size::FitContent,
1598 Au::zero,
1599 available_block_size,
1600 || block_content_size,
1601 is_table,
1602 ))
1603 } else {
1604 content_box_sizes.block.resolve_extrinsic(
1605 Size::FitContent,
1606 Au::zero(),
1607 available_block_size,
1608 )
1609 };
1610
1611 let get_inline_content_sizes = || {
1614 get_inline_content_sizes(&ConstraintSpace::new(
1615 tentative_block_size,
1616 style,
1617 preferred_aspect_ratio,
1618 ))
1619 };
1620 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1621 let inline_size = content_box_sizes.inline.resolve(
1622 Direction::Inline,
1623 automatic_inline_size(justify_self, context),
1624 Au::zero,
1625 Some(available_inline_size),
1626 get_inline_content_sizes,
1627 is_table,
1628 );
1629
1630 let containing_block_for_children = ContainingBlock {
1631 size: ContainingBlockSize {
1632 inline: inline_size,
1633 block: tentative_block_size,
1634 },
1635 style,
1636 };
1637 assert_eq!(
1639 containing_block.style.writing_mode.is_horizontal(),
1640 containing_block_for_children
1641 .style
1642 .writing_mode
1643 .is_horizontal(),
1644 "Vertical writing modes are not supported yet"
1645 );
1646 ContainingBlockPaddingAndBorder {
1647 containing_block: containing_block_for_children,
1648 pbm,
1649 block_sizes: content_box_sizes.block,
1650 depends_on_block_constraints,
1651 available_block_size,
1652 justify_self,
1653 preferred_aspect_ratio,
1654 }
1655}
1656
1657fn solve_margins(
1662 containing_block: &ContainingBlock<'_>,
1663 pbm: &PaddingBorderMargin,
1664 inline_size: Au,
1665 justify_self: AlignFlags,
1666) -> ResolvedMargins {
1667 let (inline_margins, effective_margin_inline_start) =
1668 solve_inline_margins_for_in_flow_block_level(
1669 containing_block,
1670 pbm,
1671 inline_size,
1672 justify_self,
1673 );
1674 let block_margins = solve_block_margins_for_in_flow_block_level(pbm);
1675 ResolvedMargins {
1676 margin: LogicalSides {
1677 inline_start: inline_margins.0,
1678 inline_end: inline_margins.1,
1679 block_start: block_margins.0,
1680 block_end: block_margins.1,
1681 },
1682 effective_margin_inline_start,
1683 }
1684}
1685
1686fn solve_block_margins_for_in_flow_block_level(pbm: &PaddingBorderMargin) -> (Au, Au) {
1690 (
1691 pbm.margin.block_start.auto_is(Au::zero),
1692 pbm.margin.block_end.auto_is(Au::zero),
1693 )
1694}
1695
1696fn resolve_justify_self(
1698 style: &ComputedValues,
1699 containing_block_style: &ComputedValues,
1700 has_inline_parent: bool,
1701) -> AlignFlags {
1702 let alignment = match style.clone_justify_self().0 {
1708 AlignFlags::AUTO if has_inline_parent => AlignFlags::NORMAL,
1709 AlignFlags::AUTO => containing_block_style.clone_justify_items().computed.0.0,
1710 alignment => alignment,
1711 };
1712 let is_ltr = |style: &ComputedValues| style.writing_mode.line_left_is_inline_start();
1713 let alignment_value = match alignment.value() {
1714 AlignFlags::LEFT if is_ltr(containing_block_style) => AlignFlags::START,
1715 AlignFlags::LEFT => AlignFlags::END,
1716 AlignFlags::RIGHT if is_ltr(containing_block_style) => AlignFlags::END,
1717 AlignFlags::RIGHT => AlignFlags::START,
1718 AlignFlags::SELF_START if is_ltr(containing_block_style) == is_ltr(style) => {
1719 AlignFlags::START
1720 },
1721 AlignFlags::SELF_START => AlignFlags::END,
1722 AlignFlags::SELF_END if is_ltr(containing_block_style) == is_ltr(style) => AlignFlags::END,
1723 AlignFlags::SELF_END => AlignFlags::START,
1724 alignment_value => alignment_value,
1725 };
1726 alignment.flags() | alignment_value
1727}
1728
1729#[inline]
1732fn automatic_inline_size<T>(
1733 justify_self: AlignFlags,
1734 context: Option<&IndependentFormattingContext>,
1735) -> Size<T> {
1736 let normal_stretches = || {
1737 !context.is_some_and(|context| {
1738 context
1739 .base
1740 .base_fragment_info
1741 .flags
1742 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET) ||
1743 context.is_table()
1744 })
1745 };
1746 match justify_self {
1747 AlignFlags::STRETCH => Size::Stretch,
1748 AlignFlags::NORMAL if normal_stretches() => Size::Stretch,
1749 _ => Size::FitContent,
1750 }
1751}
1752
1753fn justify_self_alignment(
1760 containing_block: &ContainingBlock,
1761 free_space: Au,
1762 justify_self: AlignFlags,
1763) -> Au {
1764 let mut alignment = justify_self.value();
1765 let is_safe = justify_self.flags() == AlignFlags::SAFE || alignment == AlignFlags::NORMAL;
1766 if is_safe && free_space <= Au::zero() {
1767 alignment = AlignFlags::START
1768 }
1769 match alignment {
1770 AlignFlags::NORMAL => {},
1771 AlignFlags::CENTER => return free_space / 2,
1772 AlignFlags::END => return free_space,
1773 _ => return Au::zero(),
1774 }
1775
1776 let style = containing_block.style;
1778 match style.clone_text_align() {
1779 TextAlignKeyword::MozCenter => free_space / 2,
1780 TextAlignKeyword::MozLeft if !style.writing_mode.line_left_is_inline_start() => free_space,
1781 TextAlignKeyword::MozRight if style.writing_mode.line_left_is_inline_start() => free_space,
1782 _ => Au::zero(),
1783 }
1784}
1785
1786fn solve_inline_margins_for_in_flow_block_level(
1799 containing_block: &ContainingBlock,
1800 pbm: &PaddingBorderMargin,
1801 inline_size: Au,
1802 justify_self: AlignFlags,
1803) -> ((Au, Au), Au) {
1804 let free_space = containing_block.size.inline - pbm.padding_border_sums.inline - inline_size;
1805 let mut justification = Au::zero();
1806 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
1807 (AuOrAuto::Auto, AuOrAuto::Auto) => {
1808 let start = Au::zero().max(free_space / 2);
1809 (start, free_space - start)
1810 },
1811 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
1812 (Au::zero().max(free_space - end), end)
1813 },
1814 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, free_space - start),
1815 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
1816 justification =
1821 justify_self_alignment(containing_block, free_space - start - end, justify_self);
1822 (start, end)
1823 },
1824 };
1825 let effective_margin_inline_start = inline_margins.0 + justification;
1826 (inline_margins, effective_margin_inline_start)
1827}
1828
1829fn solve_inline_margins_avoiding_floats(
1839 sequential_layout_state: &SequentialLayoutState,
1840 containing_block: &ContainingBlock,
1841 pbm: &PaddingBorderMargin,
1842 inline_size: Au,
1843 placement_rect: LogicalRect<Au>,
1844 justify_self: AlignFlags,
1845) -> ((Au, Au), Au) {
1846 let free_space = Au::zero().max(placement_rect.size.inline - inline_size);
1850 let cb_info = &sequential_layout_state.floats.containing_block_info;
1851 let start_adjustment = placement_rect.start_corner.inline - cb_info.inline_start;
1852 let end_adjustment = cb_info.inline_end - placement_rect.max_inline_position();
1853 let mut justification = Au::zero();
1854 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
1855 (AuOrAuto::Auto, AuOrAuto::Auto) => {
1856 let half = free_space / 2;
1857 (start_adjustment + half, end_adjustment + free_space - half)
1858 },
1859 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (start_adjustment + free_space, end),
1860 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, end_adjustment + free_space),
1861 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
1862 justification = justify_self_alignment(containing_block, free_space, justify_self);
1868 (start, end)
1869 },
1870 };
1871 let effective_margin_inline_start = inline_margins.0.max(start_adjustment) + justification;
1872 (inline_margins, effective_margin_inline_start)
1873}
1874
1875struct PlacementState<'container> {
1880 next_in_flow_margin_collapses_with_parent_start_margin: bool,
1881 last_in_flow_margin_collapses_with_parent_end_margin: bool,
1882 start_margin: CollapsedMargin,
1883 current_margin: CollapsedMargin,
1884 current_block_direction_position: Au,
1885 inflow_baselines: Baselines,
1886 is_inline_block_context: bool,
1887
1888 marker_block_size: Option<Au>,
1893
1894 containing_block: &'container ContainingBlock<'container>,
1897}
1898
1899impl<'container> PlacementState<'container> {
1900 fn new(
1901 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1902 containing_block: &'container ContainingBlock<'container>,
1903 ) -> PlacementState<'container> {
1904 let is_inline_block_context =
1905 containing_block.style.get_box().clone_display() == Display::InlineBlock;
1906 PlacementState {
1907 next_in_flow_margin_collapses_with_parent_start_margin:
1908 collapsible_with_parent_start_margin.0,
1909 last_in_flow_margin_collapses_with_parent_end_margin: true,
1910 start_margin: CollapsedMargin::zero(),
1911 current_margin: CollapsedMargin::zero(),
1912 current_block_direction_position: Au::zero(),
1913 inflow_baselines: Baselines::default(),
1914 is_inline_block_context,
1915 marker_block_size: None,
1916 containing_block,
1917 }
1918 }
1919
1920 fn place_fragment_and_update_baseline(
1921 &mut self,
1922 fragment: &mut Fragment,
1923 sequential_layout_state: Option<&mut SequentialLayoutState>,
1924 ) {
1925 self.place_fragment(fragment, sequential_layout_state);
1926
1927 let box_fragment = match fragment {
1928 Fragment::LayoutRoot(..) | Fragment::Box(..) => fragment
1929 .retrieve_box_fragment()
1930 .expect("Should be guaranteed by surrounding check"),
1931 _ => return,
1932 };
1933
1934 if self.is_inline_block_context && box_fragment.is_table_wrapper() {
1939 return;
1940 }
1941
1942 let box_block_offset = box_fragment
1943 .content_rect()
1944 .origin
1945 .to_logical(self.containing_block)
1946 .block;
1947 let box_fragment_baselines =
1948 box_fragment.baselines(self.containing_block.style.writing_mode);
1949 if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) {
1950 self.inflow_baselines.first = Some(first + box_block_offset);
1951 }
1952 if let Some(last) = box_fragment_baselines.last {
1953 self.inflow_baselines.last = Some(last + box_block_offset);
1954 }
1955 }
1956
1957 fn place_fragment(
1960 &mut self,
1961 fragment: &mut Fragment,
1962 sequential_layout_state: Option<&mut SequentialLayoutState>,
1963 ) {
1964 match fragment {
1965 Fragment::LayoutRoot(..) | Fragment::Box(..) => {
1966 let fragment = fragment
1967 .retrieve_box_fragment()
1968 .expect("Should be guaranteed by surrounding condition");
1969
1970 let is_outside_marker = fragment
1979 .base
1980 .flags
1981 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER);
1982 if is_outside_marker {
1983 assert!(self.marker_block_size.is_none());
1984 self.marker_block_size = Some(
1985 fragment
1986 .content_rect()
1987 .size
1988 .to_logical(self.containing_block.style.writing_mode)
1989 .block,
1990 );
1991 return;
1992 }
1993
1994 let BlockLevelLayoutInfo {
1995 clearance,
1996 block_margins_collapsed_with_children: fragment_block_margins,
1997 } = *fragment
1998 .block_level_layout_info
1999 .clone()
2000 .expect("A block-level fragment should have a BlockLevelLayoutInfo.");
2001 let mut fragment_block_size = fragment
2002 .border_rect()
2003 .size
2004 .to_logical(self.containing_block.style.writing_mode)
2005 .block;
2006
2007 if let Some(clearance) = clearance {
2013 fragment_block_size += clearance;
2014 self.current_block_direction_position += self.current_margin.solve();
2019 self.current_margin = CollapsedMargin::zero();
2020 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2021 if fragment_block_margins.collapsed_through {
2022 self.last_in_flow_margin_collapses_with_parent_end_margin = false;
2023 }
2024 } else if !fragment_block_margins.collapsed_through {
2025 self.last_in_flow_margin_collapses_with_parent_end_margin = true;
2026 }
2027
2028 if self.next_in_flow_margin_collapses_with_parent_start_margin {
2029 debug_assert!(self.current_margin.solve().is_zero());
2030 self.start_margin
2031 .adjoin_assign(&fragment_block_margins.start);
2032 if fragment_block_margins.collapsed_through {
2033 self.start_margin.adjoin_assign(&fragment_block_margins.end);
2034 return;
2035 }
2036 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2037 } else {
2038 self.current_margin
2039 .adjoin_assign(&fragment_block_margins.start);
2040 }
2041
2042 fragment.base.translate_rect(
2043 LogicalVec2 {
2044 inline: Au::zero(),
2045 block: self.current_margin.solve() + self.current_block_direction_position,
2046 }
2047 .to_physical_size(self.containing_block.style.writing_mode),
2048 );
2049
2050 if fragment_block_margins.collapsed_through {
2051 self.current_block_direction_position += fragment_block_size;
2054 self.current_margin
2055 .adjoin_assign(&fragment_block_margins.end);
2056 } else {
2057 self.current_block_direction_position +=
2058 self.current_margin.solve() + fragment_block_size;
2059 self.current_margin = fragment_block_margins.end;
2060 }
2061 },
2062 Fragment::AbsoluteOrFixedPositionedPlaceholder(fragment) => {
2063 fragment.borrow_mut().original_static_position_rect = LogicalRect {
2066 start_corner: LogicalVec2 {
2067 block: (self.current_margin.solve() +
2068 self.current_block_direction_position),
2069 inline: Au::zero(),
2070 },
2071 size: LogicalVec2::zero(),
2072 }
2073 .as_physical(Some(self.containing_block));
2074 },
2075 Fragment::Float(box_fragment) => {
2076 let sequential_layout_state = sequential_layout_state
2077 .expect("Found float fragment without SequentialLayoutState");
2078 let block_offset_from_containing_block_top =
2079 self.current_block_direction_position + self.current_margin.solve();
2080 sequential_layout_state.place_float_fragment(
2081 box_fragment,
2082 self.containing_block,
2083 self.start_margin,
2084 block_offset_from_containing_block_top,
2085 );
2086 },
2087 Fragment::Positioning(_) => {},
2088 _ => unreachable!("Unexpected Fragment type encountered during flow layout"),
2089 }
2090 }
2091
2092 fn finish(mut self) -> (Au, CollapsedBlockMargins, Baselines) {
2093 if !self.last_in_flow_margin_collapses_with_parent_end_margin {
2094 self.current_block_direction_position += self.current_margin.solve();
2095 self.current_margin = CollapsedMargin::zero();
2096 }
2097 let (total_block_size, collapsed_through) = match self.marker_block_size {
2098 Some(marker_block_size) => (
2099 self.current_block_direction_position.max(marker_block_size),
2100 false,
2103 ),
2104 None => (
2105 self.current_block_direction_position,
2106 self.next_in_flow_margin_collapses_with_parent_start_margin,
2107 ),
2108 };
2109
2110 (
2111 total_block_size,
2112 CollapsedBlockMargins {
2113 collapsed_through,
2114 start: self.start_margin,
2115 end: self.current_margin,
2116 },
2117 self.inflow_baselines,
2118 )
2119 }
2120}
2121
2122pub(crate) struct IndependentFloatOrAtomicLayoutResult {
2123 pub fragment: BoxFragment,
2124 pub baselines: Baselines,
2125 pub pbm_sums: LogicalSides<Au>,
2126}
2127
2128impl IndependentFormattingContext {
2129 pub(crate) fn layout_float_or_atomic_inline(
2130 &self,
2131 layout_context: &LayoutContext,
2132 child_positioning_context: &mut PositioningContext,
2133 containing_block: &ContainingBlock,
2134 ) -> IndependentFloatOrAtomicLayoutResult {
2135 let style = self.style();
2136 let container_writing_mode = containing_block.style.writing_mode;
2137 let layout_style = self.layout_style();
2138 let content_box_sizes_and_pbm =
2139 layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
2140 let pbm = &content_box_sizes_and_pbm.pbm;
2141 let margin = pbm.margin.auto_is(Au::zero);
2142 let pbm_sums = pbm.padding + pbm.border + margin;
2143 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
2144 let is_table = self.is_table();
2145
2146 let available_inline_size =
2147 Au::zero().max(containing_block.size.inline - pbm_sums.inline_sum());
2148 let available_block_size = containing_block
2149 .size
2150 .block
2151 .to_definite()
2152 .map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum()));
2153
2154 let tentative_block_content_size =
2155 self.tentative_block_content_size(preferred_aspect_ratio, available_inline_size);
2156 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
2157 SizeConstraint::Definite(content_box_sizes_and_pbm.content_box_sizes.block.resolve(
2158 Direction::Block,
2159 Size::FitContent,
2160 Au::zero,
2161 available_block_size,
2162 || block_content_size,
2163 is_table,
2164 ))
2165 } else {
2166 content_box_sizes_and_pbm
2167 .content_box_sizes
2168 .block
2169 .resolve_extrinsic(Size::FitContent, Au::zero(), available_block_size)
2170 };
2171
2172 let get_content_size = || {
2173 let constraint_space =
2174 ConstraintSpace::new(tentative_block_size, style, preferred_aspect_ratio);
2175 self.inline_content_sizes(layout_context, &constraint_space)
2176 .sizes
2177 };
2178
2179 let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
2180 Direction::Inline,
2181 Size::FitContent,
2182 Au::zero,
2183 Some(available_inline_size),
2184 get_content_size,
2185 is_table,
2186 );
2187
2188 let containing_block_for_children = ContainingBlock {
2189 size: ContainingBlockSize {
2190 inline: inline_size,
2191 block: tentative_block_size,
2192 },
2193 style,
2194 };
2195 assert_eq!(
2196 container_writing_mode.is_horizontal(),
2197 style.writing_mode.is_horizontal(),
2198 "Mixed horizontal and vertical writing modes are not supported yet"
2199 );
2200
2201 let lazy_block_size = LazySize::new(
2202 &content_box_sizes_and_pbm.content_box_sizes.block,
2203 Direction::Block,
2204 Size::FitContent,
2205 Au::zero,
2206 available_block_size,
2207 is_table,
2208 );
2209
2210 let IndependentFormattingContextLayoutResult {
2211 content_inline_size_for_table,
2212 content_block_size,
2213 fragments,
2214 baselines,
2215 specific_layout_info,
2216 ..
2217 } = self.layout(
2218 layout_context,
2219 child_positioning_context,
2220 &containing_block_for_children,
2221 containing_block,
2222 preferred_aspect_ratio,
2223 &lazy_block_size,
2224 );
2225
2226 let content_size = LogicalVec2 {
2227 inline: content_inline_size_for_table.unwrap_or(inline_size),
2228 block: lazy_block_size.resolve(|| content_block_size),
2229 }
2230 .to_physical_size(container_writing_mode);
2231 let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size);
2232
2233 let mut base_fragment_info = self.base_fragment_info();
2234 if content_box_sizes_and_pbm.depends_on_block_constraints {
2235 base_fragment_info.flags.insert(
2236 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2237 );
2238 }
2239
2240 let fragment = BoxFragment::new(
2244 base_fragment_info,
2245 style.clone(),
2246 fragments,
2247 content_rect,
2248 pbm.padding.to_physical(container_writing_mode),
2249 pbm.border.to_physical(container_writing_mode),
2250 margin.to_physical(container_writing_mode),
2251 specific_layout_info,
2252 );
2253
2254 IndependentFloatOrAtomicLayoutResult {
2255 fragment,
2256 baselines,
2257 pbm_sums,
2258 }
2259 }
2260}