1#![expect(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::{QuirksMode, SharedStyleContext};
18use style::logical_geometry::LogicalAxis;
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 pub(crate) fn inline_formatting_context(&self) -> Option<&InlineFormattingContext> {
105 match self {
106 BlockContainer::BlockLevelBoxes(..) => None,
107 BlockContainer::InlineFormattingContext(inline_formatting_context) => {
108 Some(inline_formatting_context)
109 },
110 }
111 }
112}
113
114#[derive(Debug, MallocSizeOf)]
115pub(crate) enum BlockLevelBox {
116 Independent(IndependentFormattingContext),
117 OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
118 OutOfFlowFloatBox(FloatBox),
119 OutsideMarker(OutsideMarker),
120 SameFormattingContextBlock(SameFormattingContextBlock),
121}
122
123impl BlockLevelBox {
124 pub(crate) fn repair_style(
125 &mut self,
126 context: &SharedStyleContext,
127 node: &ServoLayoutNode,
128 new_style: &ServoArc<ComputedValues>,
129 ) {
130 match self {
131 BlockLevelBox::Independent(independent_formatting_context) => {
132 independent_formatting_context.repair_style(context, node, new_style)
133 },
134 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
135 .borrow_mut()
136 .context
137 .repair_style(context, node, new_style),
138 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
139 float_box.contents.repair_style(context, node, new_style)
140 },
141 BlockLevelBox::OutsideMarker(outside_marker) => {
142 outside_marker.repair_style(context, node, new_style)
143 },
144 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
145 same_formatting_context_block.repair_style(context, node, new_style)
146 },
147 }
148 }
149
150 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
151 match self {
152 BlockLevelBox::Independent(independent_formatting_context) => {
153 callback(&independent_formatting_context.base)
154 },
155 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
156 callback(&positioned_box.borrow().context.base)
157 },
158 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&float_box.contents.base),
159 BlockLevelBox::OutsideMarker(outside_marker) => callback(&outside_marker.context.base),
160 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
161 callback(&same_formatting_context_block.base)
162 },
163 }
164 }
165
166 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
167 match self {
168 BlockLevelBox::Independent(independent_formatting_context) => {
169 callback(&mut independent_formatting_context.base)
170 },
171 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
172 callback(&mut positioned_box.borrow_mut().context.base)
173 },
174 BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base),
175 BlockLevelBox::OutsideMarker(outside_marker) => {
176 callback(&mut outside_marker.context.base)
177 },
178 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
179 callback(&mut same_formatting_context_block.base)
180 },
181 }
182 }
183
184 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
185 match self {
186 Self::Independent(independent_formatting_context) => {
187 independent_formatting_context.attached_to_tree(layout_box)
188 },
189 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
190 positioned_box.borrow().context.attached_to_tree(layout_box)
191 },
192 Self::OutOfFlowFloatBox(float_box) => float_box.contents.attached_to_tree(layout_box),
193 Self::OutsideMarker(outside_marker) => {
194 outside_marker.context.attached_to_tree(layout_box)
195 },
196 Self::SameFormattingContextBlock(same_formatting_context_block) => {
197 same_formatting_context_block
198 .contents
199 .attached_to_tree(layout_box)
200 },
201 }
202 }
203
204 fn contains_floats(&self) -> bool {
205 match self {
206 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
207 same_formatting_context_block.contains_floats
208 },
209 BlockLevelBox::OutOfFlowFloatBox { .. } => true,
210 _ => false,
211 }
212 }
213
214 fn find_block_margin_collapsing_with_parent(
215 &self,
216 layout_context: &LayoutContext,
217 collected_margin: &mut CollapsedMargin,
218 containing_block: &ContainingBlock,
219 ) -> bool {
220 let layout_style = match self {
221 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
222 same_formatting_context_block.layout_style()
223 },
224 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
225 BlockLevelBox::OutOfFlowFloatBox(_) => return true,
226 BlockLevelBox::OutsideMarker(_) => return false,
227 BlockLevelBox::Independent(context) => {
228 context.layout_style()
231 },
232 };
233
234 let style = layout_style.style();
236 if style.get_box().clear != StyleClear::None {
237 return false;
238 }
239
240 let ContentBoxSizesAndPBM {
241 content_box_sizes,
242 pbm,
243 ..
244 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
245 let margin = pbm.margin.auto_is(Au::zero);
246 collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_start));
247
248 let BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) = self else {
249 return false;
250 };
251
252 if !pbm.padding.block_start.is_zero() || !pbm.border.block_start.is_zero() {
253 return false;
254 }
255
256 let inline_stretch_size = containing_block
257 .inline_stretch_size(pbm.padding_border_sums.inline + margin.inline_sum());
258 let block_stretch_size =
259 containing_block.block_stretch_size(pbm.padding_border_sums.block + margin.block_sum());
260
261 let mut tentative_block_size = content_box_sizes.block.resolve_extrinsic(
262 automatic_block_size(layout_context, &same_formatting_context_block.base),
263 Au::zero(),
264 block_stretch_size,
265 );
266 if skippable_for_block_percentage_quirk(
267 layout_context,
268 &same_formatting_context_block.base,
269 None,
270 ) {
271 tentative_block_size.replace_if_indefinite_with(containing_block.size.block);
272 }
273
274 let get_inline_content_sizes = || {
275 let constraint_space = ConstraintSpace::new(
276 tentative_block_size,
277 style,
278 None, );
280 self.inline_content_sizes(layout_context, &constraint_space)
281 .sizes
282 };
283 let inline_size = content_box_sizes.inline.resolve(
284 LogicalAxis::Inline,
285 Size::Stretch,
286 Au::zero,
287 inline_stretch_size,
288 get_inline_content_sizes,
289 false, );
291
292 let containing_block_for_children = ContainingBlock {
293 size: ContainingBlockSize {
294 inline: inline_size,
295 block: tentative_block_size,
296 },
297 style,
298 };
299
300 if !same_formatting_context_block
301 .contents
302 .find_block_margin_collapsing_with_parent(
303 layout_context,
304 collected_margin,
305 &containing_block_for_children,
306 )
307 {
308 return false;
309 }
310
311 if !tentative_block_size.definite_or_min().is_zero() ||
312 !pbm.padding_border_sums.block.is_zero()
313 {
314 return false;
315 }
316
317 collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_end));
318
319 true
320 }
321
322 fn subtree_size(&self) -> usize {
323 self.with_base(|base| base.subtree_size())
324 }
325}
326
327#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
328pub(crate) struct CollapsibleWithParentStartMargin(bool);
329
330#[derive(Debug, MallocSizeOf)]
333pub(crate) struct OutsideMarker {
334 pub list_item_style: ServoArc<ComputedValues>,
335 pub context: IndependentFormattingContext,
336}
337
338impl OutsideMarker {
339 fn layout(
340 &self,
341 layout_context: &LayoutContext<'_>,
342 containing_block: &ContainingBlock<'_>,
343 positioning_context: &mut PositioningContext,
344 ) -> Fragment {
345 let style = &self.context.base.style;
346 let preferred_aspect_ratio = self.context.preferred_aspect_ratio(&LogicalVec2::zero());
347 let constraint_space =
348 ConstraintSpace::new(SizeConstraint::default(), style, preferred_aspect_ratio);
349 let content_sizes = self
350 .context
351 .inline_content_sizes(layout_context, &constraint_space);
352 let containing_block_for_children = ContainingBlock {
353 size: ContainingBlockSize {
354 inline: content_sizes.sizes.max_content,
355 block: SizeConstraint::default(),
356 },
357 style,
358 };
359
360 let layout = self.context.layout(
361 layout_context,
362 positioning_context,
363 &containing_block_for_children,
364 containing_block,
365 preferred_aspect_ratio,
366 &LazySize::intrinsic(),
367 );
368
369 let max_inline_size = layout
370 .fragments
371 .iter()
372 .map(|fragment| {
373 fragment
374 .base()
375 .map(|base| base.rect())
376 .unwrap_or_default()
377 .to_logical(&containing_block_for_children)
378 .max_inline_position()
379 })
380 .max()
381 .unwrap_or_default();
382
383 let pbm_of_list_item =
393 LayoutStyle::Default(&self.list_item_style).padding_border_margin(containing_block);
394 let content_rect = LogicalRect {
395 start_corner: LogicalVec2 {
396 inline: -max_inline_size -
397 (pbm_of_list_item.border.inline_start +
398 pbm_of_list_item.padding.inline_start),
399 block: Zero::zero(),
400 },
401 size: LogicalVec2 {
402 inline: max_inline_size,
403 block: layout.content_block_size,
404 },
405 };
406
407 let mut base_fragment_info = BaseFragmentInfo::anonymous();
408 base_fragment_info.flags |= FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER;
409
410 Fragment::Box(
411 BoxFragment::new(
412 base_fragment_info,
413 style.clone(),
414 layout.fragments,
415 content_rect.as_physical(Some(containing_block)),
416 PhysicalSides::zero(),
417 PhysicalSides::zero(),
418 PhysicalSides::zero(),
419 layout.specific_layout_info,
420 )
421 .into(),
422 )
423 }
424
425 fn repair_style(
426 &mut self,
427 context: &SharedStyleContext,
428 node: &ServoLayoutNode,
429 new_style: &ServoArc<ComputedValues>,
430 ) {
431 self.list_item_style = node.parent_style(context);
432 self.context.repair_style(context, node, new_style);
433 }
434}
435
436impl BlockFormattingContext {
437 pub(super) fn layout(
438 &self,
439 layout_context: &LayoutContext,
440 positioning_context: &mut PositioningContext,
441 containing_block: &ContainingBlock,
442 lazy_block_size: &LazySize,
443 base: Option<&LayoutBoxBase>,
444 ) -> IndependentFormattingContextLayoutResult {
445 let mut sequential_layout_state =
446 if self.contains_floats || !layout_context.allow_parallel_layout {
447 Some(SequentialLayoutState::new(containing_block.size.inline))
448 } else {
449 None
450 };
451
452 let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
456
457 let previous_positioning_context_len = positioning_context.len();
460
461 let mut flow_layout = self.contents.layout(
462 layout_context,
463 positioning_context,
464 containing_block,
465 sequential_layout_state.as_mut(),
466 CollapsibleWithParentStartMargin(false),
467 ignore_block_margins_for_stretch,
468 );
469 debug_assert!(
470 !flow_layout
471 .collapsible_margins_in_children
472 .collapsed_through
473 );
474
475 let clearance = sequential_layout_state.and_then(|sequential_layout_state| {
479 sequential_layout_state.calculate_clearance(Clear::Both, &CollapsedMargin::zero())
480 });
481
482 let content_block_size = flow_layout.content_block_size +
483 flow_layout.collapsible_margins_in_children.end.solve() +
484 clearance.unwrap_or_default();
485
486 if let Some(base) = base &&
490 base.base_fragment_info
491 .flags
492 .contains(FragmentFlags::IS_BUTTON)
493 {
494 flow_layout.depends_on_block_constraints = true;
495 let final_block_size = lazy_block_size.resolve(|| content_block_size);
496 let align_fragment_rect = LogicalRect {
497 start_corner: LogicalVec2 {
498 inline: Au::zero(),
499 block: Au::zero().max((final_block_size - content_block_size) / 2),
500 },
501 size: LogicalVec2 {
502 inline: containing_block.size.inline,
503 block: content_block_size,
504 },
505 }
506 .as_physical(Some(containing_block));
507 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
508 &align_fragment_rect.origin.to_vector(),
509 previous_positioning_context_len,
510 );
511 let align_fragment = PositioningFragment::new_anonymous(
512 base.style.clone(),
513 align_fragment_rect,
514 flow_layout.fragments,
515 false, );
517 flow_layout.fragments = vec![Fragment::Positioning(align_fragment)];
518 }
519
520 IndependentFormattingContextLayoutResult {
521 fragments: flow_layout.fragments,
522 content_block_size,
523 content_inline_size_for_table: None,
524 baselines: flow_layout.baselines,
525 depends_on_block_constraints: flow_layout.depends_on_block_constraints,
526 specific_layout_info: None,
527 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
528 }
529 }
530
531 #[inline]
532 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
533 LayoutStyle::Default(&base.style)
534 }
535
536 pub(crate) fn repair_style(
537 &mut self,
538 context: &SharedStyleContext,
539 node: &ServoLayoutNode,
540 new_style: &ServoArc<ComputedValues>,
541 ) {
542 self.contents.repair_style(context, node, new_style);
543 }
544
545 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
546 self.contents.attached_to_tree(layout_box);
547 }
548
549 pub(crate) fn inline_formatting_context(&self) -> Option<&InlineFormattingContext> {
550 self.contents.inline_formatting_context()
551 }
552}
553
554fn compute_inline_content_sizes_for_block_level_boxes(
560 boxes: &[ArcRefCell<BlockLevelBox>],
561 layout_context: &LayoutContext,
562 containing_block: &IndefiniteContainingBlock,
563) -> InlineContentSizesResult {
564 let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
565 match &*box_.borrow() {
566 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
567 BlockLevelBox::OutsideMarker { .. } => None,
568 BlockLevelBox::OutOfFlowFloatBox(float_box) => {
569 let inline_content_sizes_result = float_box.contents.outer_inline_content_sizes(
570 layout_context,
571 containing_block,
572 &LogicalVec2::zero(),
573 false, skippable_for_block_percentage_quirk(
575 layout_context,
576 &float_box.contents.base,
577 Some(&float_box.contents),
578 ),
579 );
580 let style = &float_box.contents.style();
581 let container_writing_mode = containing_block.style.writing_mode;
582 Some((
583 inline_content_sizes_result,
584 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode),
585 Clear::from_style_and_container_writing_mode(style, container_writing_mode),
586 ))
587 },
588 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
589 let base = &same_formatting_context_block.base;
590 let contents = &same_formatting_context_block.contents;
591 let is_anonymous_block =
592 matches!(base.style.pseudo(), Some(PseudoElement::ServoAnonymousBox));
593 let inline_content_sizes_result = sizing::outer_inline(
594 base,
595 &contents.layout_style(base),
596 containing_block,
597 &LogicalVec2::zero(),
598 false, false, !is_anonymous_block, skippable_for_block_percentage_quirk(layout_context, base, None),
602 |_| None, |constraint_space| {
604 base.inline_content_sizes(layout_context, constraint_space, contents)
605 },
606 |_aspect_ratio| None,
607 );
608 let clear = if is_anonymous_block {
617 Clear::None
618 } else {
619 Clear::Both
620 };
621 Some((inline_content_sizes_result, None, clear))
622 },
623 BlockLevelBox::Independent(independent) => {
624 let inline_content_sizes_result = independent.outer_inline_content_sizes(
625 layout_context,
626 containing_block,
627 &LogicalVec2::zero(),
628 false, skippable_for_block_percentage_quirk(
630 layout_context,
631 &independent.base,
632 Some(independent),
633 ),
634 );
635 Some((
636 inline_content_sizes_result,
637 None,
638 Clear::from_style_and_container_writing_mode(
639 independent.style(),
640 containing_block.style.writing_mode,
641 ),
642 ))
643 },
644 }
645 };
646
647 #[derive(Default)]
650 struct AccumulatedData {
651 depends_on_block_constraints: bool,
653 max_size: ContentSizes,
655 floats: LogicalSides1D<ContentSizes>,
658 }
659
660 impl AccumulatedData {
661 fn max_size_including_uncleared_floats(&self) -> ContentSizes {
662 self.max_size.max(self.floats.start.union(&self.floats.end))
663 }
664 fn clear_floats(&mut self, clear: Clear) {
665 match clear {
666 Clear::InlineStart => {
667 self.max_size = self.max_size_including_uncleared_floats();
668 self.floats.start = ContentSizes::default();
669 },
670 Clear::InlineEnd => {
671 self.max_size = self.max_size_including_uncleared_floats();
672 self.floats.end = ContentSizes::default();
673 },
674 Clear::Both => {
675 self.max_size = self.max_size_including_uncleared_floats();
676 self.floats = LogicalSides1D::default();
677 },
678 Clear::None => {},
679 };
680 }
681 }
682
683 let accumulate =
684 |mut data: AccumulatedData,
685 (inline_content_sizes_result, float, clear): (InlineContentSizesResult, _, _)| {
686 let size = inline_content_sizes_result.sizes.max(ContentSizes::zero());
687 let depends_on_block_constraints =
688 inline_content_sizes_result.depends_on_block_constraints;
689 data.depends_on_block_constraints |= depends_on_block_constraints;
690 data.clear_floats(clear);
691 match float {
692 Some(FloatSide::InlineStart) => data.floats.start.union_assign(&size),
693 Some(FloatSide::InlineEnd) => data.floats.end.union_assign(&size),
694 None => {
695 data.max_size
696 .max_assign(data.floats.start.union(&data.floats.end).union(&size));
697 data.floats = LogicalSides1D::default();
698 },
699 }
700 data
701 };
702
703 let job_counts = boxes
704 .iter()
705 .map(|block_level_box| block_level_box.borrow().subtree_size());
706 let data = if layout_context.should_parallelize_layout(job_counts) {
707 boxes
708 .par_iter()
709 .filter_map(get_box_info)
710 .collect::<Vec<_>>()
711 .into_iter()
712 .fold(AccumulatedData::default(), accumulate)
713 } else {
714 boxes
715 .iter()
716 .filter_map(get_box_info)
717 .fold(AccumulatedData::default(), accumulate)
718 };
719 InlineContentSizesResult {
720 depends_on_block_constraints: data.depends_on_block_constraints,
721 sizes: data.max_size_including_uncleared_floats(),
722 }
723}
724
725impl BlockContainer {
726 fn layout(
727 &self,
728 layout_context: &LayoutContext,
729 positioning_context: &mut PositioningContext,
730 containing_block: &ContainingBlock,
731 sequential_layout_state: Option<&mut SequentialLayoutState>,
732 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
733 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
734 ) -> IndependentFormattingContextLayoutResult {
735 match self {
736 BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children(
737 layout_context,
738 positioning_context,
739 child_boxes,
740 containing_block,
741 sequential_layout_state,
742 collapsible_with_parent_start_margin,
743 ignore_block_margins_for_stretch,
744 ),
745 BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
746 layout_context,
747 positioning_context,
748 containing_block,
749 sequential_layout_state,
750 collapsible_with_parent_start_margin,
751 ignore_block_margins_for_stretch,
752 ),
753 }
754 }
755
756 #[inline]
757 pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
758 LayoutStyle::Default(&base.style)
759 }
760
761 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
762 match self {
763 Self::BlockLevelBoxes(child_boxes) => {
764 for child_box in child_boxes {
765 child_box.borrow_mut().with_base_mut(|base| {
766 base.parent_box.replace(layout_box.clone());
767 });
768 }
769 },
770 Self::InlineFormattingContext(ifc) => ifc.attached_to_tree(layout_box),
771 }
772 }
773
774 fn find_block_margin_collapsing_with_parent(
775 &self,
776 layout_context: &LayoutContext,
777 collected_margin: &mut CollapsedMargin,
778 containing_block_for_children: &ContainingBlock,
779 ) -> bool {
780 match self {
781 BlockContainer::BlockLevelBoxes(boxes) => boxes.iter().all(|block_level_box| {
782 block_level_box
783 .borrow()
784 .find_block_margin_collapsing_with_parent(
785 layout_context,
786 collected_margin,
787 containing_block_for_children,
788 )
789 }),
790 BlockContainer::InlineFormattingContext(context) => context
791 .find_block_margin_collapsing_with_parent(
792 layout_context,
793 collected_margin,
794 containing_block_for_children,
795 ),
796 }
797 }
798}
799
800impl ComputeInlineContentSizes for BlockContainer {
801 fn compute_inline_content_sizes(
802 &self,
803 layout_context: &LayoutContext,
804 constraint_space: &ConstraintSpace,
805 ) -> InlineContentSizesResult {
806 match &self {
807 Self::BlockLevelBoxes(boxes) => compute_inline_content_sizes_for_block_level_boxes(
808 boxes,
809 layout_context,
810 &constraint_space.into(),
811 ),
812 Self::InlineFormattingContext(context) => {
813 context.compute_inline_content_sizes(layout_context, constraint_space)
814 },
815 }
816 }
817}
818
819fn layout_block_level_children(
820 layout_context: &LayoutContext,
821 positioning_context: &mut PositioningContext,
822 child_boxes: &[ArcRefCell<BlockLevelBox>],
823 containing_block: &ContainingBlock,
824 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
825 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
826 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
827) -> IndependentFormattingContextLayoutResult {
828 let mut placement_state =
829 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
830
831 let fragments = match sequential_layout_state {
832 Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
833 layout_context,
834 positioning_context,
835 child_boxes,
836 sequential_layout_state,
837 &mut placement_state,
838 ignore_block_margins_for_stretch,
839 ),
840 None => layout_block_level_children_in_parallel(
841 layout_context,
842 positioning_context,
843 child_boxes,
844 &mut placement_state,
845 ignore_block_margins_for_stretch,
846 ),
847 };
848
849 let depends_on_block_constraints = fragments.iter().any(|fragment| {
850 fragment.base().is_some_and(|base| {
851 base.flags.contains(
852 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
853 )
854 })
855 });
856
857 let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
858 IndependentFormattingContextLayoutResult {
859 fragments,
860 content_block_size,
861 collapsible_margins_in_children,
862 baselines,
863 depends_on_block_constraints,
864 content_inline_size_for_table: None,
865 specific_layout_info: None,
866 }
867}
868
869fn layout_block_level_children_in_parallel(
870 layout_context: &LayoutContext,
871 positioning_context: &mut PositioningContext,
872 child_boxes: &[ArcRefCell<BlockLevelBox>],
873 placement_state: &mut PlacementState,
874 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
875) -> Vec<Fragment> {
876 let mut layout_results: Vec<(Fragment, PositioningContext)> =
877 Vec::with_capacity(child_boxes.len());
878
879 child_boxes
880 .par_iter()
881 .map(|child_box| {
882 let mut child_positioning_context = PositioningContext::default();
883 let fragment = child_box.borrow().layout(
884 layout_context,
885 &mut child_positioning_context,
886 placement_state.containing_block,
887 None,
888 None,
889 ignore_block_margins_for_stretch,
890 false, );
892 (fragment, child_positioning_context)
893 })
894 .collect_into_vec(&mut layout_results);
895
896 layout_results
897 .into_iter()
898 .map(|(mut fragment, mut child_positioning_context)| {
899 placement_state.place_fragment_and_update_baseline(&mut fragment, None);
900 child_positioning_context.adjust_static_position_of_hoisted_fragments(
901 &fragment,
902 PositioningContextLength::zero(),
903 );
904 positioning_context.append(child_positioning_context);
905 fragment
906 })
907 .collect()
908}
909
910fn layout_block_level_children_sequentially(
911 layout_context: &LayoutContext,
912 positioning_context: &mut PositioningContext,
913 child_boxes: &[ArcRefCell<BlockLevelBox>],
914 sequential_layout_state: &mut SequentialLayoutState,
915 placement_state: &mut PlacementState,
916 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
917) -> Vec<Fragment> {
918 child_boxes
922 .iter()
923 .map(|child_box| {
924 layout_block_level_child(
925 layout_context,
926 positioning_context,
927 &child_box.borrow(),
928 Some(sequential_layout_state),
929 placement_state,
930 ignore_block_margins_for_stretch,
931 false, )
933 })
934 .collect()
935}
936
937fn layout_block_level_child(
938 layout_context: &LayoutContext,
939 positioning_context: &mut PositioningContext,
940 child_box: &BlockLevelBox,
941 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
942 placement_state: &mut PlacementState,
943 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
944 has_inline_parent: bool,
945) -> Fragment {
946 let positioning_context_length_before_layout = positioning_context.len();
947 let mut fragment = child_box.layout(
948 layout_context,
949 positioning_context,
950 placement_state.containing_block,
951 sequential_layout_state.as_deref_mut(),
952 Some(CollapsibleWithParentStartMargin(
953 placement_state.next_in_flow_margin_collapses_with_parent_start_margin,
954 )),
955 ignore_block_margins_for_stretch,
956 has_inline_parent,
957 );
958
959 placement_state.place_fragment_and_update_baseline(&mut fragment, sequential_layout_state);
960 positioning_context.adjust_static_position_of_hoisted_fragments(
961 &fragment,
962 positioning_context_length_before_layout,
963 );
964
965 fragment
966}
967
968impl BlockLevelBox {
969 #[expect(clippy::too_many_arguments)]
970 fn layout(
971 &self,
972 layout_context: &LayoutContext,
973 positioning_context: &mut PositioningContext,
974 containing_block: &ContainingBlock,
975 sequential_layout_state: Option<&mut SequentialLayoutState>,
976 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
977 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
978 has_inline_parent: bool,
979 ) -> Fragment {
980 let fragment = match self {
981 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
982 Fragment::Box(
983 same_formatting_context_block.layout_in_flow_non_replaced_block_level_cached(
984 layout_context,
985 positioning_context,
986 containing_block,
987 sequential_layout_state,
988 collapsible_with_parent_start_margin,
989 ignore_block_margins_for_stretch,
990 has_inline_parent,
991 ),
992 )
993 },
994 BlockLevelBox::Independent(independent) => Fragment::Box(
995 positioning_context
996 .layout_maybe_position_relative_fragment(
997 layout_context,
998 containing_block,
999 &independent.base,
1000 |positioning_context| {
1001 independent.layout_in_flow_block_level(
1002 layout_context,
1003 positioning_context,
1004 containing_block,
1005 sequential_layout_state,
1006 ignore_block_margins_for_stretch,
1007 has_inline_parent,
1008 )
1009 },
1010 )
1011 .into(),
1012 ),
1013 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
1014 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
1018 box_.clone(),
1019 PhysicalRect::zero(),
1023 LogicalVec2 {
1024 inline: AlignFlags::START,
1025 block: AlignFlags::START,
1026 },
1027 containing_block.style.writing_mode,
1028 );
1029 let hoisted_fragment = hoisted_box.fragment.clone();
1030 positioning_context.push(hoisted_box);
1031 Fragment::AbsoluteOrFixedPositionedPlaceholder(hoisted_fragment)
1032 },
1033 BlockLevelBox::OutOfFlowFloatBox(float_box) => Fragment::Float(
1034 float_box
1035 .layout(layout_context, positioning_context, containing_block)
1036 .into(),
1037 ),
1038 BlockLevelBox::OutsideMarker(outside_marker) => {
1039 outside_marker.layout(layout_context, containing_block, positioning_context)
1040 },
1041 };
1042
1043 self.with_base(|base| base.set_fragment(fragment.clone()));
1044
1045 fragment
1046 }
1047
1048 fn inline_content_sizes(
1049 &self,
1050 layout_context: &LayoutContext,
1051 constraint_space: &ConstraintSpace,
1052 ) -> InlineContentSizesResult {
1053 let independent_formatting_context = match self {
1054 BlockLevelBox::Independent(independent) => independent,
1055 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => &box_.borrow().context,
1056 BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents,
1057 BlockLevelBox::OutsideMarker(outside_marker) => &outside_marker.context,
1058 BlockLevelBox::SameFormattingContextBlock(same_formatting_context_block) => {
1059 return same_formatting_context_block
1060 .inline_content_sizes(layout_context, constraint_space);
1061 },
1062 };
1063 independent_formatting_context.inline_content_sizes(layout_context, constraint_space)
1064 }
1065}
1066
1067impl IndependentFormattingContext {
1068 pub(crate) fn layout_in_flow_block_level(
1076 &self,
1077 layout_context: &LayoutContext,
1078 positioning_context: &mut PositioningContext,
1079 containing_block: &ContainingBlock,
1080 sequential_layout_state: Option<&mut SequentialLayoutState>,
1081 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1082 has_inline_parent: bool,
1083 ) -> BoxFragment {
1084 if let Some(sequential_layout_state) = sequential_layout_state {
1085 return self.layout_in_flow_block_level_sequentially(
1086 layout_context,
1087 positioning_context,
1088 containing_block,
1089 sequential_layout_state,
1090 ignore_block_margins_for_stretch,
1091 has_inline_parent,
1092 self,
1093 );
1094 }
1095
1096 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
1097 self.inline_content_sizes(layout_context, constraint_space)
1098 .sizes
1099 };
1100 let layout_style = self.layout_style();
1101 let ContainingBlockPaddingAndBorder {
1102 containing_block: containing_block_for_children,
1103 pbm,
1104 block_sizes,
1105 depends_on_block_constraints,
1106 block_stretch_size,
1107 justify_self,
1108 preferred_aspect_ratio,
1109 automatic_block_size,
1110 } = solve_containing_block_padding_and_border_for_in_flow_box(
1111 layout_context,
1112 containing_block,
1113 &layout_style,
1114 get_inline_content_sizes,
1115 ignore_block_margins_for_stretch,
1116 &self.base,
1117 Some(self),
1118 has_inline_parent,
1119 );
1120
1121 let lazy_block_size = LazySize::new(
1122 &block_sizes,
1123 LogicalAxis::Block,
1124 automatic_block_size,
1125 Au::zero,
1126 block_stretch_size,
1127 layout_style.is_table(),
1128 );
1129
1130 let layout = self.layout(
1131 layout_context,
1132 positioning_context,
1133 &containing_block_for_children,
1134 containing_block,
1135 preferred_aspect_ratio,
1136 &lazy_block_size,
1137 );
1138
1139 let inline_size = layout
1140 .content_inline_size_for_table
1141 .unwrap_or(containing_block_for_children.size.inline);
1142 let block_size = lazy_block_size.resolve(|| layout.content_block_size);
1143
1144 let ResolvedMargins {
1145 margin,
1146 effective_margin_inline_start,
1147 } = solve_margins(containing_block, &pbm, inline_size, justify_self);
1148
1149 let content_rect = LogicalRect {
1150 start_corner: LogicalVec2 {
1151 block: pbm.padding.block_start + pbm.border.block_start,
1152 inline: pbm.padding.inline_start +
1153 pbm.border.inline_start +
1154 effective_margin_inline_start,
1155 },
1156 size: LogicalVec2 {
1157 block: block_size,
1158 inline: inline_size,
1159 },
1160 };
1161
1162 let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
1163 let containing_block_writing_mode = containing_block.style.writing_mode;
1164
1165 let mut base_fragment_info = self.base.base_fragment_info;
1166 if depends_on_block_constraints {
1167 base_fragment_info.flags.insert(
1168 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1169 );
1170 }
1171 BoxFragment::new(
1172 base_fragment_info,
1173 self.base.style.clone(),
1174 layout.fragments,
1175 content_rect.as_physical(Some(containing_block)),
1176 pbm.padding.to_physical(containing_block_writing_mode),
1177 pbm.border.to_physical(containing_block_writing_mode),
1178 margin.to_physical(containing_block_writing_mode),
1179 layout.specific_layout_info,
1180 )
1181 .with_baselines(layout.baselines)
1182 .with_block_level_layout_info(block_margins_collapsed_with_children, None)
1183 }
1184
1185 #[expect(clippy::too_many_arguments)]
1189 fn layout_in_flow_block_level_sequentially(
1190 &self,
1191 layout_context: &LayoutContext<'_>,
1192 positioning_context: &mut PositioningContext,
1193 containing_block: &ContainingBlock<'_>,
1194 sequential_layout_state: &mut SequentialLayoutState,
1195 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1196 has_inline_parent: bool,
1197 context: &IndependentFormattingContext,
1198 ) -> BoxFragment {
1199 let style = &self.base.style;
1200 let containing_block_writing_mode = containing_block.style.writing_mode;
1201 let ContentBoxSizesAndPBM {
1202 content_box_sizes,
1203 pbm,
1204 depends_on_block_constraints,
1205 ..
1206 } = self
1207 .layout_style()
1208 .content_box_sizes_and_padding_border_margin(&containing_block.into());
1209
1210 let (margin_block_start, margin_block_end) =
1211 solve_block_margins_for_in_flow_block_level(&pbm);
1212 let collapsed_margin_block_start = CollapsedMargin::new(margin_block_start);
1213
1214 let mut content_size;
1225 let mut layout;
1226 let mut placement_rect;
1227
1228 let clear_position = sequential_layout_state.calculate_clear_position(
1232 Clear::from_style_and_container_writing_mode(style, containing_block_writing_mode),
1233 &collapsed_margin_block_start,
1234 );
1235 let ceiling = clear_position.unwrap_or_else(|| {
1236 sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
1237 });
1238
1239 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1241 let block_stretch_size = containing_block.block_stretch_size(pbm_sums.block);
1242 let automatic_block_size = automatic_block_size(layout_context, &self.base);
1243 let is_table = self.is_table();
1244 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
1245 let skippable_for_block_percentage_quirk =
1246 skippable_for_block_percentage_quirk(layout_context, &context.base, Some(context));
1247
1248 #[derive(Default)]
1249 struct Cache {
1250 min_block_size: Au,
1251 max_block_size: Option<Au>,
1252 tentative_block_size: SizeConstraint,
1253 depends_on_stretch_size: bool,
1254 }
1255 let mut cache = Cache {
1256 depends_on_stretch_size: true,
1257 ..Default::default()
1258 };
1259
1260 let update_cache = |cache: &mut Cache, stretch_size| {
1261 let tentative_block_content_size = self
1262 .tentative_block_content_size_with_dependency(preferred_aspect_ratio, stretch_size);
1263 let (preferred_block_size, min_block_size, max_block_size, depends_on_stretch_size) =
1264 if let Some(result) = tentative_block_content_size {
1265 let (block_content_size, depends_on_stretch_size) = result;
1266 let (preferred, min, max) = content_box_sizes.block.resolve_each(
1267 automatic_block_size,
1268 Au::zero,
1269 block_stretch_size,
1270 || block_content_size,
1271 is_table,
1272 );
1273 (Some(preferred), min, max, depends_on_stretch_size)
1274 } else {
1275 let (preferred, min, max) = content_box_sizes.block.resolve_each_extrinsic(
1276 automatic_block_size,
1277 Au::zero(),
1278 block_stretch_size,
1279 );
1280 (preferred, min, max, false)
1281 };
1282 cache.min_block_size = min_block_size;
1283 cache.max_block_size = max_block_size;
1284 cache.tentative_block_size =
1285 SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
1286 if skippable_for_block_percentage_quirk {
1287 cache
1288 .tentative_block_size
1289 .replace_if_indefinite_with(containing_block.size.block);
1290 }
1291 cache.depends_on_stretch_size = depends_on_stretch_size;
1292 };
1293
1294 let get_inline_content_sizes = |cache: &Cache| {
1296 let constraint_space =
1297 ConstraintSpace::new(cache.tentative_block_size, style, preferred_aspect_ratio);
1298 self.inline_content_sizes(layout_context, &constraint_space)
1299 .sizes
1300 };
1301
1302 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1303 let automatic_inline_size = automatic_inline_size(justify_self, Some(self));
1304 let compute_inline_size = |cache: &mut Cache, stretch_size| {
1305 let stretch_size = Some(stretch_size);
1306 if cache.depends_on_stretch_size {
1307 update_cache(cache, stretch_size);
1308 }
1309 content_box_sizes.inline.resolve(
1310 LogicalAxis::Inline,
1311 automatic_inline_size,
1312 Au::zero,
1313 stretch_size,
1314 || get_inline_content_sizes(cache),
1315 is_table,
1316 )
1317 };
1318
1319 let get_lazy_block_size = || {
1320 LazySize::new(
1321 &content_box_sizes.block,
1322 LogicalAxis::Block,
1323 automatic_block_size,
1324 Au::zero,
1325 block_stretch_size,
1326 is_table,
1327 )
1328 };
1329
1330 let inline_size_with_max_available_space = compute_inline_size(&mut cache, MAX_AU);
1339 let inline_size_with_no_available_space = compute_inline_size(&mut cache, Au::zero());
1340 if inline_size_with_no_available_space == inline_size_with_max_available_space {
1341 let inline_size = inline_size_with_no_available_space;
1345 let lazy_block_size = get_lazy_block_size();
1346 layout = self.layout(
1347 layout_context,
1348 positioning_context,
1349 &ContainingBlock {
1350 size: ContainingBlockSize {
1351 inline: inline_size,
1352 block: cache.tentative_block_size,
1356 },
1357 style,
1358 },
1359 containing_block,
1360 preferred_aspect_ratio,
1361 &lazy_block_size,
1362 );
1363
1364 content_size = LogicalVec2 {
1365 block: lazy_block_size.resolve(|| layout.content_block_size),
1366 inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
1367 };
1368
1369 let mut placement = PlacementAmongFloats::new(
1370 &sequential_layout_state.floats,
1371 ceiling,
1372 content_size + pbm.padding_border_sums,
1373 &pbm,
1374 );
1375 placement_rect = placement.place();
1376 } else {
1377 let minimum_size_of_block = LogicalVec2 {
1383 inline: inline_size_with_no_available_space,
1387 block: match cache.tentative_block_size {
1395 SizeConstraint::Definite(size) if cache.max_block_size.is_some() => size,
1398 _ => cache.min_block_size,
1401 },
1402 } + pbm.padding_border_sums;
1403 let mut placement = PlacementAmongFloats::new(
1404 &sequential_layout_state.floats,
1405 ceiling,
1406 minimum_size_of_block,
1407 &pbm,
1408 );
1409
1410 loop {
1411 placement_rect = placement.place();
1413
1414 let inline_stretch_size =
1418 Au::zero().max(placement_rect.size.inline - pbm.padding_border_sums.inline);
1419 let proposed_inline_size = compute_inline_size(&mut cache, inline_stretch_size);
1420
1421 let positioning_context_length = positioning_context.len();
1425 let lazy_block_size = get_lazy_block_size();
1426 layout = self.layout(
1427 layout_context,
1428 positioning_context,
1429 &ContainingBlock {
1430 size: ContainingBlockSize {
1431 inline: proposed_inline_size,
1432 block: cache.tentative_block_size,
1433 },
1434 style,
1435 },
1436 containing_block,
1437 preferred_aspect_ratio,
1438 &lazy_block_size,
1439 );
1440
1441 let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
1442 debug_assert!(inline_size < proposed_inline_size);
1447 inline_size
1448 } else {
1449 proposed_inline_size
1450 };
1451 content_size = LogicalVec2 {
1452 block: lazy_block_size.resolve(|| layout.content_block_size),
1453 inline: inline_size,
1454 };
1455
1456 if placement.try_to_expand_for_auto_block_size(
1460 content_size.block + pbm.padding_border_sums.block,
1461 &placement_rect.size,
1462 ) {
1463 break;
1464 }
1465
1466 positioning_context.truncate(&positioning_context_length);
1470 }
1471 }
1472
1473 let has_clearance = clear_position.is_some() || placement_rect.start_corner.block > ceiling;
1477 let clearance = has_clearance.then(|| {
1478 placement_rect.start_corner.block -
1479 sequential_layout_state
1480 .position_with_zero_clearance(&collapsed_margin_block_start)
1481 });
1482
1483 let ((margin_inline_start, margin_inline_end), effective_margin_inline_start) =
1484 solve_inline_margins_avoiding_floats(
1485 sequential_layout_state,
1486 containing_block,
1487 &pbm,
1488 content_size.inline + pbm.padding_border_sums.inline,
1489 placement_rect,
1490 justify_self,
1491 );
1492
1493 let margin = LogicalSides {
1494 inline_start: margin_inline_start,
1495 inline_end: margin_inline_end,
1496 block_start: margin_block_start,
1497 block_end: margin_block_end,
1498 };
1499
1500 if clearance.is_some() {
1503 sequential_layout_state.commit_margin();
1504 }
1505 sequential_layout_state.adjoin_assign(&collapsed_margin_block_start);
1506
1507 sequential_layout_state.commit_margin();
1509 sequential_layout_state.advance_block_position(
1510 pbm.padding_border_sums.block + content_size.block + clearance.unwrap_or_else(Au::zero),
1511 );
1512 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
1513
1514 let content_rect = LogicalRect {
1515 start_corner: LogicalVec2 {
1516 block: pbm.padding.block_start +
1517 pbm.border.block_start +
1518 clearance.unwrap_or_else(Au::zero),
1519 inline: pbm.padding.inline_start +
1520 pbm.border.inline_start +
1521 effective_margin_inline_start,
1522 },
1523 size: content_size,
1524 };
1525
1526 let mut base_fragment_info = self.base.base_fragment_info;
1527 if depends_on_block_constraints {
1528 base_fragment_info.flags.insert(
1529 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
1530 );
1531 }
1532
1533 BoxFragment::new(
1534 base_fragment_info,
1535 style.clone(),
1536 layout.fragments,
1537 content_rect.as_physical(Some(containing_block)),
1538 pbm.padding.to_physical(containing_block_writing_mode),
1539 pbm.border.to_physical(containing_block_writing_mode),
1540 margin.to_physical(containing_block_writing_mode),
1541 layout.specific_layout_info,
1542 )
1543 .with_baselines(layout.baselines)
1544 .with_block_level_layout_info(CollapsedBlockMargins::from_margin(&margin), clearance)
1545 }
1546}
1547
1548struct ContainingBlockPaddingAndBorder<'a> {
1549 containing_block: ContainingBlock<'a>,
1550 pbm: PaddingBorderMargin,
1551 block_sizes: Sizes,
1552 depends_on_block_constraints: bool,
1553 block_stretch_size: Option<Au>,
1554 justify_self: AlignFlags,
1555 preferred_aspect_ratio: Option<AspectRatio>,
1556 automatic_block_size: Size<Au>,
1557}
1558
1559struct ResolvedMargins {
1560 pub margin: LogicalSides<Au>,
1562
1563 pub effective_margin_inline_start: Au,
1570}
1571
1572#[expect(clippy::too_many_arguments)]
1578fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
1579 layout_context: &LayoutContext,
1580 containing_block: &ContainingBlock<'_>,
1581 layout_style: &'a LayoutStyle,
1582 get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
1583 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1584 base: &LayoutBoxBase,
1585 context: Option<&IndependentFormattingContext>,
1586 has_inline_parent: bool,
1587) -> ContainingBlockPaddingAndBorder<'a> {
1588 let style = layout_style.style();
1589 if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
1590 let containing_block_for_children = ContainingBlock {
1594 size: containing_block.size.clone(),
1595 style,
1596 };
1597 return ContainingBlockPaddingAndBorder {
1600 containing_block: containing_block_for_children,
1601 pbm: PaddingBorderMargin::zero(),
1602 block_sizes: Sizes::default(),
1603 depends_on_block_constraints: false,
1604 block_stretch_size: None,
1607 justify_self: AlignFlags::NORMAL,
1610 preferred_aspect_ratio: None,
1611 automatic_block_size: Size::FitContent,
1612 };
1613 }
1614
1615 let ContentBoxSizesAndPBM {
1616 content_box_sizes,
1617 pbm,
1618 depends_on_block_constraints,
1619 ..
1620 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
1621
1622 let pbm_sums = pbm.sums_auto_is_zero(ignore_block_margins_for_stretch);
1623 let inline_stretch_size = containing_block.inline_stretch_size(pbm_sums.inline);
1624 let block_stretch_size = containing_block.block_stretch_size(pbm_sums.block);
1625 let automatic_block_size = automatic_block_size(layout_context, base);
1626
1627 let preferred_aspect_ratio =
1630 context.and_then(|context| context.preferred_aspect_ratio(&pbm.padding_border_sums));
1631 let is_table = layout_style.is_table();
1632
1633 let tentative_block_content_size = context.and_then(|context| {
1636 context.tentative_block_content_size(preferred_aspect_ratio, inline_stretch_size)
1637 });
1638 let mut tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
1639 SizeConstraint::Definite(content_box_sizes.block.resolve(
1640 LogicalAxis::Block,
1641 automatic_block_size,
1642 Au::zero,
1643 block_stretch_size,
1644 || block_content_size,
1645 is_table,
1646 ))
1647 } else {
1648 content_box_sizes.block.resolve_extrinsic(
1649 automatic_block_size,
1650 Au::zero(),
1651 block_stretch_size,
1652 )
1653 };
1654 if skippable_for_block_percentage_quirk(layout_context, base, context) {
1655 tentative_block_size.replace_if_indefinite_with(containing_block.size.block);
1656 }
1657
1658 let get_inline_content_sizes = || {
1661 get_inline_content_sizes(&ConstraintSpace::new(
1662 tentative_block_size,
1663 style,
1664 preferred_aspect_ratio,
1665 ))
1666 };
1667 let justify_self = resolve_justify_self(style, containing_block.style, has_inline_parent);
1668 let inline_size = content_box_sizes.inline.resolve(
1669 LogicalAxis::Inline,
1670 automatic_inline_size(justify_self, context),
1671 Au::zero,
1672 inline_stretch_size,
1673 get_inline_content_sizes,
1674 is_table,
1675 );
1676
1677 let containing_block_for_children = ContainingBlock {
1678 size: ContainingBlockSize {
1679 inline: inline_size,
1680 block: tentative_block_size,
1681 },
1682 style,
1683 };
1684 ContainingBlockPaddingAndBorder {
1686 containing_block: containing_block_for_children,
1687 pbm,
1688 block_sizes: content_box_sizes.block,
1689 depends_on_block_constraints,
1690 block_stretch_size,
1691 justify_self,
1692 preferred_aspect_ratio,
1693 automatic_block_size,
1694 }
1695}
1696
1697fn solve_margins(
1702 containing_block: &ContainingBlock<'_>,
1703 pbm: &PaddingBorderMargin,
1704 inline_size: Au,
1705 justify_self: AlignFlags,
1706) -> ResolvedMargins {
1707 let (inline_margins, effective_margin_inline_start) =
1708 solve_inline_margins_for_in_flow_block_level(
1709 containing_block,
1710 pbm,
1711 inline_size,
1712 justify_self,
1713 );
1714 let block_margins = solve_block_margins_for_in_flow_block_level(pbm);
1715 ResolvedMargins {
1716 margin: LogicalSides {
1717 inline_start: inline_margins.0,
1718 inline_end: inline_margins.1,
1719 block_start: block_margins.0,
1720 block_end: block_margins.1,
1721 },
1722 effective_margin_inline_start,
1723 }
1724}
1725
1726fn solve_block_margins_for_in_flow_block_level(pbm: &PaddingBorderMargin) -> (Au, Au) {
1730 (
1731 pbm.margin.block_start.auto_is(Au::zero),
1732 pbm.margin.block_end.auto_is(Au::zero),
1733 )
1734}
1735
1736fn resolve_justify_self(
1738 style: &ComputedValues,
1739 containing_block_style: &ComputedValues,
1740 has_inline_parent: bool,
1741) -> AlignFlags {
1742 let alignment = match style.get_justify_self().0 {
1748 AlignFlags::AUTO if has_inline_parent => AlignFlags::NORMAL,
1749 AlignFlags::AUTO => containing_block_style.get_justify_items().computed.0.0,
1750 alignment => alignment,
1751 };
1752 let is_ltr = |style: &ComputedValues| style.writing_mode.line_left_is_inline_start();
1753 let alignment_value = match alignment.value() {
1754 AlignFlags::LEFT if is_ltr(containing_block_style) => AlignFlags::START,
1755 AlignFlags::LEFT => AlignFlags::END,
1756 AlignFlags::RIGHT if is_ltr(containing_block_style) => AlignFlags::END,
1757 AlignFlags::RIGHT => AlignFlags::START,
1758 AlignFlags::SELF_START if is_ltr(containing_block_style) == is_ltr(style) => {
1759 AlignFlags::START
1760 },
1761 AlignFlags::SELF_START => AlignFlags::END,
1762 AlignFlags::SELF_END if is_ltr(containing_block_style) == is_ltr(style) => AlignFlags::END,
1763 AlignFlags::SELF_END => AlignFlags::START,
1764 alignment_value => alignment_value,
1765 };
1766 alignment.flags() | alignment_value
1767}
1768
1769#[inline]
1772fn automatic_inline_size<T>(
1773 justify_self: AlignFlags,
1774 context: Option<&IndependentFormattingContext>,
1775) -> Size<T> {
1776 let normal_stretches = || {
1777 !context.is_some_and(|context| {
1778 context
1779 .base
1780 .base_fragment_info
1781 .flags
1782 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET) ||
1783 context.is_table()
1784 })
1785 };
1786 match justify_self {
1787 AlignFlags::STRETCH => Size::Stretch,
1788 AlignFlags::NORMAL if normal_stretches() => Size::Stretch,
1789 _ => Size::FitContent,
1790 }
1791}
1792
1793#[inline]
1796fn automatic_block_size<T>(layout_context: &LayoutContext, base: &LayoutBoxBase) -> Size<T> {
1797 if layout_context.style_context.quirks_mode() == QuirksMode::Quirks &&
1807 base.base_fragment_info.flags.intersects(
1808 FragmentFlags::IS_ROOT_ELEMENT | FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT,
1809 )
1810 {
1811 Size::QuirkyStretch
1812 } else {
1813 Size::FitContent
1814 }
1815}
1816
1817fn justify_self_alignment(
1824 containing_block: &ContainingBlock,
1825 free_space: Au,
1826 justify_self: AlignFlags,
1827) -> Au {
1828 let mut alignment = justify_self.value();
1829 let is_safe = justify_self.flags() == AlignFlags::SAFE || alignment == AlignFlags::NORMAL;
1830 if is_safe && free_space <= Au::zero() {
1831 alignment = AlignFlags::START
1832 }
1833 match alignment {
1834 AlignFlags::NORMAL => {},
1835 AlignFlags::CENTER => return free_space / 2,
1836 AlignFlags::END => return free_space,
1837 _ => return Au::zero(),
1838 }
1839
1840 let style = containing_block.style;
1842 match style.get_text_align() {
1843 TextAlignKeyword::MozCenter => free_space / 2,
1844 TextAlignKeyword::MozLeft if !style.writing_mode.line_left_is_inline_start() => free_space,
1845 TextAlignKeyword::MozRight if style.writing_mode.line_left_is_inline_start() => free_space,
1846 _ => Au::zero(),
1847 }
1848}
1849
1850fn solve_inline_margins_for_in_flow_block_level(
1863 containing_block: &ContainingBlock,
1864 pbm: &PaddingBorderMargin,
1865 inline_size: Au,
1866 justify_self: AlignFlags,
1867) -> ((Au, Au), Au) {
1868 let free_space = containing_block.size.inline - pbm.padding_border_sums.inline - inline_size;
1869 let mut justification = Au::zero();
1870 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
1871 (AuOrAuto::Auto, AuOrAuto::Auto) => {
1872 let start = Au::zero().max(free_space / 2);
1873 (start, free_space - start)
1874 },
1875 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
1876 (Au::zero().max(free_space - end), end)
1877 },
1878 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, free_space - start),
1879 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
1880 justification =
1885 justify_self_alignment(containing_block, free_space - start - end, justify_self);
1886 (start, end)
1887 },
1888 };
1889 let effective_margin_inline_start = inline_margins.0 + justification;
1890 (inline_margins, effective_margin_inline_start)
1891}
1892
1893fn solve_inline_margins_avoiding_floats(
1903 sequential_layout_state: &SequentialLayoutState,
1904 containing_block: &ContainingBlock,
1905 pbm: &PaddingBorderMargin,
1906 inline_size: Au,
1907 placement_rect: LogicalRect<Au>,
1908 justify_self: AlignFlags,
1909) -> ((Au, Au), Au) {
1910 let free_space = Au::zero().max(placement_rect.size.inline - inline_size);
1914 let cb_info = &sequential_layout_state.floats.containing_block_info;
1915 let start_adjustment = placement_rect.start_corner.inline - cb_info.inline_start;
1916 let end_adjustment = cb_info.inline_end - placement_rect.max_inline_position();
1917 let mut justification = Au::zero();
1918 let inline_margins = match (pbm.margin.inline_start, pbm.margin.inline_end) {
1919 (AuOrAuto::Auto, AuOrAuto::Auto) => {
1920 let half = free_space / 2;
1921 (start_adjustment + half, end_adjustment + free_space - half)
1922 },
1923 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (start_adjustment + free_space, end),
1924 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => (start, end_adjustment + free_space),
1925 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
1926 justification = justify_self_alignment(containing_block, free_space, justify_self);
1932 (start, end)
1933 },
1934 };
1935 let effective_margin_inline_start = inline_margins.0.max(start_adjustment) + justification;
1936 (inline_margins, effective_margin_inline_start)
1937}
1938
1939struct PlacementState<'container> {
1944 next_in_flow_margin_collapses_with_parent_start_margin: bool,
1945 last_in_flow_margin_collapses_with_parent_end_margin: bool,
1946 start_margin: CollapsedMargin,
1947 current_margin: CollapsedMargin,
1948 current_block_direction_position: Au,
1949 inflow_baselines: Baselines,
1950 is_inline_block_context: bool,
1951
1952 marker_block_size: Option<Au>,
1957
1958 containing_block: &'container ContainingBlock<'container>,
1961}
1962
1963impl<'container> PlacementState<'container> {
1964 fn new(
1965 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1966 containing_block: &'container ContainingBlock<'container>,
1967 ) -> PlacementState<'container> {
1968 let is_inline_block_context =
1969 containing_block.style.get_box().get_display() == &Display::InlineBlock;
1970 PlacementState {
1971 next_in_flow_margin_collapses_with_parent_start_margin:
1972 collapsible_with_parent_start_margin.0,
1973 last_in_flow_margin_collapses_with_parent_end_margin: true,
1974 start_margin: CollapsedMargin::zero(),
1975 current_margin: CollapsedMargin::zero(),
1976 current_block_direction_position: Au::zero(),
1977 inflow_baselines: Baselines::default(),
1978 is_inline_block_context,
1979 marker_block_size: None,
1980 containing_block,
1981 }
1982 }
1983
1984 fn place_fragment_and_update_baseline(
1985 &mut self,
1986 fragment: &mut Fragment,
1987 sequential_layout_state: Option<&mut SequentialLayoutState>,
1988 ) {
1989 self.place_fragment(fragment, sequential_layout_state);
1990
1991 let box_fragment = match fragment {
1992 Fragment::LayoutRoot(..) | Fragment::Box(..) => fragment
1993 .retrieve_box_fragment()
1994 .expect("Should be guaranteed by surrounding check"),
1995 _ => return,
1996 };
1997
1998 if self.is_inline_block_context && box_fragment.is_table_wrapper() {
2003 return;
2004 }
2005
2006 let box_block_offset = box_fragment
2007 .content_rect()
2008 .origin
2009 .to_logical(self.containing_block)
2010 .block;
2011 let box_fragment_baselines =
2012 box_fragment.baselines(self.containing_block.style.writing_mode);
2013 if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment_baselines.first) {
2014 self.inflow_baselines.first = Some(first + box_block_offset);
2015 }
2016 if let Some(last) = box_fragment_baselines.last {
2017 self.inflow_baselines.last = Some(last + box_block_offset);
2018 }
2019 }
2020
2021 fn place_fragment(
2024 &mut self,
2025 fragment: &mut Fragment,
2026 sequential_layout_state: Option<&mut SequentialLayoutState>,
2027 ) {
2028 match fragment {
2029 Fragment::LayoutRoot(..) | Fragment::Box(..) => {
2030 let fragment = fragment
2031 .retrieve_box_fragment()
2032 .expect("Should be guaranteed by surrounding condition");
2033
2034 let is_outside_marker = fragment
2043 .base
2044 .flags
2045 .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER);
2046 if is_outside_marker {
2047 assert!(self.marker_block_size.is_none());
2048 self.marker_block_size = Some(
2049 fragment
2050 .content_rect()
2051 .size
2052 .to_logical(self.containing_block.style.writing_mode)
2053 .block,
2054 );
2055 return;
2056 }
2057
2058 let BlockLevelLayoutInfo {
2059 clearance,
2060 block_margins_collapsed_with_children: fragment_block_margins,
2061 } = *fragment
2062 .block_level_layout_info
2063 .clone()
2064 .expect("A block-level fragment should have a BlockLevelLayoutInfo.");
2065 let mut fragment_block_size = fragment
2066 .border_rect()
2067 .size
2068 .to_logical(self.containing_block.style.writing_mode)
2069 .block;
2070
2071 if let Some(clearance) = clearance {
2077 fragment_block_size += clearance;
2078 self.current_block_direction_position += self.current_margin.solve();
2083 self.current_margin = CollapsedMargin::zero();
2084 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2085 if fragment_block_margins.collapsed_through {
2086 self.last_in_flow_margin_collapses_with_parent_end_margin = false;
2087 }
2088 } else if !fragment_block_margins.collapsed_through {
2089 self.last_in_flow_margin_collapses_with_parent_end_margin = true;
2090 }
2091
2092 if self.next_in_flow_margin_collapses_with_parent_start_margin {
2093 debug_assert!(self.current_margin.solve().is_zero());
2094 self.start_margin
2095 .adjoin_assign(&fragment_block_margins.start);
2096 if fragment_block_margins.collapsed_through {
2097 self.start_margin.adjoin_assign(&fragment_block_margins.end);
2098 return;
2099 }
2100 self.next_in_flow_margin_collapses_with_parent_start_margin = false;
2101 } else {
2102 self.current_margin
2103 .adjoin_assign(&fragment_block_margins.start);
2104 }
2105
2106 fragment.base.translate_rect(
2107 LogicalVec2 {
2108 inline: Au::zero(),
2109 block: self.current_margin.solve() + self.current_block_direction_position,
2110 }
2111 .to_physical_size(self.containing_block.style.writing_mode),
2112 );
2113
2114 if fragment_block_margins.collapsed_through {
2115 self.current_block_direction_position += fragment_block_size;
2118 self.current_margin
2119 .adjoin_assign(&fragment_block_margins.end);
2120 } else {
2121 self.current_block_direction_position +=
2122 self.current_margin.solve() + fragment_block_size;
2123 self.current_margin = fragment_block_margins.end;
2124 }
2125 },
2126 Fragment::AbsoluteOrFixedPositionedPlaceholder(fragment) => {
2127 fragment.borrow_mut().original_static_position_rect = LogicalRect {
2130 start_corner: LogicalVec2 {
2131 block: (self.current_margin.solve() +
2132 self.current_block_direction_position),
2133 inline: Au::zero(),
2134 },
2135 size: LogicalVec2::zero(),
2136 }
2137 .as_physical(Some(self.containing_block));
2138 },
2139 Fragment::Float(box_fragment) => {
2140 let sequential_layout_state = sequential_layout_state
2141 .expect("Found float fragment without SequentialLayoutState");
2142 let block_offset_from_containing_block_top =
2143 self.current_block_direction_position + self.current_margin.solve();
2144 sequential_layout_state.place_float_fragment(
2145 box_fragment,
2146 self.containing_block,
2147 self.start_margin,
2148 block_offset_from_containing_block_top,
2149 );
2150 },
2151 Fragment::Positioning(_) => {},
2152 _ => unreachable!("Unexpected Fragment type encountered during flow layout"),
2153 }
2154 }
2155
2156 fn finish(mut self) -> (Au, CollapsedBlockMargins, Baselines) {
2157 if !self.last_in_flow_margin_collapses_with_parent_end_margin {
2158 self.current_block_direction_position += self.current_margin.solve();
2159 self.current_margin = CollapsedMargin::zero();
2160 }
2161 let (total_block_size, collapsed_through) = match self.marker_block_size {
2162 Some(marker_block_size) => (
2163 self.current_block_direction_position.max(marker_block_size),
2164 false,
2167 ),
2168 None => (
2169 self.current_block_direction_position,
2170 self.next_in_flow_margin_collapses_with_parent_start_margin,
2171 ),
2172 };
2173
2174 (
2175 total_block_size,
2176 CollapsedBlockMargins {
2177 collapsed_through,
2178 start: self.start_margin,
2179 end: self.current_margin,
2180 },
2181 self.inflow_baselines,
2182 )
2183 }
2184}
2185
2186pub(crate) struct IndependentFloatOrAtomicLayoutResult {
2187 pub fragment: BoxFragment,
2188 pub baselines: Baselines,
2189 pub pbm_sums: LogicalSides<Au>,
2190}
2191
2192impl IndependentFormattingContext {
2193 pub(crate) fn layout_float_or_atomic_inline(
2194 &self,
2195 layout_context: &LayoutContext,
2196 child_positioning_context: &mut PositioningContext,
2197 containing_block: &ContainingBlock,
2198 ) -> IndependentFloatOrAtomicLayoutResult {
2199 let style = self.style();
2200 let container_writing_mode = containing_block.style.writing_mode;
2201 let layout_style = self.layout_style();
2202 let content_box_sizes_and_pbm =
2203 layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
2204 let pbm = &content_box_sizes_and_pbm.pbm;
2205 let margin = pbm.margin.auto_is(Au::zero);
2206 let pbm_sums = pbm.padding + pbm.border + margin;
2207 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
2208 let is_table = self.is_table();
2209
2210 let inline_stretch_size = containing_block.inline_stretch_size(pbm_sums.inline_sum());
2211 let block_stretch_size = containing_block.block_stretch_size(pbm_sums.block_sum());
2212
2213 let automatic_block_size = Size::FitContent;
2214 let tentative_block_content_size =
2215 self.tentative_block_content_size(preferred_aspect_ratio, inline_stretch_size);
2216 let mut tentative_block_size =
2217 if let Some(block_content_size) = tentative_block_content_size {
2218 SizeConstraint::Definite(content_box_sizes_and_pbm.content_box_sizes.block.resolve(
2219 LogicalAxis::Block,
2220 automatic_block_size,
2221 Au::zero,
2222 block_stretch_size,
2223 || block_content_size,
2224 is_table,
2225 ))
2226 } else {
2227 content_box_sizes_and_pbm
2228 .content_box_sizes
2229 .block
2230 .resolve_extrinsic(automatic_block_size, Au::zero(), block_stretch_size)
2231 };
2232 if skippable_for_block_percentage_quirk(layout_context, &self.base, Some(self)) {
2233 tentative_block_size.replace_if_indefinite_with(containing_block.size.block);
2234 }
2235
2236 let get_content_size = || {
2237 let constraint_space =
2238 ConstraintSpace::new(tentative_block_size, style, preferred_aspect_ratio);
2239 self.inline_content_sizes(layout_context, &constraint_space)
2240 .sizes
2241 };
2242
2243 let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
2244 LogicalAxis::Inline,
2245 automatic_block_size,
2246 Au::zero,
2247 inline_stretch_size,
2248 get_content_size,
2249 is_table,
2250 );
2251
2252 let containing_block_for_children = ContainingBlock {
2253 size: ContainingBlockSize {
2254 inline: inline_size,
2255 block: tentative_block_size,
2256 },
2257 style,
2258 };
2259 assert_eq!(
2260 container_writing_mode.is_horizontal(),
2261 style.writing_mode.is_horizontal(),
2262 "Mixed horizontal and vertical writing modes are not supported yet"
2263 );
2264
2265 let lazy_block_size = LazySize::new(
2266 &content_box_sizes_and_pbm.content_box_sizes.block,
2267 LogicalAxis::Block,
2268 automatic_block_size,
2269 Au::zero,
2270 block_stretch_size,
2271 is_table,
2272 );
2273
2274 let IndependentFormattingContextLayoutResult {
2275 content_inline_size_for_table,
2276 content_block_size,
2277 fragments,
2278 baselines,
2279 specific_layout_info,
2280 ..
2281 } = self.layout(
2282 layout_context,
2283 child_positioning_context,
2284 &containing_block_for_children,
2285 containing_block,
2286 preferred_aspect_ratio,
2287 &lazy_block_size,
2288 );
2289
2290 let content_size = LogicalVec2 {
2291 inline: content_inline_size_for_table.unwrap_or(inline_size),
2292 block: lazy_block_size.resolve(|| content_block_size),
2293 }
2294 .to_physical_size(container_writing_mode);
2295 let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size);
2296
2297 let mut base_fragment_info = self.base_fragment_info();
2298 if content_box_sizes_and_pbm.depends_on_block_constraints {
2299 base_fragment_info.flags.insert(
2300 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2301 );
2302 }
2303
2304 let fragment = BoxFragment::new(
2308 base_fragment_info,
2309 style.clone(),
2310 fragments,
2311 content_rect,
2312 pbm.padding.to_physical(container_writing_mode),
2313 pbm.border.to_physical(container_writing_mode),
2314 margin.to_physical(container_writing_mode),
2315 specific_layout_info,
2316 );
2317
2318 IndependentFloatOrAtomicLayoutResult {
2319 fragment,
2320 baselines,
2321 pbm_sums,
2322 }
2323 }
2324}
2325
2326#[inline]
2331fn skippable_for_block_percentage_quirk(
2332 layout_context: &LayoutContext,
2333 base: &LayoutBoxBase,
2334 context: Option<&IndependentFormattingContext>,
2335) -> bool {
2336 layout_context.style_context.quirks_mode() == QuirksMode::Quirks &&
2340 !base
2341 .base_fragment_info
2342 .flags
2343 .contains(FragmentFlags::IS_WIDGET) &&
2344 context.is_none_or(|context| context.is_block_container())
2345}