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::CacheableLayoutResult;
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_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.layout_result.content_baselines_relative_to_margin_box;
227 if flex_context.config.flex_direction_is_reversed {
228 if let Some(last_baseline) = baselines.last {
229 all_baselines
230 .last
231 .get_or_insert_with(|| adjust_baseline(last_baseline));
232 }
233 if let Some(first_baseline) = baselines.first {
234 all_baselines.first = Some(adjust_baseline(first_baseline));
235 }
236 } else {
237 if let Some(first_baseline) = baselines.first {
238 all_baselines
239 .first
240 .get_or_insert_with(|| adjust_baseline(first_baseline));
241 }
242 if let Some(last_baseline) = baselines.last {
243 all_baselines.last = Some(adjust_baseline(last_baseline));
244 }
245 }
246
247 let mut fragment_info = self.item.box_.base_fragment_info();
248 fragment_info
249 .flags
250 .insert(FragmentFlags::IS_FLEX_OR_GRID_ITEM);
251 if self.item.depends_on_block_constraints {
252 fragment_info.flags.insert(
253 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
254 );
255 }
256 let flags = fragment_info.flags;
257
258 let containing_block = flex_context.containing_block;
259 let container_writing_mode = containing_block.style.writing_mode;
260 let style = self.item.box_.style();
261
262 let mut fragment = BoxFragment::new(
263 fragment_info,
264 style.clone(),
265 self.layout_result.fragments,
266 content_rect.as_physical(Some(flex_context.containing_block)),
267 flex_context
268 .sides_to_flow_relative(self.item.padding)
269 .to_physical(container_writing_mode),
270 flex_context
271 .sides_to_flow_relative(self.item.border)
272 .to_physical(container_writing_mode),
273 flex_context
274 .sides_to_flow_relative(item_margin)
275 .to_physical(container_writing_mode),
276 self.layout_result.specific_layout_info,
277 );
278
279 if style.establishes_containing_block_for_absolute_descendants(flags) {
283 self.layout_result
284 .positioning_context
285 .layout_collected_children(flex_context.layout_context, &mut fragment);
286 }
287
288 if style.clone_position() == Position::Relative {
289 fragment.base.rect.origin += relative_adjustement(style, containing_block)
290 .to_physical_size(containing_block.style.writing_mode)
291 }
292
293 let fragment = ArcRefCell::new(fragment);
294 self.item
295 .box_
296 .independent_formatting_context
297 .base
298 .set_fragment(Fragment::Box(fragment.clone()));
299 (fragment, self.layout_result.positioning_context)
300 }
301}
302
303struct FinalFlexLineLayout {
306 cross_size: Au,
308 item_fragments: Vec<(ArcRefCell<BoxFragment>, PositioningContext)>,
311 shared_alignment_baseline: Option<Au>,
314 all_baselines: Baselines,
319}
320
321impl FlexContainerConfig {
322 fn resolve_reversable_flex_alignment(
323 &self,
324 align_flags: AlignFlags,
325 reversed: bool,
326 ) -> AlignFlags {
327 match (align_flags.value(), reversed) {
328 (AlignFlags::FLEX_START, false) => AlignFlags::START | align_flags.flags(),
329 (AlignFlags::FLEX_START, true) => AlignFlags::END | align_flags.flags(),
330 (AlignFlags::FLEX_END, false) => AlignFlags::END | align_flags.flags(),
331 (AlignFlags::FLEX_END, true) => AlignFlags::START | align_flags.flags(),
332 (_, _) => align_flags,
333 }
334 }
335
336 fn resolve_align_self_for_child(&self, child_style: &ComputedValues) -> AlignFlags {
337 self.resolve_reversable_flex_alignment(
338 child_style
339 .resolve_align_self(self.align_items, AlignFlags::STRETCH)
340 .0,
341 self.flex_wrap_is_reversed,
342 )
343 }
344
345 fn resolve_justify_content_for_child(&self) -> AlignFlags {
346 self.resolve_reversable_flex_alignment(
347 self.justify_content.primary(),
348 self.flex_direction_is_reversed,
349 )
350 }
351
352 fn sides_to_flex_relative<T>(&self, sides: LogicalSides<T>) -> FlexRelativeSides<T> {
353 self.main_start_cross_start_sides_are
354 .sides_to_flex_relative(sides)
355 }
356
357 fn sides_to_flow_relative<T>(&self, sides: FlexRelativeSides<T>) -> LogicalSides<T> {
358 self.main_start_cross_start_sides_are
359 .sides_to_flow_relative(sides)
360 }
361}
362
363impl FlexContext<'_> {
364 #[inline]
365 fn sides_to_flow_relative<T>(&self, x: FlexRelativeSides<T>) -> LogicalSides<T> {
366 self.config.sides_to_flow_relative(x)
367 }
368
369 #[inline]
370 fn rect_to_flow_relative(
371 &self,
372 base_rect_size: FlexRelativeVec2<Au>,
373 rect: FlexRelativeRect<Au>,
374 ) -> LogicalRect<Au> {
375 super::geom::rect_to_flow_relative(
376 self.config.flex_axis,
377 self.config.main_start_cross_start_sides_are,
378 base_rect_size,
379 rect,
380 )
381 }
382}
383
384#[derive(Debug, Default)]
385struct DesiredFlexFractionAndGrowOrShrinkFactor {
386 desired_flex_fraction: f32,
387 flex_grow_or_shrink_factor: f32,
388}
389
390#[derive(Default)]
391struct FlexItemBoxInlineContentSizesInfo {
392 outer_flex_base_size: Au,
393 outer_min_main_size: Au,
394 outer_max_main_size: Option<Au>,
395 min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
396 max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
397 min_content_main_size_for_multiline_container: Au,
398 depends_on_block_constraints: bool,
399}
400
401impl ComputeInlineContentSizes for FlexContainer {
402 #[servo_tracing::instrument(name = "FlexContainer::compute_inline_content_sizes", skip_all)]
403 fn compute_inline_content_sizes(
404 &self,
405 layout_context: &LayoutContext,
406 constraint_space: &ConstraintSpace,
407 ) -> InlineContentSizesResult {
408 match self.config.flex_axis {
409 FlexAxis::Row => {
410 self.main_content_sizes(layout_context, &constraint_space.into(), || {
411 unreachable!(
412 "Unexpected FlexContext query during row flex intrinsic size calculation."
413 )
414 })
415 },
416 FlexAxis::Column => self.cross_content_sizes(layout_context, &constraint_space.into()),
417 }
418 }
419}
420
421impl FlexContainer {
422 fn cross_content_sizes(
423 &self,
424 layout_context: &LayoutContext,
425 containing_block_for_children: &IndefiniteContainingBlock,
426 ) -> InlineContentSizesResult {
427 assert_eq!(
429 self.config.flex_axis,
430 FlexAxis::Column,
431 "The cross axis should be the inline one"
432 );
433 let mut sizes = ContentSizes::zero();
434 let mut depends_on_block_constraints = false;
435 for kid in self.children.iter() {
436 let kid = &*kid.borrow();
437 match kid {
438 FlexLevelBox::FlexItem(item) => {
439 let ifc = &item.independent_formatting_context;
443 let result = ifc.outer_inline_content_sizes(
444 layout_context,
445 containing_block_for_children,
446 &LogicalVec2::zero(),
447 false, );
449 sizes.max_assign(result.sizes);
450 depends_on_block_constraints |= result.depends_on_block_constraints;
451 },
452 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
453 }
454 }
455 InlineContentSizesResult {
456 sizes,
457 depends_on_block_constraints,
458 }
459 }
460
461 fn main_content_sizes<'a>(
462 &self,
463 layout_context: &LayoutContext,
464 containing_block_for_children: &IndefiniteContainingBlock,
465 flex_context_getter: impl Fn() -> &'a FlexContext<'a>,
466 ) -> InlineContentSizesResult {
467 let mut chosen_max_flex_fraction = f32::NEG_INFINITY;
476 let mut chosen_min_flex_fraction = f32::NEG_INFINITY;
477 let mut sum_of_flex_grow_factors = 0.0;
478 let mut sum_of_flex_shrink_factors = 0.0;
479 let mut item_infos = vec![];
480
481 for kid in self.children.iter() {
482 let kid = &*kid.borrow();
483 match kid {
484 FlexLevelBox::FlexItem(item) => {
485 sum_of_flex_grow_factors += item.style().get_position().flex_grow.0;
486 sum_of_flex_shrink_factors += item.style().get_position().flex_shrink.0;
487
488 let info = item.main_content_size_info(
489 layout_context,
490 containing_block_for_children,
491 &self.config,
492 &flex_context_getter,
493 );
494
495 chosen_max_flex_fraction =
500 chosen_max_flex_fraction.max(info.max_flex_factors.desired_flex_fraction);
501 chosen_min_flex_fraction =
502 chosen_min_flex_fraction.max(info.min_flex_factors.desired_flex_fraction);
503
504 item_infos.push(info)
505 },
506 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
507 }
508 }
509
510 let normalize_flex_fraction = |chosen_flex_fraction| {
511 if chosen_flex_fraction > 0.0 && sum_of_flex_grow_factors < 1.0 {
512 chosen_flex_fraction / sum_of_flex_grow_factors
516 } else if chosen_flex_fraction < 0.0 && sum_of_flex_shrink_factors < 1.0 {
517 chosen_flex_fraction * sum_of_flex_shrink_factors
521 } else {
522 chosen_flex_fraction
523 }
524 };
525
526 let chosen_min_flex_fraction = normalize_flex_fraction(chosen_min_flex_fraction);
527 let chosen_max_flex_fraction = normalize_flex_fraction(chosen_max_flex_fraction);
528
529 let main_gap = match self.config.flex_axis {
530 FlexAxis::Row => self.style.clone_column_gap(),
531 FlexAxis::Column => self.style.clone_row_gap(),
532 };
533 let main_gap = match main_gap {
534 LengthPercentageOrNormal::LengthPercentage(length_percentage) => {
535 length_percentage.to_used_value(Au::zero())
536 },
537 LengthPercentageOrNormal::Normal => Au::zero(),
538 };
539 let extra_space_from_main_gap = main_gap * (item_infos.len() as i32 - 1);
540 let mut container_max_content_size = extra_space_from_main_gap;
541 let mut container_min_content_size = if self.config.flex_wrap == FlexWrap::Nowrap {
542 extra_space_from_main_gap
543 } else {
544 Au::zero()
545 };
546 let mut container_depends_on_block_constraints = false;
547
548 for FlexItemBoxInlineContentSizesInfo {
549 outer_flex_base_size,
550 outer_min_main_size,
551 outer_max_main_size,
552 min_flex_factors,
553 max_flex_factors,
554 min_content_main_size_for_multiline_container,
555 depends_on_block_constraints,
556 } in item_infos.iter()
557 {
558 container_max_content_size += (*outer_flex_base_size +
564 Au::from_f32_px(
565 max_flex_factors.flex_grow_or_shrink_factor * chosen_max_flex_fraction,
566 ))
567 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
568
569 if self.config.flex_wrap == FlexWrap::Nowrap {
580 container_min_content_size += (*outer_flex_base_size +
581 Au::from_f32_px(
582 min_flex_factors.flex_grow_or_shrink_factor * chosen_min_flex_fraction,
583 ))
584 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
585 } else {
586 container_min_content_size
587 .max_assign(*min_content_main_size_for_multiline_container);
588 }
589
590 container_depends_on_block_constraints |= depends_on_block_constraints;
591 }
592
593 InlineContentSizesResult {
594 sizes: ContentSizes {
595 min_content: container_min_content_size,
596 max_content: container_max_content_size,
597 },
598 depends_on_block_constraints: container_depends_on_block_constraints,
599 }
600 }
601
602 #[servo_tracing::instrument(
604 name = "FlexContainer::layout",
605 skip_all,
606 fields(self_address = self as *const _ as usize)
607 )]
608 pub(crate) fn layout(
609 &self,
610 layout_context: &LayoutContext,
611 positioning_context: &mut PositioningContext,
612 containing_block: &ContainingBlock,
613 lazy_block_size: &LazySize,
614 ) -> CacheableLayoutResult {
615 let mut flex_context = FlexContext {
616 config: self.config.clone(),
617 layout_context,
618 containing_block,
619 container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative(
621 LogicalVec2 {
622 inline: SizeConstraint::Definite(containing_block.size.inline),
623 block: containing_block.size.block,
624 },
625 ),
626 };
627
628 let container_main_size = match self.config.flex_axis {
631 FlexAxis::Row => containing_block.size.inline,
632 FlexAxis::Column => lazy_block_size.resolve(|| {
633 let mut containing_block = IndefiniteContainingBlock::from(containing_block);
634 containing_block.size.block = None;
635 self.main_content_sizes(layout_context, &containing_block, || &flex_context)
636 .sizes
637 .max_content
638 }),
639 };
640
641 let mut flex_items = Vec::with_capacity(self.children.len());
643
644 let absolutely_positioned_items_with_original_order = self
652 .children
653 .iter()
654 .map(|arcrefcell| {
655 let borrowed = arcrefcell.borrow();
656 match &*borrowed {
657 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned) => {
658 FlexContent::AbsolutelyPositionedBox(absolutely_positioned.clone())
659 },
660 FlexLevelBox::FlexItem(_) => {
661 let item = AtomicRef::map(borrowed, |child| match child {
662 FlexLevelBox::FlexItem(item) => item,
663 _ => unreachable!(),
664 });
665 flex_items.push(item);
666 FlexContent::FlexItemPlaceholder
667 },
668 }
669 })
670 .collect::<Vec<_>>();
671
672 let flex_item_boxes = flex_items.iter().map(|child| &**child);
673 let flex_items = flex_item_boxes
674 .map(|flex_item_box| FlexItem::new(&flex_context, flex_item_box))
675 .collect::<Vec<_>>();
676
677 let row_gap = self.style.clone_row_gap();
678 let column_gap = self.style.clone_column_gap();
679 let (cross_gap, main_gap) = match flex_context.config.flex_axis {
680 FlexAxis::Row => (row_gap, column_gap),
681 FlexAxis::Column => (column_gap, row_gap),
682 };
683 let cross_gap = match cross_gap {
684 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
685 .maybe_to_used_value(
686 flex_context
687 .container_inner_size_constraint
688 .cross
689 .to_definite(),
690 )
691 .unwrap_or_default(),
692 LengthPercentageOrNormal::Normal => Au::zero(),
693 };
694 let main_gap = match main_gap {
695 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
696 .maybe_to_used_value(
697 flex_context
698 .container_inner_size_constraint
699 .main
700 .to_definite(),
701 )
702 .unwrap_or_default(),
703 LengthPercentageOrNormal::Normal => Au::zero(),
704 };
705
706 let initial_line_layouts = do_initial_flex_line_layout(
709 &mut flex_context,
710 container_main_size,
711 flex_items,
712 main_gap,
713 );
714
715 let line_count = initial_line_layouts.len();
716 let content_cross_size = initial_line_layouts
717 .iter()
718 .map(|layout| layout.line_size.cross)
719 .sum::<Au>() +
720 cross_gap * (line_count as i32 - 1);
721 let content_block_size = match self.config.flex_axis {
722 FlexAxis::Row => content_cross_size,
723 FlexAxis::Column => container_main_size,
724 };
725
726 let container_cross_size = match self.config.flex_axis {
728 FlexAxis::Row => lazy_block_size.resolve(|| content_cross_size),
729 FlexAxis::Column => containing_block.size.inline,
730 };
731
732 let container_size = FlexRelativeVec2 {
733 main: container_main_size,
734 cross: container_cross_size,
735 };
736
737 let mut remaining_free_cross_space = container_cross_size - content_cross_size;
738
739 let num_lines = initial_line_layouts.len();
744 let resolved_align_content: AlignFlags = {
745 let align_content_style = flex_context.config.align_content.primary();
747 let mut is_safe = align_content_style.flags() == AlignFlags::SAFE;
748
749 let mut resolved_align_content = match align_content_style.value() {
752 AlignFlags::NORMAL => AlignFlags::STRETCH,
753 align_content => align_content,
754 };
755
756 let fallback_is_needed = match resolved_align_content {
764 _ if remaining_free_cross_space <= Au::zero() => true,
765 AlignFlags::STRETCH => num_lines < 1,
766 AlignFlags::SPACE_BETWEEN | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
767 num_lines < 2
768 },
769 _ => false,
770 };
771
772 if fallback_is_needed {
773 (resolved_align_content, is_safe) = match resolved_align_content {
774 AlignFlags::STRETCH => (AlignFlags::FLEX_START, false),
775 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
776 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
777 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
778 _ => (resolved_align_content, is_safe),
779 }
780 };
781
782 if remaining_free_cross_space <= Au::zero() && is_safe {
784 resolved_align_content = AlignFlags::START;
785 }
786
787 resolved_align_content
788 };
789
790 let flex_wrap_is_reversed = flex_context.config.flex_wrap_is_reversed;
792 let resolved_align_content = self
793 .config
794 .resolve_reversable_flex_alignment(resolved_align_content, flex_wrap_is_reversed);
795 let mut cross_start_position_cursor = match resolved_align_content {
796 AlignFlags::START if flex_wrap_is_reversed => remaining_free_cross_space,
797 AlignFlags::START => Au::zero(),
798 AlignFlags::END if flex_wrap_is_reversed => Au::zero(),
799 AlignFlags::END => remaining_free_cross_space,
800 AlignFlags::CENTER => remaining_free_cross_space / 2,
801 AlignFlags::STRETCH => Au::zero(),
802 AlignFlags::SPACE_BETWEEN => Au::zero(),
803 AlignFlags::SPACE_AROUND => remaining_free_cross_space / num_lines as i32 / 2,
804 AlignFlags::SPACE_EVENLY => remaining_free_cross_space / (num_lines as i32 + 1),
805
806 _ => Au::zero(),
808 };
809
810 let inline_axis_is_main_axis = self.config.flex_axis == FlexAxis::Row;
811 let mut baseline_alignment_participating_baselines = Baselines::default();
812 let mut all_baselines = Baselines::default();
813 let flex_item_fragments: Vec<_> = initial_line_layouts
814 .into_iter()
815 .enumerate()
816 .flat_map(|(index, initial_line_layout)| {
817 let (space_to_add_to_line, space_to_add_after_line) =
821 allocate_free_cross_space_for_flex_line(
822 resolved_align_content,
823 remaining_free_cross_space,
824 (num_lines - index) as i32,
825 );
826 remaining_free_cross_space -= space_to_add_to_line + space_to_add_after_line;
827
828 let final_line_cross_size =
829 initial_line_layout.line_size.cross + space_to_add_to_line;
830 let mut final_line_layout = initial_line_layout.finish_with_final_cross_size(
831 &mut flex_context,
832 main_gap,
833 final_line_cross_size,
834 );
835
836 let line_cross_start_position = cross_start_position_cursor;
837 cross_start_position_cursor = line_cross_start_position +
838 final_line_cross_size +
839 space_to_add_after_line +
840 cross_gap;
841
842 let flow_relative_line_position =
843 match (self.config.flex_axis, flex_wrap_is_reversed) {
844 (FlexAxis::Row, false) => LogicalVec2 {
845 block: line_cross_start_position,
846 inline: Au::zero(),
847 },
848 (FlexAxis::Row, true) => LogicalVec2 {
849 block: container_cross_size -
850 line_cross_start_position -
851 final_line_layout.cross_size,
852 inline: Au::zero(),
853 },
854 (FlexAxis::Column, false) => LogicalVec2 {
855 block: Au::zero(),
856 inline: line_cross_start_position,
857 },
858 (FlexAxis::Column, true) => LogicalVec2 {
859 block: Au::zero(),
860 inline: container_cross_size -
861 line_cross_start_position -
862 final_line_cross_size,
863 },
864 };
865
866 if inline_axis_is_main_axis {
867 let line_shared_alignment_baseline = final_line_layout
868 .shared_alignment_baseline
869 .map(|baseline| baseline + flow_relative_line_position.block);
870 if index == 0 {
871 baseline_alignment_participating_baselines.first =
872 line_shared_alignment_baseline;
873 }
874 if index == num_lines - 1 {
875 baseline_alignment_participating_baselines.last =
876 line_shared_alignment_baseline;
877 }
878 }
879
880 let line_all_baselines = final_line_layout
881 .all_baselines
882 .offset(flow_relative_line_position.block);
883 if index == 0 {
884 all_baselines.first = line_all_baselines.first;
885 }
886 if index == num_lines - 1 {
887 all_baselines.last = line_all_baselines.last;
888 }
889
890 let physical_line_position =
891 flow_relative_line_position.to_physical_size(self.style.writing_mode);
892 for (fragment, _) in &mut final_line_layout.item_fragments {
893 fragment.borrow_mut().base.rect.origin += physical_line_position;
894 }
895 final_line_layout.item_fragments
896 })
897 .collect();
898
899 let mut flex_item_fragments = flex_item_fragments.into_iter();
900 let fragments = absolutely_positioned_items_with_original_order
901 .into_iter()
902 .map(|child_as_abspos| match child_as_abspos {
903 FlexContent::AbsolutelyPositionedBox(absolutely_positioned_box) => self
904 .create_absolutely_positioned_flex_child_fragment(
905 absolutely_positioned_box,
906 containing_block,
907 container_size,
908 positioning_context,
909 ),
910 FlexContent::FlexItemPlaceholder => {
911 let (fragment, mut child_positioning_context) =
914 flex_item_fragments.next().unwrap();
915 let fragment = Fragment::Box(fragment);
916 child_positioning_context.adjust_static_position_of_hoisted_fragments(
917 &fragment,
918 PositioningContextLength::zero(),
919 );
920 positioning_context.append(child_positioning_context);
921 fragment
922 },
923 })
924 .collect::<Vec<_>>();
925
926 assert!(flex_item_fragments.next().is_none());
928
929 let baselines = Baselines {
930 first: baseline_alignment_participating_baselines
931 .first
932 .or(all_baselines.first),
933 last: baseline_alignment_participating_baselines
934 .last
935 .or(all_baselines.last),
936 };
937
938 let depends_on_block_constraints = true;
951
952 CacheableLayoutResult {
953 fragments,
954 content_block_size,
955 content_inline_size_for_table: None,
956 baselines,
957 depends_on_block_constraints,
958 specific_layout_info: None,
959 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
960 }
961 }
962
963 fn create_absolutely_positioned_flex_child_fragment(
979 &self,
980 absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
981 containing_block: &ContainingBlock,
982 container_size: FlexRelativeVec2<Au>,
983 positioning_context: &mut PositioningContext,
984 ) -> Fragment {
985 let alignment = {
986 let fragment = absolutely_positioned_box.borrow();
987 let make_flex_only_values_directional_for_absolutes =
988 |value: AlignFlags, reversed: bool| match (value.value(), reversed) {
989 (AlignFlags::NORMAL | AlignFlags::AUTO | AlignFlags::STRETCH, true) => {
990 AlignFlags::END | AlignFlags::SAFE
991 },
992 (AlignFlags::STRETCH, false) => AlignFlags::START | AlignFlags::SAFE,
993 (AlignFlags::SPACE_BETWEEN, false) => AlignFlags::START | AlignFlags::SAFE,
994 (AlignFlags::SPACE_BETWEEN, true) => AlignFlags::END | AlignFlags::SAFE,
995 _ => value,
996 };
997 let cross = make_flex_only_values_directional_for_absolutes(
998 self.config
999 .resolve_align_self_for_child(fragment.context.style()),
1000 self.config.flex_wrap_is_reversed,
1001 );
1002 let main = make_flex_only_values_directional_for_absolutes(
1003 self.config.resolve_justify_content_for_child(),
1004 self.config.flex_direction_is_reversed,
1005 );
1006
1007 FlexRelativeVec2 { cross, main }
1008 };
1009 let logical_alignment = self.config.flex_axis.vec2_to_flow_relative(alignment);
1010
1011 let static_position_rect = LogicalRect {
1012 start_corner: LogicalVec2::zero(),
1013 size: self.config.flex_axis.vec2_to_flow_relative(container_size),
1014 }
1015 .as_physical(Some(containing_block));
1016
1017 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
1018 absolutely_positioned_box,
1019 static_position_rect,
1020 logical_alignment,
1021 self.config.writing_mode,
1022 );
1023 let hoisted_fragment = hoisted_box.fragment.clone();
1024 positioning_context.push(hoisted_box);
1025 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
1026 }
1027
1028 #[inline]
1029 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
1030 LayoutStyle::Default(&self.style)
1031 }
1032
1033 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
1034 for child in &self.children {
1035 child.borrow_mut().with_base_mut(|base| {
1036 base.parent_box.replace(layout_box.clone());
1037 });
1038 }
1039 }
1040}
1041
1042fn allocate_free_cross_space_for_flex_line(
1046 resolved_align_content: AlignFlags,
1047 remaining_free_cross_space: Au,
1048 remaining_line_count: i32,
1049) -> (Au, Au) {
1050 if remaining_free_cross_space.is_zero() {
1051 return (Au::zero(), Au::zero());
1052 }
1053
1054 match resolved_align_content {
1055 AlignFlags::START => (Au::zero(), Au::zero()),
1056 AlignFlags::FLEX_START => (Au::zero(), Au::zero()),
1057 AlignFlags::END => (Au::zero(), Au::zero()),
1058 AlignFlags::FLEX_END => (Au::zero(), Au::zero()),
1059 AlignFlags::CENTER => (Au::zero(), Au::zero()),
1060 AlignFlags::STRETCH => (
1061 remaining_free_cross_space / remaining_line_count,
1062 Au::zero(),
1063 ),
1064 AlignFlags::SPACE_BETWEEN => {
1065 if remaining_line_count > 1 {
1066 (
1067 Au::zero(),
1068 remaining_free_cross_space / (remaining_line_count - 1),
1069 )
1070 } else {
1071 (Au::zero(), Au::zero())
1072 }
1073 },
1074 AlignFlags::SPACE_AROUND => (
1075 Au::zero(),
1076 remaining_free_cross_space / remaining_line_count,
1077 ),
1078 AlignFlags::SPACE_EVENLY => (
1079 Au::zero(),
1080 remaining_free_cross_space / (remaining_line_count + 1),
1081 ),
1082
1083 _ => (Au::zero(), Au::zero()),
1085 }
1086}
1087
1088impl<'a> FlexItem<'a> {
1089 fn new(flex_context: &FlexContext, box_: &'a FlexItemBox) -> Self {
1090 let containing_block = IndefiniteContainingBlock::from(flex_context.containing_block);
1091 let content_box_sizes_and_pbm = box_
1092 .independent_formatting_context
1093 .layout_style()
1094 .content_box_sizes_and_padding_border_margin(&containing_block);
1095 box_.to_flex_item(
1096 flex_context.layout_context,
1097 &containing_block,
1098 &content_box_sizes_and_pbm,
1099 &flex_context.config,
1100 &|| flex_context,
1101 )
1102 }
1103}
1104
1105fn cross_axis_is_item_block_axis(
1106 container_is_horizontal: bool,
1107 item_is_horizontal: bool,
1108 flex_axis: FlexAxis,
1109) -> bool {
1110 let item_is_orthogonal = item_is_horizontal != container_is_horizontal;
1111 let container_is_row = flex_axis == FlexAxis::Row;
1112
1113 container_is_row ^ item_is_orthogonal
1114}
1115
1116fn item_with_auto_cross_size_stretches_to_line_size(
1120 align_self: AlignItems,
1121 margin: &FlexRelativeSides<AuOrAuto>,
1122) -> bool {
1123 align_self.0.value() == AlignFlags::STRETCH &&
1124 !margin.cross_start.is_auto() &&
1125 !margin.cross_end.is_auto()
1126}
1127
1128fn do_initial_flex_line_layout<'items>(
1131 flex_context: &mut FlexContext,
1132 container_main_size: Au,
1133 mut items: Vec<FlexItem<'items>>,
1134 main_gap: Au,
1135) -> Vec<InitialFlexLineLayout<'items>> {
1136 let construct_line = |(items, outer_hypothetical_main_size)| {
1137 InitialFlexLineLayout::new(
1138 flex_context,
1139 items,
1140 outer_hypothetical_main_size,
1141 container_main_size,
1142 main_gap,
1143 )
1144 };
1145
1146 if flex_context.config.container_is_single_line {
1147 let outer_hypothetical_main_sizes_sum = items
1148 .iter()
1149 .map(|item| item.hypothetical_main_size + item.pbm_auto_is_zero.main)
1150 .sum();
1151 return vec![construct_line((items, outer_hypothetical_main_sizes_sum))];
1152 }
1153
1154 let mut lines = Vec::new();
1155 let mut line_size_so_far = Au::zero();
1156 let mut line_so_far_is_empty = true;
1157 let mut index = 0;
1158
1159 while let Some(item) = items.get(index) {
1160 let item_size = item.hypothetical_main_size + item.pbm_auto_is_zero.main;
1161 let mut line_size_would_be = line_size_so_far + item_size;
1162 if !line_so_far_is_empty {
1163 line_size_would_be += main_gap;
1164 }
1165 let item_fits = line_size_would_be <= container_main_size;
1166 if item_fits || line_so_far_is_empty {
1167 line_size_so_far = line_size_would_be;
1168 line_so_far_is_empty = false;
1169 index += 1;
1170 continue;
1171 }
1172
1173 let remaining = items.split_off(index);
1175 lines.push((items, line_size_so_far));
1176 items = remaining;
1177
1178 line_size_so_far = item_size;
1180 index = 1;
1181 }
1182
1183 lines.push((items, line_size_so_far));
1185
1186 if flex_context.layout_context.use_rayon {
1187 lines.par_drain(..).map(construct_line).collect()
1188 } else {
1189 lines.drain(..).map(construct_line).collect()
1190 }
1191}
1192
1193struct InitialFlexLineLayout<'a> {
1197 items: Vec<FlexLineItem<'a>>,
1199
1200 line_size: FlexRelativeVec2<Au>,
1202
1203 free_space_in_main_axis: Au,
1205}
1206
1207impl InitialFlexLineLayout<'_> {
1208 fn new<'items>(
1209 flex_context: &FlexContext,
1210 items: Vec<FlexItem<'items>>,
1211 outer_hypothetical_main_sizes_sum: Au,
1212 container_main_size: Au,
1213 main_gap: Au,
1214 ) -> InitialFlexLineLayout<'items> {
1215 let item_count = items.len();
1216 let (item_used_main_sizes, free_space_in_main_axis) = Self::resolve_flexible_lengths(
1217 &items,
1218 outer_hypothetical_main_sizes_sum,
1219 container_main_size - main_gap * (item_count as i32 - 1),
1220 );
1221
1222 let layout_results: Vec<_> = if flex_context.layout_context.use_rayon {
1224 items
1225 .par_iter()
1226 .zip(&item_used_main_sizes)
1227 .map(|(item, used_main_size)| item.layout(*used_main_size, flex_context, None))
1228 .collect()
1229 } else {
1230 items
1231 .iter()
1232 .zip(&item_used_main_sizes)
1233 .map(|(item, used_main_size)| item.layout(*used_main_size, flex_context, None))
1234 .collect()
1235 };
1236
1237 let items: Vec<_> = izip!(
1238 items.into_iter(),
1239 layout_results.into_iter(),
1240 item_used_main_sizes.into_iter()
1241 )
1242 .map(|(item, layout_result, used_main_size)| FlexLineItem {
1243 item,
1244 layout_result,
1245 used_main_size,
1246 })
1247 .collect();
1248
1249 let line_cross_size = Self::cross_size(&items, flex_context);
1251 let line_size = FlexRelativeVec2 {
1252 main: container_main_size,
1253 cross: line_cross_size,
1254 };
1255
1256 InitialFlexLineLayout {
1257 items,
1258 line_size,
1259 free_space_in_main_axis,
1260 }
1261 }
1262
1263 fn resolve_flexible_lengths<'items>(
1266 items: &'items [FlexItem<'items>],
1267 outer_hypothetical_main_sizes_sum: Au,
1268 container_main_size: Au,
1269 ) -> (Vec<Au>, Au) {
1270 struct FlexibleLengthResolutionItem<'items> {
1271 item: &'items FlexItem<'items>,
1272 frozen: Cell<bool>,
1273 target_main_size: Cell<Au>,
1274 flex_factor: f32,
1275 min_max_violation_kind: Cell<Ordering>,
1276 }
1277
1278 let grow = outer_hypothetical_main_sizes_sum < container_main_size;
1283
1284 let mut frozen_count = 0;
1285 let items: Vec<_> = items
1286 .iter()
1287 .map(|item| {
1288 let target_main_size = Cell::new(item.flex_base_size);
1291
1292 let flex_factor = if grow {
1299 item.box_.style().get_position().flex_grow.0
1300 } else {
1301 item.box_.style().get_position().flex_shrink.0
1302 };
1303
1304 let is_inflexible = flex_factor == 0. ||
1305 if grow {
1306 item.flex_base_size > item.hypothetical_main_size
1307 } else {
1308 item.flex_base_size < item.hypothetical_main_size
1309 };
1310
1311 let frozen = Cell::new(false);
1312 if is_inflexible {
1313 frozen_count += 1;
1314 frozen.set(true);
1315 target_main_size.set(item.hypothetical_main_size);
1316 }
1317
1318 FlexibleLengthResolutionItem {
1319 item,
1320 frozen,
1321 target_main_size,
1322 flex_factor,
1323 min_max_violation_kind: Cell::new(Ordering::Equal),
1325 }
1326 })
1327 .collect();
1328
1329 let unfrozen_items = || items.iter().filter(|item| !item.frozen.get());
1330 let main_sizes = |items: Vec<FlexibleLengthResolutionItem>| {
1331 items
1332 .into_iter()
1333 .map(|item| item.target_main_size.get())
1334 .collect()
1335 };
1336
1337 let free_space = |all_items_frozen| {
1342 let items_size = items
1343 .iter()
1344 .map(|item| {
1345 item.item.pbm_auto_is_zero.main +
1346 if all_items_frozen || item.frozen.get() {
1347 item.target_main_size.get()
1348 } else {
1349 item.item.flex_base_size
1350 }
1351 })
1352 .sum();
1353 container_main_size - items_size
1354 };
1355
1356 let initial_free_space = free_space(false);
1357 loop {
1358 let mut remaining_free_space = free_space(false);
1360 if frozen_count >= items.len() {
1363 return (main_sizes(items), remaining_free_space);
1364 }
1365
1366 let unfrozen_items_flex_factor_sum =
1372 unfrozen_items().map(|item| item.flex_factor).sum();
1373 if unfrozen_items_flex_factor_sum < 1. {
1374 let multiplied = initial_free_space.scale_by(unfrozen_items_flex_factor_sum);
1375 if multiplied.abs() < remaining_free_space.abs() {
1376 remaining_free_space = multiplied
1377 }
1378 }
1379
1380 if !remaining_free_space.is_zero() {
1386 if grow {
1392 for item in unfrozen_items() {
1393 let ratio = item.flex_factor / unfrozen_items_flex_factor_sum;
1394 item.target_main_size
1395 .set(item.item.flex_base_size + remaining_free_space.scale_by(ratio));
1396 }
1397 } else {
1406 let scaled_shrink_factor = |item: &FlexibleLengthResolutionItem| {
1408 item.item.flex_base_size.scale_by(item.flex_factor)
1409 };
1410 let scaled_shrink_factors_sum: Au =
1411 unfrozen_items().map(scaled_shrink_factor).sum();
1412 if scaled_shrink_factors_sum > Au::zero() {
1413 for item in unfrozen_items() {
1414 let ratio = scaled_shrink_factor(item).0 as f32 /
1415 scaled_shrink_factors_sum.0 as f32;
1416 item.target_main_size.set(
1417 item.item.flex_base_size -
1418 remaining_free_space.abs().scale_by(ratio),
1419 );
1420 }
1421 }
1422 }
1423 }
1424
1425 let mut total_violation = Au::zero();
1431 for item in unfrozen_items() {
1432 let unclamped = item.target_main_size.get();
1433 let clamped = unclamped.clamp_between_extremums(
1434 item.item.content_min_main_size,
1435 item.item.content_max_main_size,
1436 );
1437 item.target_main_size.set(clamped);
1438 item.min_max_violation_kind.set(clamped.cmp(&unclamped));
1441 total_violation += clamped - unclamped;
1442 }
1443
1444 match total_violation.cmp(&Au::zero()) {
1451 Ordering::Equal => {
1452 let remaining_free_space = free_space(true);
1455 return (main_sizes(items), remaining_free_space);
1456 },
1457 total_violation_kind => {
1458 for item in unfrozen_items() {
1459 if item.min_max_violation_kind.get() == total_violation_kind {
1460 item.frozen.set(true);
1461 frozen_count += 1;
1462 }
1463 }
1464 },
1465 }
1466 }
1467 }
1468
1469 fn cross_size<'items>(items: &'items [FlexLineItem<'items>], flex_context: &FlexContext) -> Au {
1471 if flex_context.config.container_is_single_line {
1472 if let SizeConstraint::Definite(size) =
1473 flex_context.container_inner_size_constraint.cross
1474 {
1475 return size;
1476 }
1477 }
1478
1479 let mut max_ascent = Au::zero();
1480 let mut max_descent = Au::zero();
1481 let mut max_outer_hypothetical_cross_size = Au::zero();
1482 for item in items.iter() {
1483 if matches!(
1485 item.item.align_self.0.value(),
1486 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1487 ) {
1488 let baseline = item.get_or_synthesize_baseline_with_cross_size(
1489 item.layout_result.hypothetical_cross_size,
1490 );
1491 let hypothetical_margin_box_cross_size =
1492 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross;
1493 max_ascent = max_ascent.max(baseline);
1494 max_descent = max_descent.max(hypothetical_margin_box_cross_size - baseline);
1495 } else {
1496 max_outer_hypothetical_cross_size = max_outer_hypothetical_cross_size.max(
1497 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross,
1498 );
1499 }
1500 }
1501
1502 let largest = max_outer_hypothetical_cross_size.max(max_ascent + max_descent);
1504 match flex_context.container_inner_size_constraint.cross {
1505 SizeConstraint::MinMax(min, max) if flex_context.config.container_is_single_line => {
1506 largest.clamp_between_extremums(min, max)
1507 },
1508 _ => largest,
1509 }
1510 }
1511
1512 fn finish_with_final_cross_size(
1513 mut self,
1514 flex_context: &mut FlexContext,
1515 main_gap: Au,
1516 final_line_cross_size: Au,
1517 ) -> FinalFlexLineLayout {
1518 let auto_margins_count = self
1526 .items
1527 .iter()
1528 .map(|item| {
1529 item.item.margin.main_start.is_auto() as u32 +
1530 item.item.margin.main_end.is_auto() as u32
1531 })
1532 .sum::<u32>();
1533 let (space_distributed_to_auto_main_margins, free_space_in_main_axis) =
1534 if self.free_space_in_main_axis > Au::zero() && auto_margins_count > 0 {
1535 (
1536 self.free_space_in_main_axis / auto_margins_count as i32,
1537 Au::zero(),
1538 )
1539 } else {
1540 (Au::zero(), self.free_space_in_main_axis)
1541 };
1542
1543 let item_count = self.items.len();
1546 let mut shared_alignment_baseline = None;
1547 let mut item_used_cross_sizes = Vec::with_capacity(item_count);
1548 let mut item_margins = Vec::with_capacity(item_count);
1549 for item in self.items.iter_mut() {
1550 let cross_axis = match flex_context.config.flex_axis {
1551 FlexAxis::Row => Direction::Block,
1552 FlexAxis::Column => Direction::Inline,
1553 };
1554 let layout = &mut item.layout_result;
1555 let get_content_size = || match cross_axis {
1556 Direction::Block => layout.content_block_size.into(),
1557 Direction::Inline => item
1558 .item
1559 .inline_content_sizes(flex_context, item.used_main_size),
1560 };
1561 let used_cross_size = item.item.content_cross_sizes.resolve(
1562 cross_axis,
1563 item.item.automatic_cross_size,
1564 Au::zero,
1565 Some(Au::zero().max(final_line_cross_size - item.item.pbm_auto_is_zero.cross)),
1566 get_content_size,
1567 item.item.box_.independent_formatting_context.is_table() &&
1572 cross_axis == Direction::Inline,
1573 );
1574 item_used_cross_sizes.push(used_cross_size);
1575
1576 let needs_new_layout = match cross_axis {
1583 Direction::Block => {
1584 (match item.item.content_cross_sizes.preferred {
1585 Size::Initial => item.item.automatic_cross_size == Size::Stretch,
1586 Size::Stretch => true,
1587 _ => false,
1588 }) && SizeConstraint::Definite(used_cross_size) !=
1589 layout.containing_block_size.block &&
1590 layout.depends_on_block_constraints
1591 },
1592 Direction::Inline => used_cross_size != layout.containing_block_size.inline,
1593 };
1594 if needs_new_layout {
1595 #[cfg(feature = "tracing")]
1596 tracing::warn!(
1597 name: "Flex item stretch cache miss",
1598 cached_inline = ?layout.containing_block_size.inline,
1599 cached_block = ?layout.containing_block_size.block,
1600 required_cross_size = ?used_cross_size,
1601 cross_axis = ?cross_axis,
1602 depends_on_block_constraints = layout.depends_on_block_constraints,
1603 );
1604 *layout =
1605 item.item
1606 .layout(item.used_main_size, flex_context, Some(used_cross_size));
1607 }
1608
1609 let baseline = item.get_or_synthesize_baseline_with_cross_size(used_cross_size);
1610 if matches!(
1611 item.item.align_self.0.value(),
1612 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1613 ) {
1614 shared_alignment_baseline =
1615 Some(shared_alignment_baseline.unwrap_or(baseline).max(baseline));
1616 }
1617 item.layout_result
1618 .flex_alignment_baseline_relative_to_margin_box = Some(baseline);
1619
1620 item_margins.push(item.item.resolve_auto_margins(
1621 flex_context,
1622 final_line_cross_size,
1623 used_cross_size,
1624 space_distributed_to_auto_main_margins,
1625 ));
1626 }
1627
1628 let resolved_justify_content: AlignFlags = {
1634 let justify_content_style = flex_context.config.justify_content.primary();
1635
1636 let mut resolved_justify_content = justify_content_style.value();
1638 let mut is_safe = justify_content_style.flags() == AlignFlags::SAFE;
1639
1640 if item_count <= 1 || free_space_in_main_axis <= Au::zero() {
1645 (resolved_justify_content, is_safe) = match resolved_justify_content {
1646 AlignFlags::STRETCH => (AlignFlags::FLEX_START, true),
1647 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
1648 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
1649 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
1650 _ => (resolved_justify_content, is_safe),
1651 }
1652 };
1653
1654 if free_space_in_main_axis <= Au::zero() && is_safe {
1656 resolved_justify_content = AlignFlags::START;
1657 }
1658
1659 resolved_justify_content
1660 };
1661
1662 let main_start_position = match resolved_justify_content {
1664 AlignFlags::START => Au::zero(),
1665 AlignFlags::FLEX_START => {
1666 if flex_context.config.flex_direction_is_reversed {
1667 free_space_in_main_axis
1668 } else {
1669 Au::zero()
1670 }
1671 },
1672 AlignFlags::END => free_space_in_main_axis,
1673 AlignFlags::FLEX_END => {
1674 if flex_context.config.flex_direction_is_reversed {
1675 Au::zero()
1676 } else {
1677 free_space_in_main_axis
1678 }
1679 },
1680 AlignFlags::CENTER => free_space_in_main_axis / 2,
1681 AlignFlags::STRETCH => Au::zero(),
1682 AlignFlags::SPACE_BETWEEN => Au::zero(),
1683 AlignFlags::SPACE_AROUND => (free_space_in_main_axis / item_count as i32) / 2,
1684 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1685
1686 _ => Au::zero(),
1688 };
1689
1690 let item_main_interval = match resolved_justify_content {
1691 AlignFlags::START => Au::zero(),
1692 AlignFlags::FLEX_START => Au::zero(),
1693 AlignFlags::END => Au::zero(),
1694 AlignFlags::FLEX_END => Au::zero(),
1695 AlignFlags::CENTER => Au::zero(),
1696 AlignFlags::STRETCH => Au::zero(),
1697 AlignFlags::SPACE_BETWEEN => free_space_in_main_axis / (item_count - 1) as i32,
1698 AlignFlags::SPACE_AROUND => free_space_in_main_axis / item_count as i32,
1699 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1700
1701 _ => Au::zero(),
1703 };
1704 let item_main_interval = item_main_interval + main_gap;
1705
1706 let mut all_baselines = Baselines::default();
1707 let mut main_position_cursor = main_start_position;
1708
1709 let items = std::mem::take(&mut self.items);
1710 let item_fragments = izip!(items, item_margins, item_used_cross_sizes.iter())
1711 .map(|(item, item_margin, item_used_cross_size)| {
1712 let item_used_size = FlexRelativeVec2 {
1713 main: item.used_main_size,
1714 cross: *item_used_cross_size,
1715 };
1716 item.collect_fragment(
1717 &self,
1718 item_used_size,
1719 item_margin,
1720 item_main_interval,
1721 final_line_cross_size,
1722 &shared_alignment_baseline,
1723 flex_context,
1724 &mut all_baselines,
1725 &mut main_position_cursor,
1726 )
1727 })
1728 .collect();
1729
1730 FinalFlexLineLayout {
1731 cross_size: final_line_cross_size,
1732 item_fragments,
1733 all_baselines,
1734 shared_alignment_baseline,
1735 }
1736 }
1737}
1738
1739impl FlexItem<'_> {
1740 #[servo_tracing::instrument(
1745 name = "FlexItem::layout",
1746 skip_all,
1747 fields(
1748 self_address = self as *const _ as usize,
1749 box_address = self.box_ as *const _ as usize,
1750 )
1751 )]
1752 #[allow(clippy::too_many_arguments)]
1753 fn layout(
1754 &self,
1755 used_main_size: Au,
1756 flex_context: &FlexContext,
1757 used_cross_size_override: Option<Au>,
1758 ) -> FlexItemLayoutResult {
1759 let containing_block = flex_context.containing_block;
1760 let independent_formatting_context = &self.box_.independent_formatting_context;
1761 let is_table = independent_formatting_context.is_table();
1762 let mut positioning_context = PositioningContext::default();
1763 let item_writing_mode = independent_formatting_context.style().writing_mode;
1764 let item_is_horizontal = item_writing_mode.is_horizontal();
1765 let flex_axis = flex_context.config.flex_axis;
1766 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
1767 containing_block.style.writing_mode.is_horizontal(),
1768 item_is_horizontal,
1769 flex_axis,
1770 );
1771
1772 let (inline_size, block_size) = if cross_axis_is_item_block_axis {
1773 let cross_size = match used_cross_size_override {
1774 Some(s) => SizeConstraint::Definite(s),
1775 None => {
1776 let stretch_size = containing_block
1777 .size
1778 .block
1779 .to_definite()
1780 .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
1781 let tentative_block_content_size = independent_formatting_context
1782 .tentative_block_content_size(self.preferred_aspect_ratio);
1783 if let Some(block_content_size) = tentative_block_content_size {
1784 SizeConstraint::Definite(self.content_cross_sizes.resolve(
1785 Direction::Block,
1786 Size::FitContent,
1787 Au::zero,
1788 stretch_size,
1789 || block_content_size,
1790 is_table,
1791 ))
1792 } else {
1793 self.content_cross_sizes.resolve_extrinsic(
1794 Size::FitContent,
1795 Au::zero(),
1796 stretch_size,
1797 )
1798 }
1799 },
1800 };
1801 (used_main_size, cross_size)
1802 } else {
1803 let cross_size = used_cross_size_override.unwrap_or_else(|| {
1804 let stretch_size =
1805 Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.cross);
1806 self.content_cross_sizes.resolve(
1807 Direction::Inline,
1808 Size::FitContent,
1809 Au::zero,
1810 Some(stretch_size),
1811 || self.inline_content_sizes(flex_context, used_main_size),
1812 is_table,
1813 )
1814 });
1815 let main_size = if self.flex_base_size_is_definite ||
1819 flex_context
1820 .container_inner_size_constraint
1821 .main
1822 .is_definite()
1823 {
1824 SizeConstraint::Definite(used_main_size)
1825 } else {
1826 SizeConstraint::default()
1827 };
1828 (cross_size, main_size)
1829 };
1830
1831 let item_style = independent_formatting_context.style();
1832 let item_as_containing_block = ContainingBlock {
1833 size: ContainingBlockSize {
1834 inline: inline_size,
1835 block: block_size,
1836 },
1837 style: item_style,
1838 };
1839
1840 let lazy_block_size = if !cross_axis_is_item_block_axis {
1841 used_main_size.into()
1842 } else if let Some(cross_size) = used_cross_size_override {
1843 cross_size.into()
1844 } else {
1845 let stretch_size = containing_block
1846 .size
1847 .block
1848 .to_definite()
1849 .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
1850 LazySize::new(
1851 &self.content_cross_sizes,
1852 Direction::Block,
1853 Size::FitContent,
1854 Au::zero,
1855 stretch_size,
1856 is_table,
1857 )
1858 };
1859
1860 let layout = independent_formatting_context.layout(
1861 flex_context.layout_context,
1862 &mut positioning_context,
1863 &item_as_containing_block,
1864 containing_block,
1865 self.preferred_aspect_ratio,
1866 &lazy_block_size,
1867 );
1868 let CacheableLayoutResult {
1869 fragments,
1870 content_block_size,
1871 baselines: content_box_baselines,
1872 depends_on_block_constraints,
1873 specific_layout_info,
1874 ..
1875 } = layout;
1876
1877 let hypothetical_cross_size = if cross_axis_is_item_block_axis {
1878 lazy_block_size.resolve(|| content_block_size)
1879 } else {
1880 inline_size
1881 };
1882
1883 let item_writing_mode_is_orthogonal_to_container_writing_mode =
1884 flex_context.config.writing_mode.is_horizontal() !=
1885 item_style.writing_mode.is_horizontal();
1886 let has_compatible_baseline = match flex_axis {
1887 FlexAxis::Row => !item_writing_mode_is_orthogonal_to_container_writing_mode,
1888 FlexAxis::Column => item_writing_mode_is_orthogonal_to_container_writing_mode,
1889 };
1890
1891 let content_baselines_relative_to_margin_box = if has_compatible_baseline {
1892 content_box_baselines.offset(
1893 self.margin.cross_start.auto_is(Au::zero) +
1894 self.padding.cross_start +
1895 self.border.cross_start,
1896 )
1897 } else {
1898 Baselines::default()
1899 };
1900
1901 let flex_alignment_baseline_relative_to_margin_box = match self.align_self.0.value() {
1902 AlignFlags::BASELINE => content_baselines_relative_to_margin_box.first,
1904 AlignFlags::LAST_BASELINE => content_baselines_relative_to_margin_box.last,
1905 _ => None,
1906 };
1907
1908 FlexItemLayoutResult {
1909 hypothetical_cross_size,
1910 fragments,
1911 positioning_context,
1912 content_baselines_relative_to_margin_box,
1913 flex_alignment_baseline_relative_to_margin_box,
1914 content_block_size,
1915 containing_block_size: item_as_containing_block.size,
1916 depends_on_block_constraints,
1917 specific_layout_info,
1918 }
1919 }
1920
1921 fn synthesized_baseline_relative_to_margin_box(&self, content_size: Au) -> Au {
1922 content_size +
1926 self.margin.cross_start.auto_is(Au::zero) +
1927 self.padding.cross_start +
1928 self.border.cross_start +
1929 self.border.cross_end +
1930 self.padding.cross_end
1931 }
1932
1933 fn resolve_auto_margins(
1939 &self,
1940 flex_context: &FlexContext,
1941 line_cross_size: Au,
1942 item_cross_content_size: Au,
1943 space_distributed_to_auto_main_margins: Au,
1944 ) -> FlexRelativeSides<Au> {
1945 let main_start = self
1946 .margin
1947 .main_start
1948 .auto_is(|| space_distributed_to_auto_main_margins);
1949 let main_end = self
1950 .margin
1951 .main_end
1952 .auto_is(|| space_distributed_to_auto_main_margins);
1953
1954 let auto_count = match (self.margin.cross_start, self.margin.cross_end) {
1955 (AuOrAuto::LengthPercentage(cross_start), AuOrAuto::LengthPercentage(cross_end)) => {
1956 return FlexRelativeSides {
1957 cross_start,
1958 cross_end,
1959 main_start,
1960 main_end,
1961 };
1962 },
1963 (AuOrAuto::Auto, AuOrAuto::Auto) => 2,
1964 _ => 1,
1965 };
1966 let outer_cross_size = self.pbm_auto_is_zero.cross + item_cross_content_size;
1967 let available = line_cross_size - outer_cross_size;
1968 let cross_start;
1969 let cross_end;
1970 if available > Au::zero() {
1971 let each_auto_margin = available / auto_count;
1972 cross_start = self.margin.cross_start.auto_is(|| each_auto_margin);
1973 cross_end = self.margin.cross_end.auto_is(|| each_auto_margin);
1974 } else {
1975 let flex_wrap = flex_context.containing_block.style.get_position().flex_wrap;
1988 let flex_wrap_reverse = match flex_wrap {
1989 FlexWrap::Nowrap | FlexWrap::Wrap => false,
1990 FlexWrap::WrapReverse => true,
1991 };
1992 if flex_wrap_reverse {
1996 cross_start = self.margin.cross_start.auto_is(|| available);
1997 cross_end = self.margin.cross_end.auto_is(Au::zero);
1998 } else {
1999 cross_start = self.margin.cross_start.auto_is(Au::zero);
2000 cross_end = self.margin.cross_end.auto_is(|| available);
2001 }
2002 }
2003
2004 FlexRelativeSides {
2005 cross_start,
2006 cross_end,
2007 main_start,
2008 main_end,
2009 }
2010 }
2011
2012 fn align_along_cross_axis(
2014 &self,
2015 margin: &FlexRelativeSides<Au>,
2016 used_cross_size: &Au,
2017 line_cross_size: Au,
2018 propagated_baseline: Au,
2019 max_propagated_baseline: Au,
2020 wrap_reverse: bool,
2021 ) -> Au {
2022 let ending_alignment = line_cross_size - *used_cross_size - self.pbm_auto_is_zero.cross;
2023 let outer_cross_start =
2024 if self.margin.cross_start.is_auto() || self.margin.cross_end.is_auto() {
2025 Au::zero()
2026 } else {
2027 match self.align_self.0.value() {
2028 AlignFlags::STRETCH => Au::zero(),
2029 AlignFlags::CENTER => ending_alignment / 2,
2030 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE => {
2031 max_propagated_baseline - propagated_baseline
2032 },
2033 AlignFlags::START => {
2034 if !wrap_reverse {
2035 Au::zero()
2036 } else {
2037 ending_alignment
2038 }
2039 },
2040 AlignFlags::END => {
2041 if !wrap_reverse {
2042 ending_alignment
2043 } else {
2044 Au::zero()
2045 }
2046 },
2047 _ => Au::zero(),
2048 }
2049 };
2050 outer_cross_start + margin.cross_start + self.border.cross_start + self.padding.cross_start
2051 }
2052
2053 #[inline]
2054 fn inline_content_sizes(&self, flex_context: &FlexContext, block_size: Au) -> ContentSizes {
2055 self.box_.inline_content_sizes(
2056 flex_context,
2057 SizeConstraint::Definite(block_size),
2058 self.preferred_aspect_ratio,
2059 )
2060 }
2061}
2062
2063impl FlexItemBox {
2064 fn to_flex_item<'a>(
2065 &self,
2066 layout_context: &LayoutContext,
2067 containing_block: &IndefiniteContainingBlock,
2068 content_box_sizes_and_pbm: &ContentBoxSizesAndPBM,
2069 config: &FlexContainerConfig,
2070 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2071 ) -> FlexItem<'_> {
2072 let flex_axis = config.flex_axis;
2073 let style = self.style();
2074 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
2075 containing_block.style.writing_mode.is_horizontal(),
2076 style.writing_mode.is_horizontal(),
2077 flex_axis,
2078 );
2079 let main_axis = if cross_axis_is_item_block_axis {
2080 Direction::Inline
2081 } else {
2082 Direction::Block
2083 };
2084 let align_self = AlignItems(config.resolve_align_self_for_child(style));
2085
2086 let ContentBoxSizesAndPBM {
2087 content_box_sizes,
2088 pbm,
2089 depends_on_block_constraints,
2090 preferred_size_computes_to_auto,
2091 } = content_box_sizes_and_pbm;
2092
2093 let preferred_aspect_ratio = self
2094 .independent_formatting_context
2095 .preferred_aspect_ratio(&pbm.padding_border_sums);
2096 let padding = config.sides_to_flex_relative(pbm.padding);
2097 let border = config.sides_to_flex_relative(pbm.border);
2098 let margin = config.sides_to_flex_relative(pbm.margin);
2099 let padding_border = padding.sum_by_axis() + border.sum_by_axis();
2100 let margin_auto_is_zero = config.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
2101 let pbm_auto_is_zero = FlexRelativeVec2 {
2102 main: padding_border.main,
2103 cross: padding_border.cross,
2104 } + margin_auto_is_zero.sum_by_axis();
2105 let (content_main_sizes, content_cross_sizes, cross_size_computes_to_auto) = match flex_axis
2106 {
2107 FlexAxis::Row => (
2108 &content_box_sizes.inline,
2109 &content_box_sizes.block,
2110 preferred_size_computes_to_auto.block,
2111 ),
2112 FlexAxis::Column => (
2113 &content_box_sizes.block,
2114 &content_box_sizes.inline,
2115 preferred_size_computes_to_auto.inline,
2116 ),
2117 };
2118 let automatic_cross_size = if cross_size_computes_to_auto &&
2119 item_with_auto_cross_size_stretches_to_line_size(align_self, &margin)
2120 {
2121 Size::Stretch
2122 } else {
2123 Size::FitContent
2124 };
2125 let automatic_cross_size_for_intrinsic_sizing = if config.container_is_single_line {
2126 automatic_cross_size
2127 } else {
2128 Size::FitContent
2129 };
2130 let containing_block_size = flex_axis.vec2_to_flex_relative(containing_block.size);
2131 let stretch_size = FlexRelativeVec2 {
2132 main: containing_block_size
2133 .main
2134 .map(|v| Au::zero().max(v - pbm_auto_is_zero.main)),
2135 cross: containing_block_size
2136 .cross
2137 .map(|v| Au::zero().max(v - pbm_auto_is_zero.cross)),
2138 };
2139
2140 let is_table = self.independent_formatting_context.is_table();
2141 let tentative_cross_content_size = if cross_axis_is_item_block_axis {
2142 self.independent_formatting_context
2143 .tentative_block_content_size(preferred_aspect_ratio)
2144 } else {
2145 None
2146 };
2147 let (preferred_cross_size, min_cross_size, max_cross_size) =
2148 if let Some(cross_content_size) = tentative_cross_content_size {
2149 let (preferred, min, max) = content_cross_sizes.resolve_each(
2150 automatic_cross_size_for_intrinsic_sizing,
2151 Au::zero,
2152 stretch_size.cross,
2153 || cross_content_size,
2154 is_table,
2155 );
2156 (Some(preferred), min, max)
2157 } else {
2158 content_cross_sizes.resolve_each_extrinsic(
2159 automatic_cross_size_for_intrinsic_sizing,
2160 Au::zero(),
2161 stretch_size.cross,
2162 )
2163 };
2164 let cross_size = SizeConstraint::new(preferred_cross_size, min_cross_size, max_cross_size);
2165
2166 let transferred_size_suggestion =
2171 LazyCell::new(|| match (preferred_aspect_ratio, cross_size) {
2172 (Some(ratio), SizeConstraint::Definite(cross_size)) => {
2173 Some(ratio.compute_dependent_size(main_axis, cross_size))
2174 },
2175 _ => None,
2176 });
2177
2178 let flex_base_size_is_definite = Cell::new(true);
2180 let main_content_sizes = LazyCell::new(|| {
2181 let flex_item = &self.independent_formatting_context;
2182 if let Some(transferred_size_suggestion) = *transferred_size_suggestion {
2188 return transferred_size_suggestion.into();
2189 }
2190
2191 flex_base_size_is_definite.set(false);
2192
2193 if cross_axis_is_item_block_axis {
2202 let constraint_space =
2205 ConstraintSpace::new(cross_size, style, preferred_aspect_ratio);
2206 let content_sizes = flex_item
2207 .inline_content_sizes(layout_context, &constraint_space)
2208 .sizes;
2209 if let Some(ratio) = preferred_aspect_ratio {
2210 let transferred_min = ratio.compute_dependent_size(main_axis, min_cross_size);
2211 let transferred_max =
2212 max_cross_size.map(|v| ratio.compute_dependent_size(main_axis, v));
2213 content_sizes
2214 .map(|size| size.clamp_between_extremums(transferred_min, transferred_max))
2215 } else {
2216 content_sizes
2217 }
2218 } else {
2219 self.layout_for_block_content_size(
2220 flex_context_getter(),
2221 &pbm_auto_is_zero,
2222 content_box_sizes,
2223 preferred_aspect_ratio,
2224 automatic_cross_size_for_intrinsic_sizing,
2225 IntrinsicSizingMode::Size,
2226 )
2227 .into()
2228 }
2229 });
2230
2231 let flex_base_size = self
2232 .flex_basis(
2233 containing_block_size.main,
2234 content_main_sizes.preferred,
2235 padding_border.main,
2236 )
2237 .resolve_for_preferred(Size::MaxContent, stretch_size.main, &main_content_sizes);
2238 let flex_base_size_is_definite = flex_base_size_is_definite.take();
2239
2240 let content_max_main_size = content_main_sizes
2241 .max
2242 .resolve_for_max(stretch_size.main, &main_content_sizes);
2243
2244 let get_automatic_minimum_size = || {
2245 if style.establishes_scroll_container(self.base_fragment_info().flags) {
2247 return Au::zero();
2248 }
2249
2250 let specified_size_suggestion = content_main_sizes
2254 .preferred
2255 .maybe_resolve_extrinsic(stretch_size.main);
2256
2257 let is_replaced = self.independent_formatting_context.is_replaced();
2258
2259 let content_size_suggestion = match preferred_aspect_ratio {
2264 Some(ratio) => main_content_sizes.min_content.clamp_between_extremums(
2265 ratio.compute_dependent_size(main_axis, min_cross_size),
2266 max_cross_size.map(|l| ratio.compute_dependent_size(main_axis, l)),
2267 ),
2268 None => main_content_sizes.min_content,
2269 };
2270
2271 match (specified_size_suggestion, *transferred_size_suggestion) {
2278 (Some(specified), _) => specified.min(content_size_suggestion),
2279 (_, Some(transferred)) if is_replaced => transferred.min(content_size_suggestion),
2280 _ => content_size_suggestion,
2281 }
2282 .clamp_below_max(content_max_main_size)
2283 };
2284 let content_min_main_size = content_main_sizes.min.resolve_for_min(
2285 get_automatic_minimum_size,
2286 stretch_size.main,
2287 &main_content_sizes,
2288 is_table,
2289 );
2290
2291 FlexItem {
2292 box_: self,
2293 content_cross_sizes: content_cross_sizes.clone(),
2294 padding,
2295 border,
2296 margin: config.sides_to_flex_relative(pbm.margin),
2297 pbm_auto_is_zero,
2298 flex_base_size,
2299 flex_base_size_is_definite,
2300 hypothetical_main_size: flex_base_size
2301 .clamp_between_extremums(content_min_main_size, content_max_main_size),
2302 content_min_main_size,
2303 content_max_main_size,
2304 align_self,
2305 depends_on_block_constraints: *depends_on_block_constraints,
2306 preferred_aspect_ratio,
2307 automatic_cross_size,
2308 automatic_cross_size_for_intrinsic_sizing,
2309 }
2310 }
2311
2312 fn main_content_size_info<'a>(
2313 &self,
2314 layout_context: &LayoutContext,
2315 containing_block: &IndefiniteContainingBlock,
2316 config: &FlexContainerConfig,
2317 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2318 ) -> FlexItemBoxInlineContentSizesInfo {
2319 let content_box_sizes_and_pbm = self
2320 .independent_formatting_context
2321 .layout_style()
2322 .content_box_sizes_and_padding_border_margin(containing_block);
2323
2324 let FlexItem {
2327 flex_base_size,
2328 content_min_main_size,
2329 content_max_main_size,
2330 pbm_auto_is_zero,
2331 preferred_aspect_ratio,
2332 automatic_cross_size_for_intrinsic_sizing,
2333 ..
2334 } = self.to_flex_item(
2335 layout_context,
2336 containing_block,
2337 &content_box_sizes_and_pbm,
2338 config,
2339 flex_context_getter,
2340 );
2341
2342 let (content_contribution_sizes, depends_on_block_constraints) = match config.flex_axis {
2345 FlexAxis::Row => {
2346 let auto_minimum = LogicalVec2 {
2347 inline: content_min_main_size,
2348 block: Au::zero(),
2349 };
2350 let InlineContentSizesResult {
2351 sizes,
2352 depends_on_block_constraints,
2353 } = self
2354 .independent_formatting_context
2355 .outer_inline_content_sizes(
2356 layout_context,
2357 containing_block,
2358 &auto_minimum,
2359 automatic_cross_size_for_intrinsic_sizing == Size::Stretch,
2360 );
2361 (sizes, depends_on_block_constraints)
2362 },
2363 FlexAxis::Column => {
2364 let size = self.layout_for_block_content_size(
2365 flex_context_getter(),
2366 &pbm_auto_is_zero,
2367 &content_box_sizes_and_pbm.content_box_sizes,
2368 preferred_aspect_ratio,
2369 automatic_cross_size_for_intrinsic_sizing,
2370 IntrinsicSizingMode::Contribution,
2371 );
2372 (size.into(), true)
2373 },
2374 };
2375
2376 let outer_flex_base_size = flex_base_size + pbm_auto_is_zero.main;
2377 let outer_min_main_size = content_min_main_size + pbm_auto_is_zero.main;
2378 let outer_max_main_size = content_max_main_size.map(|v| v + pbm_auto_is_zero.main);
2379 let max_flex_factors = self.desired_flex_factors_for_preferred_width(
2380 content_contribution_sizes.max_content,
2381 flex_base_size,
2382 outer_flex_base_size,
2383 );
2384
2385 let min_flex_factors = self.desired_flex_factors_for_preferred_width(
2389 content_contribution_sizes.min_content,
2390 flex_base_size,
2391 outer_flex_base_size,
2392 );
2393
2394 let mut min_content_main_size_for_multiline_container =
2401 content_contribution_sizes.min_content;
2402 let style_position = &self.style().get_position();
2403 if style_position.flex_grow.is_zero() {
2404 min_content_main_size_for_multiline_container.min_assign(outer_flex_base_size);
2405 }
2406 if style_position.flex_shrink.is_zero() {
2407 min_content_main_size_for_multiline_container.max_assign(outer_flex_base_size);
2408 }
2409 min_content_main_size_for_multiline_container =
2410 min_content_main_size_for_multiline_container
2411 .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
2412
2413 FlexItemBoxInlineContentSizesInfo {
2414 outer_flex_base_size,
2415 outer_min_main_size,
2416 outer_max_main_size,
2417 min_flex_factors,
2418 max_flex_factors,
2419 min_content_main_size_for_multiline_container,
2420 depends_on_block_constraints,
2421 }
2422 }
2423
2424 fn desired_flex_factors_for_preferred_width(
2425 &self,
2426 preferred_width: Au,
2427 flex_base_size: Au,
2428 outer_flex_base_size: Au,
2429 ) -> DesiredFlexFractionAndGrowOrShrinkFactor {
2430 let difference = (preferred_width - outer_flex_base_size).to_f32_px();
2431 let (flex_grow_or_scaled_flex_shrink_factor, desired_flex_fraction) = if difference > 0.0 {
2432 let flex_grow_factor = self.style().get_position().flex_grow.0;
2436
2437 (
2438 flex_grow_factor,
2439 if flex_grow_factor >= 1.0 {
2440 difference / flex_grow_factor
2441 } else {
2442 difference * flex_grow_factor
2443 },
2444 )
2445 } else if difference < 0.0 {
2446 let flex_shrink_factor = self.style().get_position().flex_shrink.0;
2450 let scaled_flex_shrink_factor = flex_shrink_factor * flex_base_size.to_f32_px();
2451
2452 (
2453 scaled_flex_shrink_factor,
2454 if scaled_flex_shrink_factor != 0.0 {
2455 difference / scaled_flex_shrink_factor
2456 } else {
2457 f32::NEG_INFINITY
2458 },
2459 )
2460 } else {
2461 (0.0, 0.0)
2462 };
2463
2464 DesiredFlexFractionAndGrowOrShrinkFactor {
2465 desired_flex_fraction,
2466 flex_grow_or_shrink_factor: flex_grow_or_scaled_flex_shrink_factor,
2467 }
2468 }
2469
2470 fn flex_basis(
2476 &self,
2477 container_definite_main_size: Option<Au>,
2478 main_preferred_size: Size<Au>,
2479 main_padding_border_sum: Au,
2480 ) -> Size<Au> {
2481 let style_position = &self.independent_formatting_context.style().get_position();
2482 match &style_position.flex_basis {
2483 FlexBasis::Content => Size::Initial,
2486
2487 FlexBasis::Size(size) => match Size::<LengthPercentage>::from(size.clone()) {
2488 Size::Initial => main_preferred_size,
2492
2493 size => {
2503 let apply_box_sizing = |length: Au| {
2504 match style_position.box_sizing {
2505 BoxSizing::ContentBox => length,
2506 BoxSizing::BorderBox => length - main_padding_border_sum,
2509 }
2510 };
2511 size.resolve_percentages_for_preferred(container_definite_main_size)
2512 .map(apply_box_sizing)
2513 },
2514 },
2515 }
2516 }
2517
2518 #[allow(clippy::too_many_arguments)]
2519 #[servo_tracing::instrument(name = "FlexContainer::layout_for_block_content_size", skip_all)]
2520 fn layout_for_block_content_size(
2521 &self,
2522 flex_context: &FlexContext,
2523 pbm_auto_is_zero: &FlexRelativeVec2<Au>,
2524 content_box_sizes: &LogicalVec2<Sizes>,
2525 preferred_aspect_ratio: Option<AspectRatio>,
2526 automatic_inline_size: Size<Au>,
2527 intrinsic_sizing_mode: IntrinsicSizingMode,
2528 ) -> Au {
2529 let content_block_size = || {
2530 let mut positioning_context = PositioningContext::default();
2531 let style = self.independent_formatting_context.style();
2532
2533 let tentative_block_size = SizeConstraint::default();
2537
2538 let inline_size = {
2541 let stretch_size =
2542 flex_context.containing_block.size.inline - pbm_auto_is_zero.cross;
2543 let get_content_size = || {
2544 self.inline_content_sizes(
2545 flex_context,
2546 tentative_block_size,
2547 preferred_aspect_ratio,
2548 )
2549 };
2550 content_box_sizes.inline.resolve(
2551 Direction::Inline,
2552 automatic_inline_size,
2553 Au::zero,
2554 Some(stretch_size),
2555 get_content_size,
2556 false,
2557 )
2558 };
2559 let item_as_containing_block = ContainingBlock {
2560 size: ContainingBlockSize {
2561 inline: inline_size,
2562 block: tentative_block_size,
2563 },
2564 style,
2565 };
2566 self.independent_formatting_context
2567 .layout(
2568 flex_context.layout_context,
2569 &mut positioning_context,
2570 &item_as_containing_block,
2571 flex_context.containing_block,
2572 preferred_aspect_ratio,
2573 &LazySize::intrinsic(),
2574 )
2575 .content_block_size
2576 };
2577 match intrinsic_sizing_mode {
2578 IntrinsicSizingMode::Contribution => {
2579 let stretch_size = flex_context
2580 .containing_block
2581 .size
2582 .block
2583 .to_definite()
2584 .map(|block_size| block_size - pbm_auto_is_zero.main);
2585 let inner_block_size = content_box_sizes.block.resolve(
2586 Direction::Block,
2587 Size::FitContent,
2588 Au::zero,
2589 stretch_size,
2590 || ContentSizes::from(content_block_size()),
2591 false, );
2599 inner_block_size + pbm_auto_is_zero.main
2600 },
2601 IntrinsicSizingMode::Size => content_block_size(),
2602 }
2603 }
2604
2605 fn inline_content_sizes(
2606 &self,
2607 flex_context: &FlexContext,
2608 block_size: SizeConstraint,
2609 preferred_aspect_ratio: Option<AspectRatio>,
2610 ) -> ContentSizes {
2611 let style = self.independent_formatting_context.style();
2612 let constraint_space = ConstraintSpace::new(block_size, style, preferred_aspect_ratio);
2613 self.independent_formatting_context
2614 .inline_content_sizes(flex_context.layout_context, &constraint_space)
2615 .sizes
2616 }
2617}