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