1use std::cell::{Cell, LazyCell};
6use std::cmp::Ordering;
7use std::sync::Arc;
8
9use app_units::Au;
10use atomic_refcell::AtomicRef;
11use itertools::izip;
12use rayon::iter::{
13 IndexedParallelIterator, IntoParallelRefIterator, ParallelDrainRange, ParallelIterator,
14};
15use style::Zero;
16use style::computed_values::position::T as Position;
17use style::logical_geometry::Direction;
18use style::properties::ComputedValues;
19use style::properties::longhands::align_items::computed_value::T as AlignItems;
20use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
21use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
22use style::values::computed::LengthPercentage;
23use style::values::generics::flex::GenericFlexBasis as FlexBasis;
24use style::values::generics::length::LengthPercentageOrNormal;
25use style::values::specified::align::AlignFlags;
26
27use super::geom::{FlexAxis, FlexRelativeRect, FlexRelativeSides, FlexRelativeVec2};
28use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox};
29use crate::cell::ArcRefCell;
30use crate::context::LayoutContext;
31use crate::dom::WeakLayoutBox;
32use crate::formatting_contexts::Baselines;
33use crate::fragment_tree::{
34 BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
35};
36use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
37use crate::layout_box_base::IndependentFormattingContextLayoutResult;
38use crate::positioned::{
39 AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
40};
41use crate::sizing::{
42 ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, IntrinsicSizingMode,
43 LazySize, Size, SizeConstraint, Sizes,
44};
45use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, LayoutStyle};
46use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
47
48struct FlexContext<'a> {
51 config: FlexContainerConfig,
52 layout_context: &'a LayoutContext<'a>,
53 containing_block: &'a ContainingBlock<'a>, container_inner_size_constraint: FlexRelativeVec2<SizeConstraint>,
55}
56
57struct FlexItem<'a> {
59 box_: &'a FlexItemBox,
60
61 content_cross_sizes: Sizes,
63
64 padding: FlexRelativeSides<Au>,
65 border: FlexRelativeSides<Au>,
66 margin: FlexRelativeSides<AuOrAuto>,
67
68 pbm_auto_is_zero: FlexRelativeVec2<Au>,
71
72 flex_base_size: Au,
74
75 flex_base_size_is_definite: bool,
79
80 hypothetical_main_size: Au,
82
83 content_min_main_size: Au,
86
87 content_max_main_size: Option<Au>,
90
91 align_self: AlignItems,
93
94 depends_on_block_constraints: bool,
96
97 preferred_aspect_ratio: Option<AspectRatio>,
99
100 automatic_cross_size: Size<Au>,
103 automatic_cross_size_for_intrinsic_sizing: Size<Au>,
104}
105
106enum FlexContent {
109 AbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
110 FlexItemPlaceholder,
111}
112
113struct FlexItemLayoutResult {
115 hypothetical_cross_size: Au,
116 fragments: Vec<Fragment>,
117 positioning_context: PositioningContext,
118
119 content_baselines_for_parent_relative_to_margin_box: Baselines,
122
123 flex_alignment_baseline_relative_to_margin_box: Option<Au>,
126
127 content_block_size: Au,
130
131 containing_block_size: ContainingBlockSize,
133
134 depends_on_block_constraints: bool,
136
137 specific_layout_info: Option<SpecificLayoutInfo>,
139}
140
141struct FlexLineItem<'a> {
144 item: FlexItem<'a>,
146
147 layout_result: FlexItemLayoutResult,
150
151 used_main_size: Au,
153}
154
155impl FlexLineItem<'_> {
156 fn get_or_synthesize_baseline_with_cross_size(&self, cross_size: Au) -> Au {
157 self.layout_result
158 .flex_alignment_baseline_relative_to_margin_box
159 .unwrap_or_else(|| {
160 self.item
161 .synthesized_baseline_relative_to_margin_box(cross_size)
162 })
163 }
164
165 #[allow(clippy::too_many_arguments)]
166 fn collect_fragment(
167 mut self,
168 initial_flex_layout: &InitialFlexLineLayout,
169 item_used_size: FlexRelativeVec2<Au>,
170 item_margin: FlexRelativeSides<Au>,
171 item_main_interval: Au,
172 final_line_cross_size: Au,
173 shared_alignment_baseline: &Option<Au>,
174 flex_context: &mut FlexContext,
175 all_baselines: &mut Baselines,
176 main_position_cursor: &mut Au,
177 ) -> (Arc<BoxFragment>, PositioningContext) {
178 *main_position_cursor +=
181 item_margin.main_start + self.item.border.main_start + self.item.padding.main_start;
182 let item_content_main_start_position = *main_position_cursor;
183
184 *main_position_cursor += item_used_size.main +
185 self.item.padding.main_end +
186 self.item.border.main_end +
187 item_margin.main_end +
188 item_main_interval;
189
190 let item_content_cross_start_position = self.item.align_along_cross_axis(
192 &item_margin,
193 &item_used_size.cross,
194 final_line_cross_size,
195 self.layout_result
196 .flex_alignment_baseline_relative_to_margin_box
197 .unwrap_or_default(),
198 shared_alignment_baseline.unwrap_or_default(),
199 flex_context.config.flex_wrap_is_reversed,
200 );
201
202 let start_corner = FlexRelativeVec2 {
203 main: item_content_main_start_position,
204 cross: item_content_cross_start_position,
205 };
206
207 let final_line_size = FlexRelativeVec2 {
209 main: initial_flex_layout.line_size.main,
210 cross: final_line_cross_size,
211 };
212 let content_rect = flex_context.rect_to_flow_relative(
213 final_line_size,
214 FlexRelativeRect {
215 start_corner,
216 size: item_used_size,
217 },
218 );
219
220 let adjust_baseline = |baseline: Au| {
221 baseline + item_content_cross_start_position -
222 self.item.border.cross_start -
223 self.item.padding.cross_start -
224 item_margin.cross_start
225 };
226
227 let baselines = self
228 .layout_result
229 .content_baselines_for_parent_relative_to_margin_box;
230 if flex_context.config.flex_direction_is_reversed {
231 if let Some(last_baseline) = baselines.last {
232 all_baselines
233 .last
234 .get_or_insert_with(|| adjust_baseline(last_baseline));
235 }
236 if let Some(first_baseline) = baselines.first {
237 all_baselines.first = Some(adjust_baseline(first_baseline));
238 }
239 } else {
240 if let Some(first_baseline) = baselines.first {
241 all_baselines
242 .first
243 .get_or_insert_with(|| adjust_baseline(first_baseline));
244 }
245 if let Some(last_baseline) = baselines.last {
246 all_baselines.last = Some(adjust_baseline(last_baseline));
247 }
248 }
249
250 let mut fragment_info = self.item.box_.base_fragment_info();
251 fragment_info
252 .flags
253 .insert(FragmentFlags::IS_FLEX_OR_GRID_ITEM);
254 if self.item.depends_on_block_constraints {
255 fragment_info.flags.insert(
256 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
257 );
258 }
259 let flags = fragment_info.flags;
260
261 let containing_block = flex_context.containing_block;
262 let container_writing_mode = containing_block.style.writing_mode;
263 let style = self.item.box_.style();
264
265 let mut fragment = BoxFragment::new(
266 fragment_info,
267 style.clone(),
268 self.layout_result.fragments,
269 content_rect.as_physical(Some(flex_context.containing_block)),
270 flex_context
271 .sides_to_flow_relative(self.item.padding)
272 .to_physical(container_writing_mode),
273 flex_context
274 .sides_to_flow_relative(self.item.border)
275 .to_physical(container_writing_mode),
276 flex_context
277 .sides_to_flow_relative(item_margin)
278 .to_physical(container_writing_mode),
279 self.layout_result.specific_layout_info,
280 );
281
282 if style.establishes_containing_block_for_absolute_descendants(flags) {
286 self.layout_result
287 .positioning_context
288 .layout_collected_children(flex_context.layout_context, &mut fragment);
289 }
290
291 if style.clone_position() == Position::Relative {
292 fragment.base.translate_rect(
293 relative_adjustement(style, containing_block)
294 .to_physical_size(containing_block.style.writing_mode),
295 );
296 }
297
298 let fragment = Arc::new(fragment);
299 self.item
300 .box_
301 .independent_formatting_context
302 .base
303 .set_fragment(Fragment::Box(fragment.clone()));
304 (fragment, self.layout_result.positioning_context)
305 }
306}
307
308struct FinalFlexLineLayout {
311 cross_size: Au,
313 item_fragments: Vec<(Arc<BoxFragment>, PositioningContext)>,
316 shared_alignment_baseline: Option<Au>,
319 all_baselines: Baselines,
324}
325
326impl FlexContainerConfig {
327 fn resolve_reversable_flex_alignment(
328 &self,
329 align_flags: AlignFlags,
330 reversed: bool,
331 ) -> AlignFlags {
332 match (align_flags.value(), reversed) {
333 (AlignFlags::FLEX_START, false) => AlignFlags::START | align_flags.flags(),
334 (AlignFlags::FLEX_START, true) => AlignFlags::END | align_flags.flags(),
335 (AlignFlags::FLEX_END, false) => AlignFlags::END | align_flags.flags(),
336 (AlignFlags::FLEX_END, true) => AlignFlags::START | align_flags.flags(),
337 (_, _) => align_flags,
338 }
339 }
340
341 fn resolve_align_self_for_child(&self, child_style: &ComputedValues) -> AlignFlags {
342 self.resolve_reversable_flex_alignment(
343 child_style
344 .resolve_align_self(self.align_items, AlignFlags::STRETCH)
345 .0,
346 self.flex_wrap_is_reversed,
347 )
348 }
349
350 fn resolve_justify_content_for_child(&self) -> AlignFlags {
351 self.resolve_reversable_flex_alignment(
352 self.justify_content.primary(),
353 self.flex_direction_is_reversed,
354 )
355 }
356
357 fn sides_to_flex_relative<T>(&self, sides: LogicalSides<T>) -> FlexRelativeSides<T> {
358 self.main_start_cross_start_sides_are
359 .sides_to_flex_relative(sides)
360 }
361
362 fn sides_to_flow_relative<T>(&self, sides: FlexRelativeSides<T>) -> LogicalSides<T> {
363 self.main_start_cross_start_sides_are
364 .sides_to_flow_relative(sides)
365 }
366}
367
368impl FlexContext<'_> {
369 #[inline]
370 fn sides_to_flow_relative<T>(&self, x: FlexRelativeSides<T>) -> LogicalSides<T> {
371 self.config.sides_to_flow_relative(x)
372 }
373
374 #[inline]
375 fn rect_to_flow_relative(
376 &self,
377 base_rect_size: FlexRelativeVec2<Au>,
378 rect: FlexRelativeRect<Au>,
379 ) -> LogicalRect<Au> {
380 super::geom::rect_to_flow_relative(
381 self.config.flex_axis,
382 self.config.main_start_cross_start_sides_are,
383 base_rect_size,
384 rect,
385 )
386 }
387}
388
389#[derive(Debug, Default)]
390struct DesiredFlexFractionAndGrowOrShrinkFactor {
391 desired_flex_fraction: f32,
392 flex_grow_or_shrink_factor: f32,
393}
394
395#[derive(Default)]
396struct FlexItemBoxInlineContentSizesInfo {
397 outer_flex_base_size: Au,
398 outer_min_main_size: Au,
399 outer_max_main_size: Option<Au>,
400 min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
401 max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
402 min_content_main_size_for_multiline_container: Au,
403 depends_on_block_constraints: bool,
404}
405
406impl ComputeInlineContentSizes for FlexContainer {
407 #[servo_tracing::instrument(name = "FlexContainer::compute_inline_content_sizes", skip_all)]
408 fn compute_inline_content_sizes(
409 &self,
410 layout_context: &LayoutContext,
411 constraint_space: &ConstraintSpace,
412 ) -> InlineContentSizesResult {
413 match self.config.flex_axis {
414 FlexAxis::Row => {
415 self.main_content_sizes(layout_context, &constraint_space.into(), || {
416 unreachable!(
417 "Unexpected FlexContext query during row flex intrinsic size calculation."
418 )
419 })
420 },
421 FlexAxis::Column => self.cross_content_sizes(layout_context, &constraint_space.into()),
422 }
423 }
424}
425
426impl FlexContainer {
427 fn cross_content_sizes(
428 &self,
429 layout_context: &LayoutContext,
430 containing_block_for_children: &IndefiniteContainingBlock,
431 ) -> InlineContentSizesResult {
432 assert_eq!(
434 self.config.flex_axis,
435 FlexAxis::Column,
436 "The cross axis should be the inline one"
437 );
438 let mut sizes = ContentSizes::zero();
439 let mut depends_on_block_constraints = false;
440 for kid in self.children.iter() {
441 let kid = &*kid.borrow();
442 match kid {
443 FlexLevelBox::FlexItem(item) => {
444 let ifc = &item.independent_formatting_context;
448 let result = ifc.outer_inline_content_sizes(
449 layout_context,
450 containing_block_for_children,
451 &LogicalVec2::zero(),
452 false, );
454 sizes.max_assign(result.sizes);
455 depends_on_block_constraints |= result.depends_on_block_constraints;
456 },
457 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
458 }
459 }
460 InlineContentSizesResult {
461 sizes,
462 depends_on_block_constraints,
463 }
464 }
465
466 fn main_content_sizes<'a>(
467 &self,
468 layout_context: &LayoutContext,
469 containing_block_for_children: &IndefiniteContainingBlock,
470 flex_context_getter: impl Fn() -> &'a FlexContext<'a>,
471 ) -> InlineContentSizesResult {
472 let mut chosen_max_flex_fraction = f32::NEG_INFINITY;
481 let mut chosen_min_flex_fraction = f32::NEG_INFINITY;
482 let mut sum_of_flex_grow_factors = 0.0;
483 let mut sum_of_flex_shrink_factors = 0.0;
484 let mut item_infos = vec![];
485
486 for kid in self.children.iter() {
487 let kid = &*kid.borrow();
488 match kid {
489 FlexLevelBox::FlexItem(item) => {
490 sum_of_flex_grow_factors += item.style().get_position().flex_grow.0;
491 sum_of_flex_shrink_factors += item.style().get_position().flex_shrink.0;
492
493 let info = item.main_content_size_info(
494 layout_context,
495 containing_block_for_children,
496 &self.config,
497 &flex_context_getter,
498 );
499
500 chosen_max_flex_fraction =
505 chosen_max_flex_fraction.max(info.max_flex_factors.desired_flex_fraction);
506 chosen_min_flex_fraction =
507 chosen_min_flex_fraction.max(info.min_flex_factors.desired_flex_fraction);
508
509 item_infos.push(info)
510 },
511 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
512 }
513 }
514
515 let normalize_flex_fraction = |chosen_flex_fraction| {
516 if chosen_flex_fraction > 0.0 && sum_of_flex_grow_factors < 1.0 {
517 chosen_flex_fraction / sum_of_flex_grow_factors
521 } else if chosen_flex_fraction < 0.0 && sum_of_flex_shrink_factors < 1.0 {
522 chosen_flex_fraction * sum_of_flex_shrink_factors
526 } else {
527 chosen_flex_fraction
528 }
529 };
530
531 let chosen_min_flex_fraction = normalize_flex_fraction(chosen_min_flex_fraction);
532 let chosen_max_flex_fraction = normalize_flex_fraction(chosen_max_flex_fraction);
533
534 let main_gap = match self.config.flex_axis {
535 FlexAxis::Row => self.style.clone_column_gap(),
536 FlexAxis::Column => self.style.clone_row_gap(),
537 };
538 let main_gap = match main_gap {
539 LengthPercentageOrNormal::LengthPercentage(length_percentage) => {
540 length_percentage.to_used_value(Au::zero())
541 },
542 LengthPercentageOrNormal::Normal => Au::zero(),
543 };
544 let extra_space_from_main_gap = main_gap * (item_infos.len() as i32 - 1);
545 let mut container_max_content_size = extra_space_from_main_gap;
546 let mut container_min_content_size = if self.config.flex_wrap == FlexWrap::Nowrap {
547 extra_space_from_main_gap
548 } else {
549 Au::zero()
550 };
551 let mut container_depends_on_block_constraints = false;
552
553 for FlexItemBoxInlineContentSizesInfo {
554 outer_flex_base_size,
555 outer_min_main_size,
556 outer_max_main_size,
557 min_flex_factors,
558 max_flex_factors,
559 min_content_main_size_for_multiline_container,
560 depends_on_block_constraints,
561 } in item_infos.iter()
562 {
563 container_max_content_size += (*outer_flex_base_size +
569 Au::from_f32_px(
570 max_flex_factors.flex_grow_or_shrink_factor * chosen_max_flex_fraction,
571 ))
572 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
573
574 if self.config.flex_wrap == FlexWrap::Nowrap {
585 container_min_content_size += (*outer_flex_base_size +
586 Au::from_f32_px(
587 min_flex_factors.flex_grow_or_shrink_factor * chosen_min_flex_fraction,
588 ))
589 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
590 } else {
591 container_min_content_size
592 .max_assign(*min_content_main_size_for_multiline_container);
593 }
594
595 container_depends_on_block_constraints |= depends_on_block_constraints;
596 }
597
598 InlineContentSizesResult {
599 sizes: ContentSizes {
600 min_content: container_min_content_size,
601 max_content: container_max_content_size,
602 },
603 depends_on_block_constraints: container_depends_on_block_constraints,
604 }
605 }
606
607 #[servo_tracing::instrument(
609 name = "FlexContainer::layout",
610 skip_all,
611 fields(self_address = self as *const _ as usize)
612 )]
613 pub(crate) fn layout(
614 &self,
615 layout_context: &LayoutContext,
616 positioning_context: &mut PositioningContext,
617 containing_block: &ContainingBlock,
618 lazy_block_size: &LazySize,
619 ) -> IndependentFormattingContextLayoutResult {
620 let mut flex_context = FlexContext {
621 config: self.config.clone(),
622 layout_context,
623 containing_block,
624 container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative(
626 LogicalVec2 {
627 inline: SizeConstraint::Definite(containing_block.size.inline),
628 block: containing_block.size.block,
629 },
630 ),
631 };
632
633 let container_main_size = match self.config.flex_axis {
636 FlexAxis::Row => containing_block.size.inline,
637 FlexAxis::Column => lazy_block_size.resolve(|| {
638 let mut containing_block = IndefiniteContainingBlock::from(containing_block);
639 containing_block.size.block = None;
640 self.main_content_sizes(layout_context, &containing_block, || &flex_context)
641 .sizes
642 .max_content
643 }),
644 };
645
646 let mut flex_items = Vec::with_capacity(self.children.len());
648
649 let absolutely_positioned_items_with_original_order = self
657 .children
658 .iter()
659 .map(|arcrefcell| {
660 let borrowed = arcrefcell.borrow();
661 match &*borrowed {
662 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned) => {
663 FlexContent::AbsolutelyPositionedBox(absolutely_positioned.clone())
664 },
665 FlexLevelBox::FlexItem(_) => {
666 let item = AtomicRef::map(borrowed, |child| match child {
667 FlexLevelBox::FlexItem(item) => item,
668 _ => unreachable!(),
669 });
670 flex_items.push(item);
671 FlexContent::FlexItemPlaceholder
672 },
673 }
674 })
675 .collect::<Vec<_>>();
676
677 let flex_item_boxes = flex_items.iter().map(|child| &**child);
678 let flex_items = flex_item_boxes
679 .map(|flex_item_box| FlexItem::new(&flex_context, flex_item_box))
680 .collect::<Vec<_>>();
681
682 let row_gap = self.style.clone_row_gap();
683 let column_gap = self.style.clone_column_gap();
684 let (cross_gap, main_gap) = match flex_context.config.flex_axis {
685 FlexAxis::Row => (row_gap, column_gap),
686 FlexAxis::Column => (column_gap, row_gap),
687 };
688 let cross_gap = match cross_gap {
689 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
690 .maybe_to_used_value(
691 flex_context
692 .container_inner_size_constraint
693 .cross
694 .to_definite(),
695 )
696 .unwrap_or_default(),
697 LengthPercentageOrNormal::Normal => Au::zero(),
698 };
699 let main_gap = match main_gap {
700 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
701 .maybe_to_used_value(
702 flex_context
703 .container_inner_size_constraint
704 .main
705 .to_definite(),
706 )
707 .unwrap_or_default(),
708 LengthPercentageOrNormal::Normal => Au::zero(),
709 };
710
711 let initial_line_layouts = do_initial_flex_line_layout(
714 &mut flex_context,
715 container_main_size,
716 flex_items,
717 main_gap,
718 );
719
720 let line_count = initial_line_layouts.len();
721 let content_cross_size = initial_line_layouts
722 .iter()
723 .map(|layout| layout.line_size.cross)
724 .sum::<Au>() +
725 cross_gap * (line_count as i32 - 1);
726 let content_block_size = match self.config.flex_axis {
727 FlexAxis::Row => content_cross_size,
728 FlexAxis::Column => container_main_size,
729 };
730
731 let container_cross_size = match self.config.flex_axis {
733 FlexAxis::Row => lazy_block_size.resolve(|| content_cross_size),
734 FlexAxis::Column => containing_block.size.inline,
735 };
736
737 let container_size = FlexRelativeVec2 {
738 main: container_main_size,
739 cross: container_cross_size,
740 };
741
742 let mut remaining_free_cross_space = container_cross_size - content_cross_size;
743
744 let num_lines = initial_line_layouts.len();
749 let resolved_align_content: AlignFlags = {
750 let align_content_style = flex_context.config.align_content.primary();
752 let mut is_safe = align_content_style.flags() == AlignFlags::SAFE;
753
754 let mut resolved_align_content = match align_content_style.value() {
757 AlignFlags::NORMAL => AlignFlags::STRETCH,
758 align_content => align_content,
759 };
760
761 let fallback_is_needed = match resolved_align_content {
769 _ if remaining_free_cross_space <= Au::zero() => true,
770 AlignFlags::STRETCH => num_lines < 1,
771 AlignFlags::SPACE_BETWEEN | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
772 num_lines < 2
773 },
774 _ => false,
775 };
776
777 if fallback_is_needed {
778 (resolved_align_content, is_safe) = match resolved_align_content {
779 AlignFlags::STRETCH => (AlignFlags::FLEX_START, false),
780 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
781 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
782 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
783 _ => (resolved_align_content, is_safe),
784 }
785 };
786
787 if remaining_free_cross_space <= Au::zero() && is_safe {
789 resolved_align_content = AlignFlags::START;
790 }
791
792 resolved_align_content
793 };
794
795 let flex_wrap_is_reversed = flex_context.config.flex_wrap_is_reversed;
797 let resolved_align_content = self
798 .config
799 .resolve_reversable_flex_alignment(resolved_align_content, flex_wrap_is_reversed);
800 let mut cross_start_position_cursor = match resolved_align_content {
801 AlignFlags::START if flex_wrap_is_reversed => remaining_free_cross_space,
802 AlignFlags::START => Au::zero(),
803 AlignFlags::END if flex_wrap_is_reversed => Au::zero(),
804 AlignFlags::END => remaining_free_cross_space,
805 AlignFlags::CENTER => remaining_free_cross_space / 2,
806 AlignFlags::STRETCH => Au::zero(),
807 AlignFlags::SPACE_BETWEEN => Au::zero(),
808 AlignFlags::SPACE_AROUND => remaining_free_cross_space / num_lines as i32 / 2,
809 AlignFlags::SPACE_EVENLY => remaining_free_cross_space / (num_lines as i32 + 1),
810
811 _ => Au::zero(),
813 };
814
815 let inline_axis_is_main_axis = self.config.flex_axis == FlexAxis::Row;
816 let mut baseline_alignment_participating_baselines = Baselines::default();
817 let mut all_baselines = Baselines::default();
818 let flex_item_fragments: Vec<_> = initial_line_layouts
819 .into_iter()
820 .enumerate()
821 .flat_map(|(index, initial_line_layout)| {
822 let (space_to_add_to_line, space_to_add_after_line) =
826 allocate_free_cross_space_for_flex_line(
827 resolved_align_content,
828 remaining_free_cross_space,
829 (num_lines - index) as i32,
830 );
831 remaining_free_cross_space -= space_to_add_to_line + space_to_add_after_line;
832
833 let final_line_cross_size =
834 initial_line_layout.line_size.cross + space_to_add_to_line;
835 let mut final_line_layout = initial_line_layout.finish_with_final_cross_size(
836 &mut flex_context,
837 main_gap,
838 final_line_cross_size,
839 );
840
841 let line_cross_start_position = cross_start_position_cursor;
842 cross_start_position_cursor = line_cross_start_position +
843 final_line_cross_size +
844 space_to_add_after_line +
845 cross_gap;
846
847 let flow_relative_line_position =
848 match (self.config.flex_axis, flex_wrap_is_reversed) {
849 (FlexAxis::Row, false) => LogicalVec2 {
850 block: line_cross_start_position,
851 inline: Au::zero(),
852 },
853 (FlexAxis::Row, true) => LogicalVec2 {
854 block: container_cross_size -
855 line_cross_start_position -
856 final_line_layout.cross_size,
857 inline: Au::zero(),
858 },
859 (FlexAxis::Column, false) => LogicalVec2 {
860 block: Au::zero(),
861 inline: line_cross_start_position,
862 },
863 (FlexAxis::Column, true) => LogicalVec2 {
864 block: Au::zero(),
865 inline: container_cross_size -
866 line_cross_start_position -
867 final_line_cross_size,
868 },
869 };
870
871 if inline_axis_is_main_axis {
872 let line_shared_alignment_baseline = final_line_layout
873 .shared_alignment_baseline
874 .map(|baseline| baseline + flow_relative_line_position.block);
875 if index == 0 {
876 baseline_alignment_participating_baselines.first =
877 line_shared_alignment_baseline;
878 }
879 if index == num_lines - 1 {
880 baseline_alignment_participating_baselines.last =
881 line_shared_alignment_baseline;
882 }
883 }
884
885 let line_all_baselines = final_line_layout
886 .all_baselines
887 .offset(flow_relative_line_position.block);
888 if index == 0 {
889 all_baselines.first = line_all_baselines.first;
890 }
891 if index == num_lines - 1 {
892 all_baselines.last = line_all_baselines.last;
893 }
894
895 let physical_line_position =
896 flow_relative_line_position.to_physical_size(self.style.writing_mode);
897 for (fragment, _) in &mut final_line_layout.item_fragments {
898 fragment.base.translate_rect(physical_line_position);
899 }
900 final_line_layout.item_fragments
901 })
902 .collect();
903
904 let mut flex_item_fragments = flex_item_fragments.into_iter();
905 let fragments = absolutely_positioned_items_with_original_order
906 .into_iter()
907 .map(|child_as_abspos| match child_as_abspos {
908 FlexContent::AbsolutelyPositionedBox(absolutely_positioned_box) => self
909 .create_absolutely_positioned_flex_child_fragment(
910 absolutely_positioned_box,
911 containing_block,
912 container_size,
913 positioning_context,
914 ),
915 FlexContent::FlexItemPlaceholder => {
916 let (fragment, mut child_positioning_context) =
919 flex_item_fragments.next().unwrap();
920 let fragment = Fragment::Box(fragment);
921 child_positioning_context.adjust_static_position_of_hoisted_fragments(
922 &fragment,
923 PositioningContextLength::zero(),
924 );
925 positioning_context.append(child_positioning_context);
926 fragment
927 },
928 })
929 .collect::<Vec<_>>();
930
931 assert!(flex_item_fragments.next().is_none());
933
934 let baselines = Baselines {
935 first: baseline_alignment_participating_baselines
936 .first
937 .or(all_baselines.first),
938 last: baseline_alignment_participating_baselines
939 .last
940 .or(all_baselines.last),
941 };
942
943 let depends_on_block_constraints = true;
956
957 IndependentFormattingContextLayoutResult {
958 fragments,
959 content_block_size,
960 content_inline_size_for_table: None,
961 baselines,
962 depends_on_block_constraints,
963 specific_layout_info: None,
964 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
965 }
966 }
967
968 fn create_absolutely_positioned_flex_child_fragment(
984 &self,
985 absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
986 containing_block: &ContainingBlock,
987 container_size: FlexRelativeVec2<Au>,
988 positioning_context: &mut PositioningContext,
989 ) -> Fragment {
990 let alignment = {
991 let fragment = absolutely_positioned_box.borrow();
992 let make_flex_only_values_directional_for_absolutes =
993 |value: AlignFlags, reversed: bool| match (value.value(), reversed) {
994 (AlignFlags::NORMAL | AlignFlags::AUTO | AlignFlags::STRETCH, true) => {
995 AlignFlags::END | AlignFlags::SAFE
996 },
997 (AlignFlags::STRETCH, false) => AlignFlags::START | AlignFlags::SAFE,
998 (AlignFlags::SPACE_BETWEEN, false) => AlignFlags::START | AlignFlags::SAFE,
999 (AlignFlags::SPACE_BETWEEN, true) => AlignFlags::END | AlignFlags::SAFE,
1000 _ => value,
1001 };
1002 let cross = make_flex_only_values_directional_for_absolutes(
1003 self.config
1004 .resolve_align_self_for_child(fragment.context.style()),
1005 self.config.flex_wrap_is_reversed,
1006 );
1007 let main = make_flex_only_values_directional_for_absolutes(
1008 self.config.resolve_justify_content_for_child(),
1009 self.config.flex_direction_is_reversed,
1010 );
1011
1012 FlexRelativeVec2 { cross, main }
1013 };
1014 let logical_alignment = self.config.flex_axis.vec2_to_flow_relative(alignment);
1015
1016 let static_position_rect = LogicalRect {
1017 start_corner: LogicalVec2::zero(),
1018 size: self.config.flex_axis.vec2_to_flow_relative(container_size),
1019 }
1020 .as_physical(Some(containing_block));
1021
1022 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
1023 absolutely_positioned_box,
1024 static_position_rect,
1025 logical_alignment,
1026 self.config.writing_mode,
1027 );
1028 let hoisted_fragment = hoisted_box.fragment.clone();
1029 positioning_context.push(hoisted_box);
1030 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
1031 }
1032
1033 #[inline]
1034 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
1035 LayoutStyle::Default(&self.style)
1036 }
1037
1038 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
1039 for child in &self.children {
1040 child.borrow_mut().with_base_mut(|base| {
1041 base.parent_box.replace(layout_box.clone());
1042 });
1043 }
1044 }
1045}
1046
1047fn allocate_free_cross_space_for_flex_line(
1051 resolved_align_content: AlignFlags,
1052 remaining_free_cross_space: Au,
1053 remaining_line_count: i32,
1054) -> (Au, Au) {
1055 if remaining_free_cross_space.is_zero() {
1056 return (Au::zero(), Au::zero());
1057 }
1058
1059 match resolved_align_content {
1060 AlignFlags::START => (Au::zero(), Au::zero()),
1061 AlignFlags::FLEX_START => (Au::zero(), Au::zero()),
1062 AlignFlags::END => (Au::zero(), Au::zero()),
1063 AlignFlags::FLEX_END => (Au::zero(), Au::zero()),
1064 AlignFlags::CENTER => (Au::zero(), Au::zero()),
1065 AlignFlags::STRETCH => (
1066 remaining_free_cross_space / remaining_line_count,
1067 Au::zero(),
1068 ),
1069 AlignFlags::SPACE_BETWEEN => {
1070 if remaining_line_count > 1 {
1071 (
1072 Au::zero(),
1073 remaining_free_cross_space / (remaining_line_count - 1),
1074 )
1075 } else {
1076 (Au::zero(), Au::zero())
1077 }
1078 },
1079 AlignFlags::SPACE_AROUND => (
1080 Au::zero(),
1081 remaining_free_cross_space / remaining_line_count,
1082 ),
1083 AlignFlags::SPACE_EVENLY => (
1084 Au::zero(),
1085 remaining_free_cross_space / (remaining_line_count + 1),
1086 ),
1087
1088 _ => (Au::zero(), Au::zero()),
1090 }
1091}
1092
1093impl<'a> FlexItem<'a> {
1094 fn new(flex_context: &FlexContext, box_: &'a FlexItemBox) -> Self {
1095 let containing_block = IndefiniteContainingBlock::from(flex_context.containing_block);
1096 let content_box_sizes_and_pbm = box_
1097 .independent_formatting_context
1098 .layout_style()
1099 .content_box_sizes_and_padding_border_margin(&containing_block);
1100 box_.to_flex_item(
1101 flex_context.layout_context,
1102 &containing_block,
1103 &content_box_sizes_and_pbm,
1104 &flex_context.config,
1105 &|| flex_context,
1106 )
1107 }
1108}
1109
1110fn cross_axis_is_item_block_axis(
1111 container_is_horizontal: bool,
1112 item_is_horizontal: bool,
1113 flex_axis: FlexAxis,
1114) -> bool {
1115 let item_is_orthogonal = item_is_horizontal != container_is_horizontal;
1116 let container_is_row = flex_axis == FlexAxis::Row;
1117
1118 container_is_row ^ item_is_orthogonal
1119}
1120
1121fn item_with_auto_cross_size_stretches_to_line_size(
1125 align_self: AlignItems,
1126 margin: &FlexRelativeSides<AuOrAuto>,
1127) -> bool {
1128 align_self.0.value() == AlignFlags::STRETCH &&
1129 !margin.cross_start.is_auto() &&
1130 !margin.cross_end.is_auto()
1131}
1132
1133fn do_initial_flex_line_layout<'items>(
1136 flex_context: &mut FlexContext,
1137 container_main_size: Au,
1138 mut items: Vec<FlexItem<'items>>,
1139 main_gap: Au,
1140) -> Vec<InitialFlexLineLayout<'items>> {
1141 let construct_line = |(items, outer_hypothetical_main_size)| {
1142 InitialFlexLineLayout::new(
1143 flex_context,
1144 items,
1145 outer_hypothetical_main_size,
1146 container_main_size,
1147 main_gap,
1148 )
1149 };
1150
1151 if flex_context.config.container_is_single_line {
1152 let outer_hypothetical_main_sizes_sum = items
1153 .iter()
1154 .map(|item| item.hypothetical_main_size + item.pbm_auto_is_zero.main)
1155 .sum();
1156 return vec![construct_line((items, outer_hypothetical_main_sizes_sum))];
1157 }
1158
1159 let mut lines = Vec::new();
1160 let mut line_size_so_far = Au::zero();
1161 let mut line_so_far_is_empty = true;
1162 let mut index = 0;
1163
1164 while let Some(item) = items.get(index) {
1165 let item_size = item.hypothetical_main_size + item.pbm_auto_is_zero.main;
1166 let mut line_size_would_be = line_size_so_far + item_size;
1167 if !line_so_far_is_empty {
1168 line_size_would_be += main_gap;
1169 }
1170 let item_fits = line_size_would_be <= container_main_size;
1171 if item_fits || line_so_far_is_empty {
1172 line_size_so_far = line_size_would_be;
1173 line_so_far_is_empty = false;
1174 index += 1;
1175 continue;
1176 }
1177
1178 let remaining = items.split_off(index);
1180 lines.push((items, line_size_so_far));
1181 items = remaining;
1182
1183 line_size_so_far = item_size;
1185 index = 1;
1186 }
1187
1188 lines.push((items, line_size_so_far));
1190
1191 if flex_context.layout_context.use_rayon {
1192 lines.par_drain(..).map(construct_line).collect()
1193 } else {
1194 lines.into_iter().map(construct_line).collect()
1195 }
1196}
1197
1198struct InitialFlexLineLayout<'a> {
1202 items: Vec<FlexLineItem<'a>>,
1204
1205 line_size: FlexRelativeVec2<Au>,
1207
1208 free_space_in_main_axis: Au,
1210}
1211
1212impl InitialFlexLineLayout<'_> {
1213 fn new<'items>(
1214 flex_context: &FlexContext,
1215 items: Vec<FlexItem<'items>>,
1216 outer_hypothetical_main_sizes_sum: Au,
1217 container_main_size: Au,
1218 main_gap: Au,
1219 ) -> InitialFlexLineLayout<'items> {
1220 let item_count = items.len();
1221 let (item_used_main_sizes, free_space_in_main_axis) = Self::resolve_flexible_lengths(
1222 &items,
1223 outer_hypothetical_main_sizes_sum,
1224 container_main_size - main_gap * (item_count as i32 - 1),
1225 );
1226
1227 let layout_results: Vec<_> = if flex_context.layout_context.use_rayon {
1229 items
1230 .par_iter()
1231 .zip(&item_used_main_sizes)
1232 .map(|(item, used_main_size)| item.layout(*used_main_size, flex_context, None))
1233 .collect()
1234 } else {
1235 items
1236 .iter()
1237 .zip(&item_used_main_sizes)
1238 .map(|(item, used_main_size)| item.layout(*used_main_size, flex_context, None))
1239 .collect()
1240 };
1241
1242 let items: Vec<_> = izip!(
1243 items.into_iter(),
1244 layout_results.into_iter(),
1245 item_used_main_sizes.into_iter()
1246 )
1247 .map(|(item, layout_result, used_main_size)| FlexLineItem {
1248 item,
1249 layout_result,
1250 used_main_size,
1251 })
1252 .collect();
1253
1254 let line_cross_size = Self::cross_size(&items, flex_context);
1256 let line_size = FlexRelativeVec2 {
1257 main: container_main_size,
1258 cross: line_cross_size,
1259 };
1260
1261 InitialFlexLineLayout {
1262 items,
1263 line_size,
1264 free_space_in_main_axis,
1265 }
1266 }
1267
1268 fn resolve_flexible_lengths<'items>(
1271 items: &'items [FlexItem<'items>],
1272 outer_hypothetical_main_sizes_sum: Au,
1273 container_main_size: Au,
1274 ) -> (Vec<Au>, Au) {
1275 struct FlexibleLengthResolutionItem<'items> {
1276 item: &'items FlexItem<'items>,
1277 frozen: Cell<bool>,
1278 target_main_size: Cell<Au>,
1279 flex_factor: f32,
1280 min_max_violation_kind: Cell<Ordering>,
1281 }
1282
1283 let grow = outer_hypothetical_main_sizes_sum < container_main_size;
1288
1289 let mut frozen_count = 0;
1290 let items: Vec<_> = items
1291 .iter()
1292 .map(|item| {
1293 let target_main_size = Cell::new(item.flex_base_size);
1296
1297 let flex_factor = if grow {
1304 item.box_.style().get_position().flex_grow.0
1305 } else {
1306 item.box_.style().get_position().flex_shrink.0
1307 };
1308
1309 let is_inflexible = flex_factor == 0. ||
1310 if grow {
1311 item.flex_base_size > item.hypothetical_main_size
1312 } else {
1313 item.flex_base_size < item.hypothetical_main_size
1314 };
1315
1316 let frozen = Cell::new(false);
1317 if is_inflexible {
1318 frozen_count += 1;
1319 frozen.set(true);
1320 target_main_size.set(item.hypothetical_main_size);
1321 }
1322
1323 FlexibleLengthResolutionItem {
1324 item,
1325 frozen,
1326 target_main_size,
1327 flex_factor,
1328 min_max_violation_kind: Cell::new(Ordering::Equal),
1330 }
1331 })
1332 .collect();
1333
1334 let unfrozen_items = || items.iter().filter(|item| !item.frozen.get());
1335 let main_sizes = |items: Vec<FlexibleLengthResolutionItem>| {
1336 items
1337 .into_iter()
1338 .map(|item| item.target_main_size.get())
1339 .collect()
1340 };
1341
1342 let free_space = |all_items_frozen| {
1347 let items_size = items
1348 .iter()
1349 .map(|item| {
1350 item.item.pbm_auto_is_zero.main +
1351 if all_items_frozen || item.frozen.get() {
1352 item.target_main_size.get()
1353 } else {
1354 item.item.flex_base_size
1355 }
1356 })
1357 .sum();
1358 container_main_size - items_size
1359 };
1360
1361 let initial_free_space = free_space(false);
1362 loop {
1363 let mut remaining_free_space = free_space(false);
1365 if frozen_count >= items.len() {
1368 return (main_sizes(items), remaining_free_space);
1369 }
1370
1371 let unfrozen_items_flex_factor_sum =
1377 unfrozen_items().map(|item| item.flex_factor).sum();
1378 if unfrozen_items_flex_factor_sum < 1. {
1379 let multiplied = initial_free_space.scale_by(unfrozen_items_flex_factor_sum);
1380 if multiplied.abs() < remaining_free_space.abs() {
1381 remaining_free_space = multiplied
1382 }
1383 }
1384
1385 if !remaining_free_space.is_zero() {
1391 if grow {
1397 for item in unfrozen_items() {
1398 let ratio = item.flex_factor / unfrozen_items_flex_factor_sum;
1399 item.target_main_size
1400 .set(item.item.flex_base_size + remaining_free_space.scale_by(ratio));
1401 }
1402 } else {
1411 let scaled_shrink_factor = |item: &FlexibleLengthResolutionItem| {
1413 item.item.flex_base_size.scale_by(item.flex_factor)
1414 };
1415 let scaled_shrink_factors_sum: Au =
1416 unfrozen_items().map(scaled_shrink_factor).sum();
1417 if scaled_shrink_factors_sum > Au::zero() {
1418 for item in unfrozen_items() {
1419 let ratio = scaled_shrink_factor(item).0 as f32 /
1420 scaled_shrink_factors_sum.0 as f32;
1421 item.target_main_size.set(
1422 item.item.flex_base_size -
1423 remaining_free_space.abs().scale_by(ratio),
1424 );
1425 }
1426 }
1427 }
1428 }
1429
1430 let mut total_violation = Au::zero();
1436 for item in unfrozen_items() {
1437 let unclamped = item.target_main_size.get();
1438 let clamped = unclamped.clamp_between_extremums(
1439 item.item.content_min_main_size,
1440 item.item.content_max_main_size,
1441 );
1442 item.target_main_size.set(clamped);
1443 item.min_max_violation_kind.set(clamped.cmp(&unclamped));
1446 total_violation += clamped - unclamped;
1447 }
1448
1449 match total_violation.cmp(&Au::zero()) {
1456 Ordering::Equal => {
1457 let remaining_free_space = free_space(true);
1460 return (main_sizes(items), remaining_free_space);
1461 },
1462 total_violation_kind => {
1463 for item in unfrozen_items() {
1464 if item.min_max_violation_kind.get() == total_violation_kind {
1465 item.frozen.set(true);
1466 frozen_count += 1;
1467 }
1468 }
1469 },
1470 }
1471 }
1472 }
1473
1474 fn cross_size<'items>(items: &'items [FlexLineItem<'items>], flex_context: &FlexContext) -> Au {
1476 if flex_context.config.container_is_single_line &&
1477 let SizeConstraint::Definite(size) =
1478 flex_context.container_inner_size_constraint.cross
1479 {
1480 return size;
1481 }
1482
1483 let mut max_ascent = Au::zero();
1484 let mut max_descent = Au::zero();
1485 let mut max_outer_hypothetical_cross_size = Au::zero();
1486 for item in items.iter() {
1487 if matches!(
1489 item.item.align_self.0.value(),
1490 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1491 ) {
1492 let baseline = item.get_or_synthesize_baseline_with_cross_size(
1493 item.layout_result.hypothetical_cross_size,
1494 );
1495 let hypothetical_margin_box_cross_size =
1496 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross;
1497 max_ascent = max_ascent.max(baseline);
1498 max_descent = max_descent.max(hypothetical_margin_box_cross_size - baseline);
1499 } else {
1500 max_outer_hypothetical_cross_size = max_outer_hypothetical_cross_size.max(
1501 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross,
1502 );
1503 }
1504 }
1505
1506 let largest = max_outer_hypothetical_cross_size.max(max_ascent + max_descent);
1508 match flex_context.container_inner_size_constraint.cross {
1509 SizeConstraint::MinMax(min, max) if flex_context.config.container_is_single_line => {
1510 largest.clamp_between_extremums(min, max)
1511 },
1512 _ => largest,
1513 }
1514 }
1515
1516 fn finish_with_final_cross_size(
1517 mut self,
1518 flex_context: &mut FlexContext,
1519 main_gap: Au,
1520 final_line_cross_size: Au,
1521 ) -> FinalFlexLineLayout {
1522 let auto_margins_count = self
1530 .items
1531 .iter()
1532 .map(|item| {
1533 item.item.margin.main_start.is_auto() as u32 +
1534 item.item.margin.main_end.is_auto() as u32
1535 })
1536 .sum::<u32>();
1537 let (space_distributed_to_auto_main_margins, free_space_in_main_axis) =
1538 if self.free_space_in_main_axis > Au::zero() && auto_margins_count > 0 {
1539 (
1540 self.free_space_in_main_axis / auto_margins_count as i32,
1541 Au::zero(),
1542 )
1543 } else {
1544 (Au::zero(), self.free_space_in_main_axis)
1545 };
1546
1547 let item_count = self.items.len();
1550 let mut shared_alignment_baseline = None;
1551 let mut item_used_cross_sizes = Vec::with_capacity(item_count);
1552 let mut item_margins = Vec::with_capacity(item_count);
1553 for item in self.items.iter_mut() {
1554 let cross_axis = match flex_context.config.flex_axis {
1555 FlexAxis::Row => Direction::Block,
1556 FlexAxis::Column => Direction::Inline,
1557 };
1558 let layout = &mut item.layout_result;
1559 let get_content_size = || match cross_axis {
1560 Direction::Block => layout.content_block_size.into(),
1561 Direction::Inline => item
1562 .item
1563 .inline_content_sizes(flex_context, item.used_main_size),
1564 };
1565 let used_cross_size = item.item.content_cross_sizes.resolve(
1566 cross_axis,
1567 item.item.automatic_cross_size,
1568 Au::zero,
1569 Some(Au::zero().max(final_line_cross_size - item.item.pbm_auto_is_zero.cross)),
1570 get_content_size,
1571 item.item.box_.independent_formatting_context.is_table() &&
1576 cross_axis == Direction::Inline,
1577 );
1578 item_used_cross_sizes.push(used_cross_size);
1579
1580 let needs_new_layout = match cross_axis {
1587 Direction::Block => {
1588 (match item.item.content_cross_sizes.preferred {
1589 Size::Initial => item.item.automatic_cross_size == Size::Stretch,
1590 Size::Stretch => true,
1591 _ => false,
1592 }) && SizeConstraint::Definite(used_cross_size) !=
1593 layout.containing_block_size.block &&
1594 layout.depends_on_block_constraints
1595 },
1596 Direction::Inline => used_cross_size != layout.containing_block_size.inline,
1597 };
1598 if needs_new_layout {
1599 #[cfg(feature = "tracing")]
1600 tracing::warn!(
1601 name: "Flex item stretch cache miss",
1602 cached_inline = ?layout.containing_block_size.inline,
1603 cached_block = ?layout.containing_block_size.block,
1604 required_cross_size = ?used_cross_size,
1605 cross_axis = ?cross_axis,
1606 depends_on_block_constraints = layout.depends_on_block_constraints,
1607 );
1608 *layout =
1609 item.item
1610 .layout(item.used_main_size, flex_context, Some(used_cross_size));
1611 }
1612
1613 let baseline = item.get_or_synthesize_baseline_with_cross_size(used_cross_size);
1614 if matches!(
1615 item.item.align_self.0.value(),
1616 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1617 ) {
1618 shared_alignment_baseline =
1619 Some(shared_alignment_baseline.unwrap_or(baseline).max(baseline));
1620 }
1621 item.layout_result
1622 .flex_alignment_baseline_relative_to_margin_box = Some(baseline);
1623
1624 item_margins.push(item.item.resolve_auto_margins(
1625 flex_context,
1626 final_line_cross_size,
1627 used_cross_size,
1628 space_distributed_to_auto_main_margins,
1629 ));
1630 }
1631
1632 let resolved_justify_content: AlignFlags = {
1638 let justify_content_style = flex_context.config.justify_content.primary();
1639
1640 let mut resolved_justify_content = justify_content_style.value();
1642 let mut is_safe = justify_content_style.flags() == AlignFlags::SAFE;
1643
1644 if item_count <= 1 || free_space_in_main_axis <= Au::zero() {
1649 (resolved_justify_content, is_safe) = match resolved_justify_content {
1650 AlignFlags::STRETCH => (AlignFlags::FLEX_START, true),
1651 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
1652 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
1653 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
1654 _ => (resolved_justify_content, is_safe),
1655 }
1656 };
1657
1658 if free_space_in_main_axis <= Au::zero() && is_safe {
1660 resolved_justify_content = AlignFlags::START;
1661 }
1662
1663 resolved_justify_content
1664 };
1665
1666 let main_start_position = match resolved_justify_content {
1668 AlignFlags::START => Au::zero(),
1669 AlignFlags::FLEX_START => {
1670 if flex_context.config.flex_direction_is_reversed {
1671 free_space_in_main_axis
1672 } else {
1673 Au::zero()
1674 }
1675 },
1676 AlignFlags::END => free_space_in_main_axis,
1677 AlignFlags::FLEX_END => {
1678 if flex_context.config.flex_direction_is_reversed {
1679 Au::zero()
1680 } else {
1681 free_space_in_main_axis
1682 }
1683 },
1684 AlignFlags::CENTER => free_space_in_main_axis / 2,
1685 AlignFlags::STRETCH => Au::zero(),
1686 AlignFlags::SPACE_BETWEEN => Au::zero(),
1687 AlignFlags::SPACE_AROUND => (free_space_in_main_axis / item_count as i32) / 2,
1688 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1689
1690 _ => Au::zero(),
1692 };
1693
1694 let item_main_interval = match resolved_justify_content {
1695 AlignFlags::START => Au::zero(),
1696 AlignFlags::FLEX_START => Au::zero(),
1697 AlignFlags::END => Au::zero(),
1698 AlignFlags::FLEX_END => Au::zero(),
1699 AlignFlags::CENTER => Au::zero(),
1700 AlignFlags::STRETCH => Au::zero(),
1701 AlignFlags::SPACE_BETWEEN => free_space_in_main_axis / (item_count - 1) as i32,
1702 AlignFlags::SPACE_AROUND => free_space_in_main_axis / item_count as i32,
1703 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1704
1705 _ => Au::zero(),
1707 };
1708 let item_main_interval = item_main_interval + main_gap;
1709
1710 let mut all_baselines = Baselines::default();
1711 let mut main_position_cursor = main_start_position;
1712
1713 let items = std::mem::take(&mut self.items);
1714 let item_fragments = izip!(items, item_margins, item_used_cross_sizes.iter())
1715 .map(|(item, item_margin, item_used_cross_size)| {
1716 let item_used_size = FlexRelativeVec2 {
1717 main: item.used_main_size,
1718 cross: *item_used_cross_size,
1719 };
1720 item.collect_fragment(
1721 &self,
1722 item_used_size,
1723 item_margin,
1724 item_main_interval,
1725 final_line_cross_size,
1726 &shared_alignment_baseline,
1727 flex_context,
1728 &mut all_baselines,
1729 &mut main_position_cursor,
1730 )
1731 })
1732 .collect();
1733
1734 FinalFlexLineLayout {
1735 cross_size: final_line_cross_size,
1736 item_fragments,
1737 all_baselines,
1738 shared_alignment_baseline,
1739 }
1740 }
1741}
1742
1743impl FlexItem<'_> {
1744 #[servo_tracing::instrument(
1749 name = "FlexItem::layout",
1750 skip_all,
1751 fields(
1752 self_address = self as *const _ as usize,
1753 box_address = self.box_ as *const _ as usize,
1754 )
1755 )]
1756 #[allow(clippy::too_many_arguments)]
1757 fn layout(
1758 &self,
1759 used_main_size: Au,
1760 flex_context: &FlexContext,
1761 used_cross_size_override: Option<Au>,
1762 ) -> FlexItemLayoutResult {
1763 let containing_block = flex_context.containing_block;
1764 let independent_formatting_context = &self.box_.independent_formatting_context;
1765 let is_table = independent_formatting_context.is_table();
1766 let mut positioning_context = PositioningContext::default();
1767 let item_writing_mode = independent_formatting_context.style().writing_mode;
1768 let item_is_horizontal = item_writing_mode.is_horizontal();
1769 let flex_axis = flex_context.config.flex_axis;
1770 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
1771 containing_block.style.writing_mode.is_horizontal(),
1772 item_is_horizontal,
1773 flex_axis,
1774 );
1775
1776 let (inline_size, block_size) = if cross_axis_is_item_block_axis {
1777 let cross_size = match used_cross_size_override {
1778 Some(s) => SizeConstraint::Definite(s),
1779 None => {
1780 let inline_stretch_size =
1781 Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.main);
1782 let block_stretch_size = containing_block
1783 .size
1784 .block
1785 .to_definite()
1786 .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
1787 let tentative_block_content_size = independent_formatting_context
1788 .tentative_block_content_size(
1789 self.preferred_aspect_ratio,
1790 inline_stretch_size,
1791 );
1792 if let Some(block_content_size) = tentative_block_content_size {
1793 SizeConstraint::Definite(self.content_cross_sizes.resolve(
1794 Direction::Block,
1795 Size::FitContent,
1796 Au::zero,
1797 block_stretch_size,
1798 || block_content_size,
1799 is_table,
1800 ))
1801 } else {
1802 self.content_cross_sizes.resolve_extrinsic(
1803 Size::FitContent,
1804 Au::zero(),
1805 block_stretch_size,
1806 )
1807 }
1808 },
1809 };
1810 (used_main_size, cross_size)
1811 } else {
1812 let cross_size = used_cross_size_override.unwrap_or_else(|| {
1813 let stretch_size =
1814 Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.cross);
1815 self.content_cross_sizes.resolve(
1816 Direction::Inline,
1817 Size::FitContent,
1818 Au::zero,
1819 Some(stretch_size),
1820 || self.inline_content_sizes(flex_context, used_main_size),
1821 is_table,
1822 )
1823 });
1824 let is_grid = self.box_.independent_formatting_context.is_grid();
1833 let main_size = if is_grid ||
1834 self.flex_base_size_is_definite ||
1835 flex_context
1836 .container_inner_size_constraint
1837 .main
1838 .is_definite()
1839 {
1840 SizeConstraint::Definite(used_main_size)
1841 } else {
1842 SizeConstraint::default()
1843 };
1844 (cross_size, main_size)
1845 };
1846
1847 let item_style = independent_formatting_context.style();
1848 let item_as_containing_block = ContainingBlock {
1849 size: ContainingBlockSize {
1850 inline: inline_size,
1851 block: block_size,
1852 },
1853 style: item_style,
1854 };
1855
1856 let lazy_block_size = if !cross_axis_is_item_block_axis {
1857 used_main_size.into()
1858 } else if let Some(cross_size) = used_cross_size_override {
1859 cross_size.into()
1860 } else {
1861 let stretch_size = containing_block
1862 .size
1863 .block
1864 .to_definite()
1865 .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
1866 LazySize::new(
1867 &self.content_cross_sizes,
1868 Direction::Block,
1869 Size::FitContent,
1870 Au::zero,
1871 stretch_size,
1872 is_table,
1873 )
1874 };
1875
1876 let layout = independent_formatting_context.layout(
1877 flex_context.layout_context,
1878 &mut positioning_context,
1879 &item_as_containing_block,
1880 containing_block,
1881 self.preferred_aspect_ratio,
1882 &lazy_block_size,
1883 );
1884 let IndependentFormattingContextLayoutResult {
1885 fragments,
1886 content_block_size,
1887 baselines: content_box_baselines,
1888 depends_on_block_constraints,
1889 specific_layout_info,
1890 ..
1891 } = layout;
1892
1893 let hypothetical_cross_size = if cross_axis_is_item_block_axis {
1894 lazy_block_size.resolve(|| content_block_size)
1895 } else {
1896 inline_size
1897 };
1898
1899 let item_inline_axis_is_horizontal = item_style.writing_mode.is_horizontal();
1900 let container_inline_axis_is_horizontal = flex_context.config.writing_mode.is_horizontal();
1901 let container_main_axis_is_horizontal = match flex_axis {
1902 FlexAxis::Row => container_inline_axis_is_horizontal,
1903 FlexAxis::Column => !container_inline_axis_is_horizontal,
1904 };
1905 let item_inline_axis_parallel_to_container_inline_axis =
1906 item_inline_axis_is_horizontal == container_inline_axis_is_horizontal;
1907 let item_inline_axis_parallel_to_container_main_axis =
1908 item_inline_axis_is_horizontal == container_main_axis_is_horizontal;
1909
1910 let content_baselines_relative_to_margin_box = content_box_baselines.offset(
1911 self.margin.cross_start.auto_is(Au::zero) +
1912 self.padding.cross_start +
1913 self.border.cross_start,
1914 );
1915
1916 let content_baselines_for_parent_relative_to_margin_box =
1917 if item_inline_axis_parallel_to_container_inline_axis {
1918 content_baselines_relative_to_margin_box
1919 } else {
1920 Baselines::default()
1921 };
1922
1923 let flex_alignment_baseline_relative_to_margin_box =
1924 if item_inline_axis_parallel_to_container_main_axis {
1925 match self.align_self.0.value() {
1926 AlignFlags::BASELINE => content_baselines_relative_to_margin_box.first,
1928 AlignFlags::LAST_BASELINE => content_baselines_relative_to_margin_box.last,
1929 _ => None,
1930 }
1931 } else {
1932 None
1933 };
1934
1935 FlexItemLayoutResult {
1936 hypothetical_cross_size,
1937 fragments,
1938 positioning_context,
1939 content_baselines_for_parent_relative_to_margin_box,
1940 flex_alignment_baseline_relative_to_margin_box,
1941 content_block_size,
1942 containing_block_size: item_as_containing_block.size,
1943 depends_on_block_constraints,
1944 specific_layout_info,
1945 }
1946 }
1947
1948 fn synthesized_baseline_relative_to_margin_box(&self, content_size: Au) -> Au {
1949 content_size +
1953 self.margin.cross_start.auto_is(Au::zero) +
1954 self.padding.cross_start +
1955 self.border.cross_start +
1956 self.border.cross_end +
1957 self.padding.cross_end
1958 }
1959
1960 fn resolve_auto_margins(
1966 &self,
1967 flex_context: &FlexContext,
1968 line_cross_size: Au,
1969 item_cross_content_size: Au,
1970 space_distributed_to_auto_main_margins: Au,
1971 ) -> FlexRelativeSides<Au> {
1972 let main_start = self
1973 .margin
1974 .main_start
1975 .auto_is(|| space_distributed_to_auto_main_margins);
1976 let main_end = self
1977 .margin
1978 .main_end
1979 .auto_is(|| space_distributed_to_auto_main_margins);
1980
1981 let auto_count = match (self.margin.cross_start, self.margin.cross_end) {
1982 (AuOrAuto::LengthPercentage(cross_start), AuOrAuto::LengthPercentage(cross_end)) => {
1983 return FlexRelativeSides {
1984 cross_start,
1985 cross_end,
1986 main_start,
1987 main_end,
1988 };
1989 },
1990 (AuOrAuto::Auto, AuOrAuto::Auto) => 2,
1991 _ => 1,
1992 };
1993 let outer_cross_size = self.pbm_auto_is_zero.cross + item_cross_content_size;
1994 let available = line_cross_size - outer_cross_size;
1995 let cross_start;
1996 let cross_end;
1997 if available > Au::zero() {
1998 let each_auto_margin = available / auto_count;
1999 cross_start = self.margin.cross_start.auto_is(|| each_auto_margin);
2000 cross_end = self.margin.cross_end.auto_is(|| each_auto_margin);
2001 } else {
2002 let flex_wrap = flex_context.containing_block.style.get_position().flex_wrap;
2015 let flex_wrap_reverse = match flex_wrap {
2016 FlexWrap::Nowrap | FlexWrap::Wrap => false,
2017 FlexWrap::WrapReverse => true,
2018 };
2019 if flex_wrap_reverse {
2023 cross_start = self.margin.cross_start.auto_is(|| available);
2024 cross_end = self.margin.cross_end.auto_is(Au::zero);
2025 } else {
2026 cross_start = self.margin.cross_start.auto_is(Au::zero);
2027 cross_end = self.margin.cross_end.auto_is(|| available);
2028 }
2029 }
2030
2031 FlexRelativeSides {
2032 cross_start,
2033 cross_end,
2034 main_start,
2035 main_end,
2036 }
2037 }
2038
2039 fn align_along_cross_axis(
2041 &self,
2042 margin: &FlexRelativeSides<Au>,
2043 used_cross_size: &Au,
2044 line_cross_size: Au,
2045 propagated_baseline: Au,
2046 max_propagated_baseline: Au,
2047 wrap_reverse: bool,
2048 ) -> Au {
2049 let ending_alignment = line_cross_size - *used_cross_size - self.pbm_auto_is_zero.cross;
2050 let outer_cross_start =
2051 if self.margin.cross_start.is_auto() || self.margin.cross_end.is_auto() {
2052 Au::zero()
2053 } else {
2054 match self.align_self.0.value() {
2055 AlignFlags::STRETCH => Au::zero(),
2056 AlignFlags::CENTER => ending_alignment / 2,
2057 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE => {
2058 max_propagated_baseline - propagated_baseline
2059 },
2060 AlignFlags::START => {
2061 if !wrap_reverse {
2062 Au::zero()
2063 } else {
2064 ending_alignment
2065 }
2066 },
2067 AlignFlags::END => {
2068 if !wrap_reverse {
2069 ending_alignment
2070 } else {
2071 Au::zero()
2072 }
2073 },
2074 _ => Au::zero(),
2075 }
2076 };
2077 outer_cross_start + margin.cross_start + self.border.cross_start + self.padding.cross_start
2078 }
2079
2080 #[inline]
2081 fn inline_content_sizes(&self, flex_context: &FlexContext, block_size: Au) -> ContentSizes {
2082 self.box_.inline_content_sizes(
2083 flex_context,
2084 SizeConstraint::Definite(block_size),
2085 self.preferred_aspect_ratio,
2086 )
2087 }
2088}
2089
2090impl FlexItemBox {
2091 fn to_flex_item<'a>(
2092 &self,
2093 layout_context: &LayoutContext,
2094 containing_block: &IndefiniteContainingBlock,
2095 content_box_sizes_and_pbm: &ContentBoxSizesAndPBM,
2096 config: &FlexContainerConfig,
2097 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2098 ) -> FlexItem<'_> {
2099 let flex_axis = config.flex_axis;
2100 let style = self.style();
2101 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
2102 containing_block.style.writing_mode.is_horizontal(),
2103 style.writing_mode.is_horizontal(),
2104 flex_axis,
2105 );
2106 let main_axis = if cross_axis_is_item_block_axis {
2107 Direction::Inline
2108 } else {
2109 Direction::Block
2110 };
2111 let align_self = AlignItems(config.resolve_align_self_for_child(style));
2112
2113 let ContentBoxSizesAndPBM {
2114 content_box_sizes,
2115 pbm,
2116 depends_on_block_constraints,
2117 preferred_size_computes_to_auto,
2118 } = content_box_sizes_and_pbm;
2119
2120 let preferred_aspect_ratio = self
2121 .independent_formatting_context
2122 .preferred_aspect_ratio(&pbm.padding_border_sums);
2123 let padding = config.sides_to_flex_relative(pbm.padding);
2124 let border = config.sides_to_flex_relative(pbm.border);
2125 let margin = config.sides_to_flex_relative(pbm.margin);
2126 let padding_border = padding.sum_by_axis() + border.sum_by_axis();
2127 let margin_auto_is_zero = config.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
2128 let pbm_auto_is_zero = FlexRelativeVec2 {
2129 main: padding_border.main,
2130 cross: padding_border.cross,
2131 } + margin_auto_is_zero.sum_by_axis();
2132 let (content_main_sizes, content_cross_sizes, cross_size_computes_to_auto) = match flex_axis
2133 {
2134 FlexAxis::Row => (
2135 &content_box_sizes.inline,
2136 &content_box_sizes.block,
2137 preferred_size_computes_to_auto.block,
2138 ),
2139 FlexAxis::Column => (
2140 &content_box_sizes.block,
2141 &content_box_sizes.inline,
2142 preferred_size_computes_to_auto.inline,
2143 ),
2144 };
2145 let automatic_cross_size = if cross_size_computes_to_auto &&
2146 item_with_auto_cross_size_stretches_to_line_size(align_self, &margin)
2147 {
2148 Size::Stretch
2149 } else {
2150 Size::FitContent
2151 };
2152 let automatic_cross_size_for_intrinsic_sizing = if config.container_is_single_line {
2153 automatic_cross_size
2154 } else {
2155 Size::FitContent
2156 };
2157 let containing_block_size = flex_axis.vec2_to_flex_relative(containing_block.size);
2158 let stretch_size = FlexRelativeVec2 {
2159 main: containing_block_size
2160 .main
2161 .map(|v| Au::zero().max(v - pbm_auto_is_zero.main)),
2162 cross: containing_block_size
2163 .cross
2164 .map(|v| Au::zero().max(v - pbm_auto_is_zero.cross)),
2165 };
2166
2167 let is_table = self.independent_formatting_context.is_table();
2168 let tentative_cross_content_size = if cross_axis_is_item_block_axis {
2169 self.independent_formatting_context
2170 .tentative_block_content_size(
2171 preferred_aspect_ratio,
2172 stretch_size.main.unwrap_or_default(),
2173 )
2174 } else {
2175 None
2176 };
2177 let (preferred_cross_size, min_cross_size, max_cross_size) =
2178 if let Some(cross_content_size) = tentative_cross_content_size {
2179 let (preferred, min, max) = content_cross_sizes.resolve_each(
2180 automatic_cross_size_for_intrinsic_sizing,
2181 Au::zero,
2182 stretch_size.cross,
2183 || cross_content_size,
2184 is_table,
2185 );
2186 (Some(preferred), min, max)
2187 } else {
2188 content_cross_sizes.resolve_each_extrinsic(
2189 automatic_cross_size_for_intrinsic_sizing,
2190 Au::zero(),
2191 stretch_size.cross,
2192 )
2193 };
2194 let cross_size = SizeConstraint::new(preferred_cross_size, min_cross_size, max_cross_size);
2195
2196 let transferred_size_suggestion =
2201 LazyCell::new(|| match (preferred_aspect_ratio, cross_size) {
2202 (Some(ratio), SizeConstraint::Definite(cross_size)) => {
2203 Some(ratio.compute_dependent_size(main_axis, cross_size))
2204 },
2205 _ => None,
2206 });
2207
2208 let flex_base_size_is_definite = Cell::new(true);
2210 let main_content_sizes = LazyCell::new(|| {
2211 let flex_item = &self.independent_formatting_context;
2212 if let Some(transferred_size_suggestion) = *transferred_size_suggestion {
2218 return transferred_size_suggestion.into();
2219 }
2220
2221 flex_base_size_is_definite.set(false);
2222
2223 if cross_axis_is_item_block_axis {
2232 let constraint_space =
2235 ConstraintSpace::new(cross_size, style, preferred_aspect_ratio);
2236 let content_sizes = flex_item
2237 .inline_content_sizes(layout_context, &constraint_space)
2238 .sizes;
2239 if let Some(ratio) = preferred_aspect_ratio {
2240 let transferred_min = ratio.compute_dependent_size(main_axis, min_cross_size);
2241 let transferred_max =
2242 max_cross_size.map(|v| ratio.compute_dependent_size(main_axis, v));
2243 content_sizes
2244 .map(|size| size.clamp_between_extremums(transferred_min, transferred_max))
2245 } else {
2246 content_sizes
2247 }
2248 } else {
2249 self.layout_for_block_content_size(
2250 flex_context_getter(),
2251 &pbm_auto_is_zero,
2252 content_box_sizes,
2253 preferred_aspect_ratio,
2254 automatic_cross_size_for_intrinsic_sizing,
2255 IntrinsicSizingMode::Size,
2256 )
2257 .into()
2258 }
2259 });
2260
2261 let flex_base_size = self
2262 .flex_basis(
2263 containing_block_size.main,
2264 content_main_sizes.preferred,
2265 padding_border.main,
2266 )
2267 .resolve_for_preferred(Size::MaxContent, stretch_size.main, &main_content_sizes);
2268 let flex_base_size_is_definite = flex_base_size_is_definite.take();
2269
2270 let content_max_main_size = content_main_sizes
2271 .max
2272 .resolve_for_max(stretch_size.main, &main_content_sizes);
2273
2274 let get_automatic_minimum_size = || {
2275 if style.establishes_scroll_container(self.base_fragment_info().flags) {
2277 return Au::zero();
2278 }
2279
2280 let specified_size_suggestion = content_main_sizes
2284 .preferred
2285 .maybe_resolve_extrinsic(stretch_size.main);
2286
2287 let is_replaced = self.independent_formatting_context.is_replaced();
2288
2289 let content_size_suggestion = match preferred_aspect_ratio {
2294 Some(ratio) => main_content_sizes.min_content.clamp_between_extremums(
2295 ratio.compute_dependent_size(main_axis, min_cross_size),
2296 max_cross_size.map(|l| ratio.compute_dependent_size(main_axis, l)),
2297 ),
2298 None => main_content_sizes.min_content,
2299 };
2300
2301 match (specified_size_suggestion, *transferred_size_suggestion) {
2308 (Some(specified), _) => specified.min(content_size_suggestion),
2309 (_, Some(transferred)) if is_replaced => transferred.min(content_size_suggestion),
2310 _ => content_size_suggestion,
2311 }
2312 .clamp_below_max(content_max_main_size)
2313 };
2314 let content_min_main_size = content_main_sizes.min.resolve_for_min(
2315 get_automatic_minimum_size,
2316 stretch_size.main,
2317 &main_content_sizes,
2318 is_table,
2319 );
2320
2321 FlexItem {
2322 box_: self,
2323 content_cross_sizes: content_cross_sizes.clone(),
2324 padding,
2325 border,
2326 margin: config.sides_to_flex_relative(pbm.margin),
2327 pbm_auto_is_zero,
2328 flex_base_size,
2329 flex_base_size_is_definite,
2330 hypothetical_main_size: flex_base_size
2331 .clamp_between_extremums(content_min_main_size, content_max_main_size),
2332 content_min_main_size,
2333 content_max_main_size,
2334 align_self,
2335 depends_on_block_constraints: *depends_on_block_constraints,
2336 preferred_aspect_ratio,
2337 automatic_cross_size,
2338 automatic_cross_size_for_intrinsic_sizing,
2339 }
2340 }
2341
2342 fn main_content_size_info<'a>(
2343 &self,
2344 layout_context: &LayoutContext,
2345 containing_block: &IndefiniteContainingBlock,
2346 config: &FlexContainerConfig,
2347 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2348 ) -> FlexItemBoxInlineContentSizesInfo {
2349 let content_box_sizes_and_pbm = self
2350 .independent_formatting_context
2351 .layout_style()
2352 .content_box_sizes_and_padding_border_margin(containing_block);
2353
2354 let FlexItem {
2357 flex_base_size,
2358 content_min_main_size,
2359 content_max_main_size,
2360 pbm_auto_is_zero,
2361 preferred_aspect_ratio,
2362 automatic_cross_size_for_intrinsic_sizing,
2363 ..
2364 } = self.to_flex_item(
2365 layout_context,
2366 containing_block,
2367 &content_box_sizes_and_pbm,
2368 config,
2369 flex_context_getter,
2370 );
2371
2372 let (content_contribution_sizes, depends_on_block_constraints) = match config.flex_axis {
2375 FlexAxis::Row => {
2376 let auto_minimum = LogicalVec2 {
2377 inline: content_min_main_size,
2378 block: Au::zero(),
2379 };
2380 let InlineContentSizesResult {
2381 sizes,
2382 depends_on_block_constraints,
2383 } = self
2384 .independent_formatting_context
2385 .outer_inline_content_sizes(
2386 layout_context,
2387 containing_block,
2388 &auto_minimum,
2389 automatic_cross_size_for_intrinsic_sizing == Size::Stretch,
2390 );
2391 (sizes, depends_on_block_constraints)
2392 },
2393 FlexAxis::Column => {
2394 let size = self.layout_for_block_content_size(
2395 flex_context_getter(),
2396 &pbm_auto_is_zero,
2397 &content_box_sizes_and_pbm.content_box_sizes,
2398 preferred_aspect_ratio,
2399 automatic_cross_size_for_intrinsic_sizing,
2400 IntrinsicSizingMode::Contribution,
2401 );
2402 (size.into(), true)
2403 },
2404 };
2405
2406 let outer_flex_base_size = flex_base_size + pbm_auto_is_zero.main;
2407 let outer_min_main_size = content_min_main_size + pbm_auto_is_zero.main;
2408 let outer_max_main_size = content_max_main_size.map(|v| v + pbm_auto_is_zero.main);
2409 let max_flex_factors = self.desired_flex_factors_for_preferred_width(
2410 content_contribution_sizes.max_content,
2411 flex_base_size,
2412 outer_flex_base_size,
2413 );
2414
2415 let min_flex_factors = self.desired_flex_factors_for_preferred_width(
2419 content_contribution_sizes.min_content,
2420 flex_base_size,
2421 outer_flex_base_size,
2422 );
2423
2424 let mut min_content_main_size_for_multiline_container =
2431 content_contribution_sizes.min_content;
2432 let style_position = &self.style().get_position();
2433 if style_position.flex_grow.is_zero() {
2434 min_content_main_size_for_multiline_container.min_assign(outer_flex_base_size);
2435 }
2436 if style_position.flex_shrink.is_zero() {
2437 min_content_main_size_for_multiline_container.max_assign(outer_flex_base_size);
2438 }
2439 min_content_main_size_for_multiline_container =
2440 min_content_main_size_for_multiline_container
2441 .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
2442
2443 FlexItemBoxInlineContentSizesInfo {
2444 outer_flex_base_size,
2445 outer_min_main_size,
2446 outer_max_main_size,
2447 min_flex_factors,
2448 max_flex_factors,
2449 min_content_main_size_for_multiline_container,
2450 depends_on_block_constraints,
2451 }
2452 }
2453
2454 fn desired_flex_factors_for_preferred_width(
2455 &self,
2456 preferred_width: Au,
2457 flex_base_size: Au,
2458 outer_flex_base_size: Au,
2459 ) -> DesiredFlexFractionAndGrowOrShrinkFactor {
2460 let difference = (preferred_width - outer_flex_base_size).to_f32_px();
2461 let (flex_grow_or_scaled_flex_shrink_factor, desired_flex_fraction) = if difference > 0.0 {
2462 let flex_grow_factor = self.style().get_position().flex_grow.0;
2466
2467 (
2468 flex_grow_factor,
2469 if flex_grow_factor >= 1.0 {
2470 difference / flex_grow_factor
2471 } else {
2472 difference * flex_grow_factor
2473 },
2474 )
2475 } else if difference < 0.0 {
2476 let flex_shrink_factor = self.style().get_position().flex_shrink.0;
2480 let scaled_flex_shrink_factor = flex_shrink_factor * flex_base_size.to_f32_px();
2481
2482 (
2483 scaled_flex_shrink_factor,
2484 if scaled_flex_shrink_factor != 0.0 {
2485 difference / scaled_flex_shrink_factor
2486 } else {
2487 f32::NEG_INFINITY
2488 },
2489 )
2490 } else {
2491 (0.0, 0.0)
2492 };
2493
2494 DesiredFlexFractionAndGrowOrShrinkFactor {
2495 desired_flex_fraction,
2496 flex_grow_or_shrink_factor: flex_grow_or_scaled_flex_shrink_factor,
2497 }
2498 }
2499
2500 fn flex_basis(
2506 &self,
2507 container_definite_main_size: Option<Au>,
2508 main_preferred_size: Size<Au>,
2509 main_padding_border_sum: Au,
2510 ) -> Size<Au> {
2511 let style_position = &self.independent_formatting_context.style().get_position();
2512 match &style_position.flex_basis {
2513 FlexBasis::Content => Size::Initial,
2516
2517 FlexBasis::Size(size) => match Size::<LengthPercentage>::from(size.clone()) {
2518 Size::Initial => main_preferred_size,
2522
2523 size => {
2533 let apply_box_sizing = |length: Au| {
2534 match style_position.box_sizing {
2535 BoxSizing::ContentBox => length,
2536 BoxSizing::BorderBox => length - main_padding_border_sum,
2539 }
2540 };
2541 size.resolve_percentages_for_preferred(container_definite_main_size)
2542 .map(apply_box_sizing)
2543 },
2544 },
2545 }
2546 }
2547
2548 #[allow(clippy::too_many_arguments)]
2549 #[servo_tracing::instrument(name = "FlexContainer::layout_for_block_content_size", skip_all)]
2550 fn layout_for_block_content_size(
2551 &self,
2552 flex_context: &FlexContext,
2553 pbm_auto_is_zero: &FlexRelativeVec2<Au>,
2554 content_box_sizes: &LogicalVec2<Sizes>,
2555 preferred_aspect_ratio: Option<AspectRatio>,
2556 automatic_inline_size: Size<Au>,
2557 intrinsic_sizing_mode: IntrinsicSizingMode,
2558 ) -> Au {
2559 let content_block_size = || {
2560 let mut positioning_context = PositioningContext::default();
2561 let style = self.independent_formatting_context.style();
2562
2563 let tentative_block_size = SizeConstraint::default();
2567
2568 let inline_size = {
2571 let stretch_size =
2572 flex_context.containing_block.size.inline - pbm_auto_is_zero.cross;
2573 let get_content_size = || {
2574 self.inline_content_sizes(
2575 flex_context,
2576 tentative_block_size,
2577 preferred_aspect_ratio,
2578 )
2579 };
2580 content_box_sizes.inline.resolve(
2581 Direction::Inline,
2582 automatic_inline_size,
2583 Au::zero,
2584 Some(stretch_size),
2585 get_content_size,
2586 false,
2587 )
2588 };
2589 let item_as_containing_block = ContainingBlock {
2590 size: ContainingBlockSize {
2591 inline: inline_size,
2592 block: tentative_block_size,
2593 },
2594 style,
2595 };
2596 self.independent_formatting_context
2597 .layout(
2598 flex_context.layout_context,
2599 &mut positioning_context,
2600 &item_as_containing_block,
2601 flex_context.containing_block,
2602 preferred_aspect_ratio,
2603 &LazySize::intrinsic(),
2604 )
2605 .content_block_size
2606 };
2607 match intrinsic_sizing_mode {
2608 IntrinsicSizingMode::Contribution => {
2609 let stretch_size = flex_context
2610 .containing_block
2611 .size
2612 .block
2613 .to_definite()
2614 .map(|block_size| block_size - pbm_auto_is_zero.main);
2615 let inner_block_size = content_box_sizes.block.resolve(
2616 Direction::Block,
2617 Size::FitContent,
2618 Au::zero,
2619 stretch_size,
2620 || ContentSizes::from(content_block_size()),
2621 false, );
2629 inner_block_size + pbm_auto_is_zero.main
2630 },
2631 IntrinsicSizingMode::Size => content_block_size(),
2632 }
2633 }
2634
2635 fn inline_content_sizes(
2636 &self,
2637 flex_context: &FlexContext,
2638 block_size: SizeConstraint,
2639 preferred_aspect_ratio: Option<AspectRatio>,
2640 ) -> ContentSizes {
2641 let style = self.independent_formatting_context.style();
2642 let constraint_space = ConstraintSpace::new(block_size, style, preferred_aspect_ratio);
2643 self.independent_formatting_context
2644 .inline_content_sizes(flex_context.layout_context, &constraint_space)
2645 .sizes
2646 }
2647}