1use std::cell::{Cell, LazyCell};
6use std::cmp::Ordering;
7use std::sync::Arc;
8
9use app_units::Au;
10use atomic_refcell::AtomicRef;
11use itertools::izip;
12use rayon::iter::{
13 IndexedParallelIterator, IntoParallelIterator, ParallelDrainRange, ParallelIterator,
14};
15use style::Zero;
16use style::computed_values::position::T as Position;
17use style::logical_geometry::{LogicalAxis, WritingMode};
18use style::properties::ComputedValues;
19use style::properties::longhands::align_items::computed_value::T as AlignItems;
20use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
21use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
22use style::values::computed::LengthPercentage;
23use style::values::generics::flex::GenericFlexBasis as FlexBasis;
24use style::values::generics::length::LengthPercentageOrNormal;
25use style::values::specified::align::AlignFlags;
26
27use super::geom::{FlexAxis, FlexRelativeRect, FlexRelativeSides, FlexRelativeVec2};
28use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox};
29use crate::cell::ArcRefCell;
30use crate::context::LayoutContext;
31use crate::dom::WeakLayoutBox;
32use crate::formatting_contexts::Baselines;
33use crate::fragment_tree::{
34 BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
35};
36use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
37use crate::layout_box_base::IndependentFormattingContextLayoutResult;
38use crate::positioned::{
39 AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
40};
41use crate::sizing::{
42 ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, IntrinsicSizingMode,
43 LazySize, Size, SizeConstraint, Sizes,
44};
45use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, LayoutStyle};
46use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
47
48struct FlexContext<'a> {
51 config: FlexContainerConfig,
52 layout_context: &'a LayoutContext<'a>,
53 containing_block: &'a ContainingBlock<'a>, container_inner_size_constraint: FlexRelativeVec2<SizeConstraint>,
55}
56
57struct FlexItem<'a> {
59 box_: &'a FlexItemBox,
60
61 content_cross_sizes: Sizes,
63
64 padding: FlexRelativeSides<Au>,
65 border: FlexRelativeSides<Au>,
66 margin: FlexRelativeSides<AuOrAuto>,
67
68 pbm_auto_is_zero: FlexRelativeVec2<Au>,
71
72 flex_base_size: Au,
74
75 flex_base_size_is_definite: bool,
79
80 hypothetical_main_size: Au,
82
83 content_min_main_size: Au,
86
87 content_max_main_size: Option<Au>,
90
91 align_self: AlignItems,
93
94 depends_on_block_constraints: bool,
96
97 preferred_aspect_ratio: Option<AspectRatio>,
99
100 automatic_cross_size: Size<Au>,
103 automatic_cross_size_for_intrinsic_sizing: Size<Au>,
104}
105
106enum FlexContent {
109 AbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
110 FlexItemPlaceholder,
111}
112
113struct FlexItemLayoutResult {
115 hypothetical_cross_size: Au,
116 fragments: Vec<Fragment>,
117 positioning_context: PositioningContext,
118
119 content_baselines_for_parent_relative_to_margin_box: Baselines,
122
123 flex_alignment_baseline_relative_to_margin_box: Option<Au>,
126
127 content_block_size: Au,
130
131 containing_block_size: ContainingBlockSize,
133
134 depends_on_block_constraints: bool,
136
137 specific_layout_info: Option<SpecificLayoutInfo>,
139}
140
141struct FlexLineItem<'a> {
144 item: FlexItem<'a>,
146
147 layout_result: FlexItemLayoutResult,
150
151 used_main_size: Au,
153}
154
155impl FlexLineItem<'_> {
156 fn get_or_synthesize_baseline_with_cross_size(
157 &self,
158 cross_size: Au,
159 flex_container_config: &FlexContainerConfig,
160 ) -> Au {
161 self.layout_result
162 .flex_alignment_baseline_relative_to_margin_box
163 .unwrap_or_else(|| {
164 self.item
165 .synthesized_baseline_relative_to_margin_box(cross_size, flex_container_config)
166 })
167 }
168
169 #[expect(clippy::too_many_arguments)]
170 fn collect_fragment(
171 mut self,
172 initial_flex_layout: &InitialFlexLineLayout,
173 item_used_size: FlexRelativeVec2<Au>,
174 item_margin: FlexRelativeSides<Au>,
175 item_main_interval: Au,
176 final_line_cross_size: Au,
177 shared_alignment_baseline: &Option<Au>,
178 flex_context: &mut FlexContext,
179 all_baselines: &mut Baselines,
180 main_position_cursor: &mut Au,
181 ) -> (Arc<BoxFragment>, PositioningContext) {
182 *main_position_cursor +=
185 item_margin.main_start + self.item.border.main_start + self.item.padding.main_start;
186 let item_content_main_start_position = *main_position_cursor;
187
188 *main_position_cursor += item_used_size.main +
189 self.item.padding.main_end +
190 self.item.border.main_end +
191 item_margin.main_end +
192 item_main_interval;
193
194 let item_content_cross_start_position = self.item.align_along_cross_axis(
196 &item_margin,
197 &item_used_size.cross,
198 final_line_cross_size,
199 self.layout_result
200 .flex_alignment_baseline_relative_to_margin_box
201 .unwrap_or_default(),
202 shared_alignment_baseline.unwrap_or_default(),
203 flex_context.config.flex_wrap_is_reversed,
204 );
205
206 let start_corner = FlexRelativeVec2 {
207 main: item_content_main_start_position,
208 cross: item_content_cross_start_position,
209 };
210
211 let final_line_size = FlexRelativeVec2 {
213 main: initial_flex_layout.line_size.main,
214 cross: final_line_cross_size,
215 };
216 let content_rect = flex_context.rect_to_flow_relative(
217 final_line_size,
218 FlexRelativeRect {
219 start_corner,
220 size: item_used_size,
221 },
222 );
223
224 let adjust_baseline = |baseline: Au| {
225 baseline + item_content_cross_start_position -
226 self.item.border.cross_start -
227 self.item.padding.cross_start -
228 item_margin.cross_start
229 };
230
231 let baselines = self
232 .layout_result
233 .content_baselines_for_parent_relative_to_margin_box;
234 if flex_context.config.flex_direction_is_reversed {
235 if let Some(last_baseline) = baselines.last {
236 all_baselines
237 .last
238 .get_or_insert_with(|| adjust_baseline(last_baseline));
239 }
240 if let Some(first_baseline) = baselines.first {
241 all_baselines.first = Some(adjust_baseline(first_baseline));
242 }
243 } else {
244 if let Some(first_baseline) = baselines.first {
245 all_baselines
246 .first
247 .get_or_insert_with(|| adjust_baseline(first_baseline));
248 }
249 if let Some(last_baseline) = baselines.last {
250 all_baselines.last = Some(adjust_baseline(last_baseline));
251 }
252 }
253
254 let mut fragment_info = self.item.box_.base_fragment_info();
255 fragment_info
256 .flags
257 .insert(FragmentFlags::IS_FLEX_OR_GRID_ITEM);
258 if self.item.depends_on_block_constraints {
259 fragment_info.flags.insert(
260 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
261 );
262 }
263 let flags = fragment_info.flags;
264
265 let containing_block = flex_context.containing_block;
266 let container_writing_mode = containing_block.style.writing_mode;
267 let style = self.item.box_.style();
268
269 let mut fragment = BoxFragment::new(
270 fragment_info,
271 style.clone(),
272 self.layout_result.fragments,
273 content_rect.as_physical(Some(flex_context.containing_block)),
274 flex_context
275 .sides_to_flow_relative(self.item.padding)
276 .to_physical(container_writing_mode),
277 flex_context
278 .sides_to_flow_relative(self.item.border)
279 .to_physical(container_writing_mode),
280 flex_context
281 .sides_to_flow_relative(item_margin)
282 .to_physical(container_writing_mode),
283 self.layout_result.specific_layout_info,
284 );
285
286 if style.establishes_containing_block_for_absolute_descendants(flags) {
290 self.layout_result
291 .positioning_context
292 .layout_collected_children(flex_context.layout_context, &mut fragment);
293 }
294
295 if style.get_box().position == Position::Relative {
296 fragment.base.translate_rect(
297 relative_adjustement(style, containing_block)
298 .to_physical_size(containing_block.style.writing_mode),
299 );
300 }
301
302 let fragment = Arc::new(fragment);
303 self.item
304 .box_
305 .independent_formatting_context
306 .base
307 .set_fragment(Fragment::Box(fragment.clone()));
308 (fragment, self.layout_result.positioning_context)
309 }
310}
311
312struct FinalFlexLineLayout {
315 cross_size: Au,
317 item_fragments: Vec<(Arc<BoxFragment>, PositioningContext)>,
320 shared_alignment_baseline: Option<Au>,
323 all_baselines: Baselines,
328}
329
330impl FlexContainerConfig {
331 fn resolve_reversable_flex_alignment(
332 &self,
333 align_flags: AlignFlags,
334 reversed: bool,
335 ) -> AlignFlags {
336 match (align_flags.value(), reversed) {
337 (AlignFlags::FLEX_START, false) => AlignFlags::START | align_flags.flags(),
338 (AlignFlags::FLEX_START, true) => AlignFlags::END | align_flags.flags(),
339 (AlignFlags::FLEX_END, false) => AlignFlags::END | align_flags.flags(),
340 (AlignFlags::FLEX_END, true) => AlignFlags::START | align_flags.flags(),
341 (_, _) => align_flags,
342 }
343 }
344
345 fn resolve_align_self_for_child(&self, child_style: &ComputedValues) -> AlignFlags {
346 self.resolve_reversable_flex_alignment(
347 child_style
348 .resolve_align_self(self.align_items, AlignFlags::STRETCH)
349 .0,
350 self.flex_wrap_is_reversed,
351 )
352 }
353
354 fn resolve_justify_content_for_child(&self) -> AlignFlags {
355 self.resolve_reversable_flex_alignment(
356 self.justify_content.primary(),
357 self.flex_direction_is_reversed,
358 )
359 }
360
361 fn sides_to_flex_relative<T>(&self, sides: LogicalSides<T>) -> FlexRelativeSides<T> {
362 self.main_start_cross_start_sides_are
363 .sides_to_flex_relative(sides)
364 }
365
366 fn sides_to_flow_relative<T>(&self, sides: FlexRelativeSides<T>) -> LogicalSides<T> {
367 self.main_start_cross_start_sides_are
368 .sides_to_flow_relative(sides)
369 }
370}
371
372impl FlexContext<'_> {
373 #[inline]
374 fn sides_to_flow_relative<T>(&self, x: FlexRelativeSides<T>) -> LogicalSides<T> {
375 self.config.sides_to_flow_relative(x)
376 }
377
378 #[inline]
379 fn rect_to_flow_relative(
380 &self,
381 base_rect_size: FlexRelativeVec2<Au>,
382 rect: FlexRelativeRect<Au>,
383 ) -> LogicalRect<Au> {
384 super::geom::rect_to_flow_relative(
385 self.config.flex_axis,
386 self.config.main_start_cross_start_sides_are,
387 base_rect_size,
388 rect,
389 )
390 }
391}
392
393#[derive(Debug, Default)]
394struct DesiredFlexFractionAndGrowOrShrinkFactor {
395 desired_flex_fraction: f32,
396 flex_grow_or_shrink_factor: f32,
397}
398
399#[derive(Default)]
400struct FlexItemBoxInlineContentSizesInfo {
401 outer_flex_base_size: Au,
402 outer_min_main_size: Au,
403 outer_max_main_size: Option<Au>,
404 min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
405 max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
406 min_content_main_size_for_multiline_container: Au,
407 depends_on_block_constraints: bool,
408}
409
410impl ComputeInlineContentSizes for FlexContainer {
411 #[servo_tracing::instrument(name = "FlexContainer::compute_inline_content_sizes", skip_all)]
412 fn compute_inline_content_sizes(
413 &self,
414 layout_context: &LayoutContext,
415 constraint_space: &ConstraintSpace,
416 ) -> InlineContentSizesResult {
417 match self.config.flex_axis {
418 FlexAxis::Row => {
419 self.main_content_sizes(layout_context, &constraint_space.into(), || {
420 unreachable!(
421 "Unexpected FlexContext query during row flex intrinsic size calculation."
422 )
423 })
424 },
425 FlexAxis::Column => self.cross_content_sizes(layout_context, &constraint_space.into()),
426 }
427 }
428}
429
430impl FlexContainer {
431 fn cross_content_sizes(
432 &self,
433 layout_context: &LayoutContext,
434 containing_block_for_children: &IndefiniteContainingBlock,
435 ) -> InlineContentSizesResult {
436 assert_eq!(
438 self.config.flex_axis,
439 FlexAxis::Column,
440 "The cross axis should be the inline one"
441 );
442 let mut sizes = ContentSizes::zero();
443 let mut depends_on_block_constraints = false;
444 for kid in self.children.iter() {
445 let kid = &*kid.borrow();
446 match kid {
447 FlexLevelBox::FlexItem(item) => {
448 let ifc = &item.independent_formatting_context;
452 let result = ifc.outer_inline_content_sizes(
453 layout_context,
454 containing_block_for_children,
455 &LogicalVec2::zero(),
456 false, false, );
459 sizes.max_assign(result.sizes);
460 depends_on_block_constraints |= result.depends_on_block_constraints;
461 },
462 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
463 }
464 }
465 InlineContentSizesResult {
466 sizes,
467 depends_on_block_constraints,
468 }
469 }
470
471 fn main_content_sizes<'a>(
472 &self,
473 layout_context: &LayoutContext,
474 containing_block_for_children: &IndefiniteContainingBlock,
475 flex_context_getter: impl Fn() -> &'a FlexContext<'a>,
476 ) -> InlineContentSizesResult {
477 let mut chosen_max_flex_fraction = f32::NEG_INFINITY;
486 let mut chosen_min_flex_fraction = f32::NEG_INFINITY;
487 let mut sum_of_flex_grow_factors = 0.0;
488 let mut sum_of_flex_shrink_factors = 0.0;
489 let mut item_infos = vec![];
490
491 for kid in self.children.iter() {
492 let kid = &*kid.borrow();
493 match kid {
494 FlexLevelBox::FlexItem(item) => {
495 sum_of_flex_grow_factors += item.style().get_position().flex_grow.0;
496 sum_of_flex_shrink_factors += item.style().get_position().flex_shrink.0;
497
498 let info = item.main_content_size_info(
499 layout_context,
500 containing_block_for_children,
501 &self.config,
502 &flex_context_getter,
503 );
504
505 chosen_max_flex_fraction =
510 chosen_max_flex_fraction.max(info.max_flex_factors.desired_flex_fraction);
511 chosen_min_flex_fraction =
512 chosen_min_flex_fraction.max(info.min_flex_factors.desired_flex_fraction);
513
514 item_infos.push(info)
515 },
516 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
517 }
518 }
519
520 let normalize_flex_fraction = |chosen_flex_fraction| {
521 if chosen_flex_fraction > 0.0 && sum_of_flex_grow_factors < 1.0 {
522 chosen_flex_fraction / sum_of_flex_grow_factors
526 } else if chosen_flex_fraction < 0.0 && sum_of_flex_shrink_factors < 1.0 {
527 chosen_flex_fraction * sum_of_flex_shrink_factors
531 } else {
532 chosen_flex_fraction
533 }
534 };
535
536 let chosen_min_flex_fraction = normalize_flex_fraction(chosen_min_flex_fraction);
537 let chosen_max_flex_fraction = normalize_flex_fraction(chosen_max_flex_fraction);
538
539 let main_gap = match self.config.flex_axis {
540 FlexAxis::Row => self.style.get_column_gap(),
541 FlexAxis::Column => self.style.get_row_gap(),
542 };
543 let main_gap = match main_gap {
544 LengthPercentageOrNormal::LengthPercentage(length_percentage) => {
545 length_percentage.to_used_value(Au::zero())
546 },
547 LengthPercentageOrNormal::Normal => Au::zero(),
548 };
549 let extra_space_from_main_gap = main_gap * (item_infos.len() as i32 - 1);
550 let mut container_max_content_size = extra_space_from_main_gap;
551 let mut container_min_content_size = if self.config.flex_wrap == FlexWrap::NOWRAP {
552 extra_space_from_main_gap
553 } else {
554 Au::zero()
555 };
556 let mut container_depends_on_block_constraints = false;
557
558 for FlexItemBoxInlineContentSizesInfo {
559 outer_flex_base_size,
560 outer_min_main_size,
561 outer_max_main_size,
562 min_flex_factors,
563 max_flex_factors,
564 min_content_main_size_for_multiline_container,
565 depends_on_block_constraints,
566 } in item_infos.iter()
567 {
568 container_max_content_size += (*outer_flex_base_size +
574 Au::from_f32_px(
575 max_flex_factors.flex_grow_or_shrink_factor * chosen_max_flex_fraction,
576 ))
577 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
578
579 if self.config.flex_wrap == FlexWrap::NOWRAP {
590 container_min_content_size += (*outer_flex_base_size +
591 Au::from_f32_px(
592 min_flex_factors.flex_grow_or_shrink_factor * chosen_min_flex_fraction,
593 ))
594 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
595 } else {
596 container_min_content_size
597 .max_assign(*min_content_main_size_for_multiline_container);
598 }
599
600 container_depends_on_block_constraints |= depends_on_block_constraints;
601 }
602
603 InlineContentSizesResult {
604 sizes: ContentSizes {
605 min_content: container_min_content_size,
606 max_content: container_max_content_size,
607 },
608 depends_on_block_constraints: container_depends_on_block_constraints,
609 }
610 }
611
612 #[servo_tracing::instrument(
614 name = "FlexContainer::layout",
615 skip_all,
616 fields(self_address = self as *const _ as usize)
617 )]
618 pub(crate) fn layout(
619 &self,
620 layout_context: &LayoutContext,
621 positioning_context: &mut PositioningContext,
622 containing_block: &ContainingBlock,
623 lazy_block_size: &LazySize,
624 ) -> IndependentFormattingContextLayoutResult {
625 let mut flex_context = FlexContext {
626 config: self.config.clone(),
627 layout_context,
628 containing_block,
629 container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative(
631 LogicalVec2 {
632 inline: SizeConstraint::Definite(containing_block.size.inline),
633 block: containing_block.size.block,
634 },
635 ),
636 };
637
638 let container_main_size = match self.config.flex_axis {
641 FlexAxis::Row => containing_block.size.inline,
642 FlexAxis::Column => lazy_block_size.resolve(|| {
643 let mut containing_block = IndefiniteContainingBlock::from(containing_block);
644 containing_block.size.block = None;
645 self.main_content_sizes(layout_context, &containing_block, || &flex_context)
646 .sizes
647 .max_content
648 }),
649 };
650
651 let mut flex_items = Vec::with_capacity(self.children.len());
653
654 let absolutely_positioned_items_with_original_order = self
662 .children
663 .iter()
664 .map(|arcrefcell| {
665 let borrowed = arcrefcell.borrow();
666 match &*borrowed {
667 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned) => {
668 FlexContent::AbsolutelyPositionedBox(absolutely_positioned.clone())
669 },
670 FlexLevelBox::FlexItem(_) => {
671 let item = AtomicRef::map(borrowed, |child| match child {
672 FlexLevelBox::FlexItem(item) => item,
673 _ => unreachable!(),
674 });
675 flex_items.push(item);
676 FlexContent::FlexItemPlaceholder
677 },
678 }
679 })
680 .collect::<Vec<_>>();
681
682 let row_gap = self.style.get_row_gap();
683 let column_gap = self.style.get_column_gap();
684 let (cross_gap, main_gap) = match flex_context.config.flex_axis {
685 FlexAxis::Row => (row_gap, column_gap),
686 FlexAxis::Column => (column_gap, row_gap),
687 };
688 let cross_gap = match cross_gap {
689 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
690 .maybe_to_used_value(
691 flex_context
692 .container_inner_size_constraint
693 .cross
694 .to_definite(),
695 )
696 .unwrap_or_default(),
697 LengthPercentageOrNormal::Normal => Au::zero(),
698 };
699 let main_gap = match main_gap {
700 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
701 .maybe_to_used_value(
702 flex_context
703 .container_inner_size_constraint
704 .main
705 .to_definite(),
706 )
707 .unwrap_or_default(),
708 LengthPercentageOrNormal::Normal => Au::zero(),
709 };
710
711 let initial_line_layouts = do_initial_flex_line_layout(
714 &mut flex_context,
715 container_main_size,
716 &flex_items,
717 main_gap,
718 );
719
720 let line_count = initial_line_layouts.len();
721 let content_cross_size = initial_line_layouts
722 .iter()
723 .map(|layout| layout.line_size.cross)
724 .sum::<Au>() +
725 cross_gap * (line_count as i32 - 1);
726 let content_block_size = match self.config.flex_axis {
727 FlexAxis::Row => content_cross_size,
728 FlexAxis::Column => container_main_size,
729 };
730
731 let container_cross_size = match self.config.flex_axis {
733 FlexAxis::Row => lazy_block_size.resolve(|| content_cross_size),
734 FlexAxis::Column => containing_block.size.inline,
735 };
736
737 let container_size = FlexRelativeVec2 {
738 main: container_main_size,
739 cross: container_cross_size,
740 };
741
742 let mut remaining_free_cross_space = container_cross_size - content_cross_size;
743
744 let num_lines = initial_line_layouts.len();
749 let resolved_align_content: AlignFlags = {
750 let align_content_style = flex_context.config.align_content.primary();
752 let mut is_safe = align_content_style.flags() == AlignFlags::SAFE;
753
754 let mut resolved_align_content = match align_content_style.value() {
757 AlignFlags::NORMAL => AlignFlags::STRETCH,
758 align_content => align_content,
759 };
760
761 let fallback_is_needed = match resolved_align_content {
769 _ if remaining_free_cross_space <= Au::zero() => true,
770 AlignFlags::STRETCH => num_lines < 1,
771 AlignFlags::SPACE_BETWEEN | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
772 num_lines < 2
773 },
774 _ => false,
775 };
776
777 if fallback_is_needed {
778 (resolved_align_content, is_safe) = match resolved_align_content {
779 AlignFlags::STRETCH => (AlignFlags::FLEX_START, false),
780 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
781 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
782 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
783 _ => (resolved_align_content, is_safe),
784 }
785 };
786
787 if remaining_free_cross_space <= Au::zero() && is_safe {
789 resolved_align_content = AlignFlags::START;
790 }
791
792 resolved_align_content
793 };
794
795 let flex_wrap_is_reversed = flex_context.config.flex_wrap_is_reversed;
797 let resolved_align_content = self
798 .config
799 .resolve_reversable_flex_alignment(resolved_align_content, flex_wrap_is_reversed);
800 let mut cross_start_position_cursor = match resolved_align_content {
801 AlignFlags::START if flex_wrap_is_reversed => remaining_free_cross_space,
802 AlignFlags::START => Au::zero(),
803 AlignFlags::END if flex_wrap_is_reversed => Au::zero(),
804 AlignFlags::END => remaining_free_cross_space,
805 AlignFlags::CENTER => remaining_free_cross_space / 2,
806 AlignFlags::STRETCH => Au::zero(),
807 AlignFlags::SPACE_BETWEEN => Au::zero(),
808 AlignFlags::SPACE_AROUND => remaining_free_cross_space / num_lines as i32 / 2,
809 AlignFlags::SPACE_EVENLY => remaining_free_cross_space / (num_lines as i32 + 1),
810
811 _ => Au::zero(),
813 };
814
815 let inline_axis_is_main_axis = self.config.flex_axis == FlexAxis::Row;
816 let mut baseline_alignment_participating_baselines = Baselines::default();
817 let mut all_baselines = Baselines::default();
818 let mut flex_item_fragments = initial_line_layouts.into_iter().enumerate().flat_map(
819 |(index, initial_line_layout)| {
820 let (space_to_add_to_line, space_to_add_after_line) =
824 allocate_free_cross_space_for_flex_line(
825 resolved_align_content,
826 remaining_free_cross_space,
827 (num_lines - index) as i32,
828 );
829 remaining_free_cross_space -= space_to_add_to_line + space_to_add_after_line;
830
831 let final_line_cross_size =
832 initial_line_layout.line_size.cross + space_to_add_to_line;
833 let mut final_line_layout = initial_line_layout.finish_with_final_cross_size(
834 &mut flex_context,
835 main_gap,
836 final_line_cross_size,
837 );
838
839 let line_cross_start_position = cross_start_position_cursor;
840 cross_start_position_cursor = line_cross_start_position +
841 final_line_cross_size +
842 space_to_add_after_line +
843 cross_gap;
844
845 let flow_relative_line_position =
846 match (self.config.flex_axis, flex_wrap_is_reversed) {
847 (FlexAxis::Row, false) => LogicalVec2 {
848 block: line_cross_start_position,
849 inline: Au::zero(),
850 },
851 (FlexAxis::Row, true) => LogicalVec2 {
852 block: container_cross_size -
853 line_cross_start_position -
854 final_line_layout.cross_size,
855 inline: Au::zero(),
856 },
857 (FlexAxis::Column, false) => LogicalVec2 {
858 block: Au::zero(),
859 inline: line_cross_start_position,
860 },
861 (FlexAxis::Column, true) => LogicalVec2 {
862 block: Au::zero(),
863 inline: container_cross_size -
864 line_cross_start_position -
865 final_line_cross_size,
866 },
867 };
868
869 if inline_axis_is_main_axis {
870 let line_shared_alignment_baseline = final_line_layout
871 .shared_alignment_baseline
872 .map(|baseline| baseline + flow_relative_line_position.block);
873 if index == 0 {
874 baseline_alignment_participating_baselines.first =
875 line_shared_alignment_baseline;
876 }
877 if index == num_lines - 1 {
878 baseline_alignment_participating_baselines.last =
879 line_shared_alignment_baseline;
880 }
881 }
882
883 let line_all_baselines = final_line_layout
884 .all_baselines
885 .offset(flow_relative_line_position.block);
886 if index == 0 {
887 all_baselines.first = line_all_baselines.first;
888 }
889 if index == num_lines - 1 {
890 all_baselines.last = line_all_baselines.last;
891 }
892
893 let physical_line_position =
894 flow_relative_line_position.to_physical_size(self.style.writing_mode);
895 for (fragment, _) in &mut final_line_layout.item_fragments {
896 fragment.base.translate_rect(physical_line_position);
897 }
898 final_line_layout.item_fragments
899 },
900 );
901
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
993 },
994 (AlignFlags::STRETCH, false) => AlignFlags::START,
995 (AlignFlags::SPACE_BETWEEN, false) => AlignFlags::START,
996 (AlignFlags::SPACE_BETWEEN, true) => AlignFlags::END,
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::AbsoluteOrFixedPositionedPlaceholder(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 flex_item_boxes: &'items Vec<AtomicRef<'items, FlexItemBox>>,
1136 main_gap: Au,
1137) -> Vec<InitialFlexLineLayout<'items>> {
1138 let construct_line = |items: Vec<FlexItem<'items>>| {
1139 InitialFlexLineLayout::new(flex_context, items, container_main_size, main_gap)
1140 };
1141
1142 if flex_context.config.container_is_single_line {
1143 let items: Vec<_> = flex_item_boxes
1144 .iter()
1145 .map(|item_box| FlexItem::new(flex_context, item_box))
1146 .collect();
1147 return vec![construct_line(items)];
1148 }
1149
1150 let mut lines = Vec::new();
1151 let mut line_items = Vec::new();
1152 let mut line_size_so_far = Au::zero();
1153
1154 for item_box in flex_item_boxes {
1155 let item = FlexItem::new(flex_context, item_box);
1156 let item_size = item.hypothetical_main_size + item.pbm_auto_is_zero.main;
1157
1158 let mut line_size_would_be = line_size_so_far + item_size;
1160 if !line_items.is_empty() {
1161 line_size_would_be += main_gap;
1162 }
1163
1164 let item_fits = line_size_would_be <= container_main_size;
1165 if item_fits || line_items.is_empty() {
1166 line_items.push(item);
1167 line_size_so_far = line_size_would_be;
1168 } else {
1169 lines.push(std::mem::take(&mut line_items));
1170 line_items.push(item);
1171 line_size_so_far = item_size;
1172 }
1173 }
1174
1175 lines.push(line_items);
1177
1178 let job_sizes = lines
1179 .iter()
1180 .map(|line| line.iter().map(FlexItem::subtree_size).sum());
1181 if flex_context
1182 .layout_context
1183 .should_parallelize_layout(job_sizes)
1184 {
1185 lines.par_drain(..).map(construct_line).collect()
1186 } else {
1187 lines.into_iter().map(construct_line).collect()
1188 }
1189}
1190
1191struct InitialFlexLineLayout<'a> {
1195 items: Vec<FlexLineItem<'a>>,
1197
1198 line_size: FlexRelativeVec2<Au>,
1200
1201 free_space_in_main_axis: Au,
1203}
1204
1205impl InitialFlexLineLayout<'_> {
1206 fn new<'items>(
1207 flex_context: &FlexContext,
1208 items: Vec<FlexItem<'items>>,
1209 container_main_size: Au,
1210 main_gap: Au,
1211 ) -> InitialFlexLineLayout<'items> {
1212 let item_count = items.len();
1213 let (item_used_main_sizes, free_space_in_main_axis) = Self::resolve_flexible_lengths(
1214 &items,
1215 container_main_size - main_gap * (item_count as i32 - 1),
1216 );
1217
1218 let line_cross_size_before_layout = Self::cross_size_before_layout(flex_context);
1224 let layout_item = |(item, used_main_size): (FlexItem<'items>, Au)| {
1225 let used_cross_size = line_cross_size_before_layout.and_then(|line_cross_size| {
1226 item.used_cross_size_before_layout(flex_context, used_main_size, line_cross_size)
1227 });
1228 let layout_result = item.layout(used_main_size, flex_context, used_cross_size);
1229 FlexLineItem {
1230 item,
1231 layout_result,
1232 used_main_size,
1233 }
1234 };
1235 let items: Vec<_> = if flex_context
1236 .layout_context
1237 .should_parallelize_layout(items.iter().map(FlexItem::subtree_size))
1238 {
1239 items
1240 .into_par_iter()
1241 .zip(item_used_main_sizes.into_par_iter())
1242 .map(layout_item)
1243 .collect()
1244 } else {
1245 items
1246 .into_iter()
1247 .zip(item_used_main_sizes)
1248 .map(layout_item)
1249 .collect()
1250 };
1251
1252 let line_cross_size = Self::cross_size(&items, flex_context);
1254 let line_size = FlexRelativeVec2 {
1255 main: container_main_size,
1256 cross: line_cross_size,
1257 };
1258
1259 InitialFlexLineLayout {
1260 items,
1261 line_size,
1262 free_space_in_main_axis,
1263 }
1264 }
1265
1266 fn resolve_flexible_lengths<'items>(
1269 items: &'items [FlexItem<'items>],
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 outer_hypothetical_main_sizes_sum: Au = items
1285 .iter()
1286 .map(|item| item.hypothetical_main_size + item.pbm_auto_is_zero.main)
1287 .sum();
1288 let grow = outer_hypothetical_main_sizes_sum < container_main_size;
1289
1290 let mut frozen_count = 0;
1291 let items: Vec<_> = items
1292 .iter()
1293 .map(|item| {
1294 let target_main_size = Cell::new(item.flex_base_size);
1297
1298 let flex_factor = if grow {
1305 item.box_.style().get_position().flex_grow.0
1306 } else {
1307 item.box_.style().get_position().flex_shrink.0
1308 };
1309
1310 let is_inflexible = flex_factor == 0. ||
1311 if grow {
1312 item.flex_base_size > item.hypothetical_main_size
1313 } else {
1314 item.flex_base_size < item.hypothetical_main_size
1315 };
1316
1317 let frozen = Cell::new(false);
1318 if is_inflexible {
1319 frozen_count += 1;
1320 frozen.set(true);
1321 target_main_size.set(item.hypothetical_main_size);
1322 }
1323
1324 FlexibleLengthResolutionItem {
1325 item,
1326 frozen,
1327 target_main_size,
1328 flex_factor,
1329 min_max_violation_kind: Cell::new(Ordering::Equal),
1331 }
1332 })
1333 .collect();
1334
1335 let unfrozen_items = || items.iter().filter(|item| !item.frozen.get());
1336 let main_sizes = |items: Vec<FlexibleLengthResolutionItem>| {
1337 items
1338 .into_iter()
1339 .map(|item| item.target_main_size.get())
1340 .collect()
1341 };
1342
1343 let free_space = |all_items_frozen| {
1348 let items_size = items
1349 .iter()
1350 .map(|item| {
1351 item.item.pbm_auto_is_zero.main +
1352 if all_items_frozen || item.frozen.get() {
1353 item.target_main_size.get()
1354 } else {
1355 item.item.flex_base_size
1356 }
1357 })
1358 .sum();
1359 container_main_size - items_size
1360 };
1361
1362 let initial_free_space = free_space(false);
1363 loop {
1364 let mut remaining_free_space = free_space(false);
1366 if frozen_count >= items.len() {
1369 return (main_sizes(items), remaining_free_space);
1370 }
1371
1372 let unfrozen_items_flex_factor_sum =
1378 unfrozen_items().map(|item| item.flex_factor).sum();
1379 if unfrozen_items_flex_factor_sum < 1. {
1380 let multiplied = initial_free_space.scale_by(unfrozen_items_flex_factor_sum);
1381 if multiplied.abs() < remaining_free_space.abs() {
1382 remaining_free_space = multiplied
1383 }
1384 }
1385
1386 if !remaining_free_space.is_zero() {
1392 if grow {
1398 for item in unfrozen_items() {
1399 let ratio = item.flex_factor / unfrozen_items_flex_factor_sum;
1400 item.target_main_size
1401 .set(item.item.flex_base_size + remaining_free_space.scale_by(ratio));
1402 }
1403 } else {
1412 let scaled_shrink_factor = |item: &FlexibleLengthResolutionItem| {
1414 item.item.flex_base_size.scale_by(item.flex_factor)
1415 };
1416 let scaled_shrink_factors_sum: Au =
1417 unfrozen_items().map(scaled_shrink_factor).sum();
1418 if scaled_shrink_factors_sum > Au::zero() {
1419 for item in unfrozen_items() {
1420 let ratio = scaled_shrink_factor(item).0 as f32 /
1421 scaled_shrink_factors_sum.0 as f32;
1422 item.target_main_size.set(
1423 item.item.flex_base_size -
1424 remaining_free_space.abs().scale_by(ratio),
1425 );
1426 }
1427 }
1428 }
1429 }
1430
1431 let mut total_violation = Au::zero();
1437 for item in unfrozen_items() {
1438 let unclamped = item.target_main_size.get();
1439 let clamped = unclamped.clamp_between_extremums(
1440 item.item.content_min_main_size,
1441 item.item.content_max_main_size,
1442 );
1443 item.target_main_size.set(clamped);
1444 item.min_max_violation_kind.set(clamped.cmp(&unclamped));
1447 total_violation += clamped - unclamped;
1448 }
1449
1450 match total_violation.cmp(&Au::zero()) {
1457 Ordering::Equal => {
1458 let remaining_free_space = free_space(true);
1461 return (main_sizes(items), remaining_free_space);
1462 },
1463 total_violation_kind => {
1464 for item in unfrozen_items() {
1465 if item.min_max_violation_kind.get() == total_violation_kind {
1466 item.frozen.set(true);
1467 frozen_count += 1;
1468 }
1469 }
1470 },
1471 }
1472 }
1473 }
1474
1475 fn cross_size_before_layout(flex_context: &FlexContext) -> Option<Au> {
1479 flex_context
1480 .container_inner_size_constraint
1481 .cross
1482 .to_definite()
1483 .filter(|_| flex_context.config.container_is_single_line)
1484 }
1485
1486 fn cross_size<'items>(items: &'items [FlexLineItem<'items>], flex_context: &FlexContext) -> Au {
1488 if let Some(size) = Self::cross_size_before_layout(flex_context) {
1489 return size;
1490 }
1491
1492 let mut max_ascent = Au::zero();
1493 let mut max_descent = Au::zero();
1494 let mut max_outer_hypothetical_cross_size = Au::zero();
1495 for item in items.iter() {
1496 if matches!(
1498 item.item.align_self.0.value(),
1499 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1500 ) {
1501 let baseline = item.get_or_synthesize_baseline_with_cross_size(
1502 item.layout_result.hypothetical_cross_size,
1503 &flex_context.config,
1504 );
1505 let hypothetical_margin_box_cross_size =
1506 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross;
1507 max_ascent = max_ascent.max(baseline);
1508 max_descent = max_descent.max(hypothetical_margin_box_cross_size - baseline);
1509 } else {
1510 max_outer_hypothetical_cross_size = max_outer_hypothetical_cross_size.max(
1511 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross,
1512 );
1513 }
1514 }
1515
1516 let largest = max_outer_hypothetical_cross_size.max(max_ascent + max_descent);
1518 match flex_context.container_inner_size_constraint.cross {
1519 SizeConstraint::MinMax(min, max) if flex_context.config.container_is_single_line => {
1520 largest.clamp_between_extremums(min, max)
1521 },
1522 _ => largest,
1523 }
1524 }
1525
1526 fn finish_with_final_cross_size(
1527 mut self,
1528 flex_context: &mut FlexContext,
1529 main_gap: Au,
1530 final_line_cross_size: Au,
1531 ) -> FinalFlexLineLayout {
1532 let auto_margins_count = self
1540 .items
1541 .iter()
1542 .map(|item| {
1543 item.item.margin.main_start.is_auto() as u32 +
1544 item.item.margin.main_end.is_auto() as u32
1545 })
1546 .sum::<u32>();
1547 let (space_distributed_to_auto_main_margins, free_space_in_main_axis) =
1548 if self.free_space_in_main_axis > Au::zero() && auto_margins_count > 0 {
1549 (
1550 self.free_space_in_main_axis / auto_margins_count as i32,
1551 Au::zero(),
1552 )
1553 } else {
1554 (Au::zero(), self.free_space_in_main_axis)
1555 };
1556
1557 let item_count = self.items.len();
1560 let mut shared_alignment_baseline = None;
1561 let mut item_used_cross_sizes = Vec::with_capacity(item_count);
1562 let mut item_margins = Vec::with_capacity(item_count);
1563 for item in self.items.iter_mut() {
1564 let cross_axis = match flex_context.config.flex_axis {
1565 FlexAxis::Row => LogicalAxis::Block,
1566 FlexAxis::Column => LogicalAxis::Inline,
1567 };
1568 let layout = &mut item.layout_result;
1569 let get_content_size = || match cross_axis {
1570 LogicalAxis::Block => layout.content_block_size.into(),
1571 LogicalAxis::Inline => item
1572 .item
1573 .inline_content_sizes(flex_context, item.used_main_size),
1574 };
1575 let used_cross_size =
1576 item.item
1577 .used_cross_size(cross_axis, final_line_cross_size, get_content_size);
1578 item_used_cross_sizes.push(used_cross_size);
1579
1580 let needs_new_layout = match cross_axis {
1587 LogicalAxis::Block => {
1588 (match item.item.content_cross_sizes.preferred {
1589 Size::Initial => item.item.automatic_cross_size == Size::Stretch,
1590 Size::Stretch => true,
1591 _ => false,
1592 }) && SizeConstraint::Definite(used_cross_size) !=
1593 layout.containing_block_size.block &&
1594 layout.depends_on_block_constraints
1595 },
1596 LogicalAxis::Inline => used_cross_size != layout.containing_block_size.inline,
1597 };
1598 if needs_new_layout {
1599 #[cfg(feature = "tracing")]
1600 tracing::warn!(
1601 name: "Flex item stretch cache miss",
1602 cached_inline = ?layout.containing_block_size.inline,
1603 cached_block = ?layout.containing_block_size.block,
1604 required_cross_size = ?used_cross_size,
1605 cross_axis = ?cross_axis,
1606 depends_on_block_constraints = layout.depends_on_block_constraints,
1607 );
1608 *layout =
1609 item.item
1610 .layout(item.used_main_size, flex_context, Some(used_cross_size));
1611 }
1612
1613 let baseline = item
1614 .get_or_synthesize_baseline_with_cross_size(used_cross_size, &flex_context.config);
1615 if matches!(
1616 item.item.align_self.0.value(),
1617 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1618 ) {
1619 shared_alignment_baseline =
1620 Some(shared_alignment_baseline.unwrap_or(baseline).max(baseline));
1621 }
1622 item.layout_result
1623 .flex_alignment_baseline_relative_to_margin_box = Some(baseline);
1624
1625 item_margins.push(item.item.resolve_auto_margins(
1626 flex_context,
1627 final_line_cross_size,
1628 used_cross_size,
1629 space_distributed_to_auto_main_margins,
1630 ));
1631 }
1632
1633 let resolved_justify_content: AlignFlags = {
1639 let justify_content_style = flex_context.config.justify_content.primary();
1640
1641 let inline_axis_is_main_axis = flex_context.config.flex_axis == FlexAxis::Row;
1643 let line_left_is_inline_start =
1644 flex_context.config.writing_mode.line_left_is_inline_start();
1645 let mut resolved_justify_content = match justify_content_style.value() {
1646 AlignFlags::LEFT if inline_axis_is_main_axis => {
1649 if line_left_is_inline_start {
1650 AlignFlags::FLEX_START
1651 } else {
1652 AlignFlags::FLEX_END
1653 }
1654 },
1655 AlignFlags::RIGHT if inline_axis_is_main_axis => {
1657 if line_left_is_inline_start {
1658 AlignFlags::FLEX_END
1659 } else {
1660 AlignFlags::FLEX_START
1661 }
1662 },
1663 AlignFlags::LEFT | AlignFlags::RIGHT => AlignFlags::FLEX_START,
1666 justify_content => justify_content,
1667 };
1668 let mut is_safe = justify_content_style.flags() == AlignFlags::SAFE;
1669
1670 if item_count <= 1 || free_space_in_main_axis <= Au::zero() {
1675 (resolved_justify_content, is_safe) = match resolved_justify_content {
1676 AlignFlags::STRETCH => (AlignFlags::FLEX_START, true),
1677 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
1678 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
1679 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
1680 _ => (resolved_justify_content, is_safe),
1681 }
1682 };
1683
1684 if free_space_in_main_axis <= Au::zero() && is_safe {
1686 resolved_justify_content = AlignFlags::START;
1687 }
1688
1689 resolved_justify_content
1690 };
1691
1692 let main_start_position = match resolved_justify_content {
1694 AlignFlags::START => Au::zero(),
1695 AlignFlags::FLEX_START => {
1696 if flex_context.config.flex_direction_is_reversed {
1697 free_space_in_main_axis
1698 } else {
1699 Au::zero()
1700 }
1701 },
1702 AlignFlags::END => free_space_in_main_axis,
1703 AlignFlags::FLEX_END => {
1704 if flex_context.config.flex_direction_is_reversed {
1705 Au::zero()
1706 } else {
1707 free_space_in_main_axis
1708 }
1709 },
1710 AlignFlags::CENTER => free_space_in_main_axis / 2,
1711 AlignFlags::STRETCH => Au::zero(),
1712 AlignFlags::SPACE_BETWEEN => Au::zero(),
1713 AlignFlags::SPACE_AROUND => (free_space_in_main_axis / item_count as i32) / 2,
1714 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1715
1716 _ => Au::zero(),
1718 };
1719
1720 let item_main_interval = match resolved_justify_content {
1721 AlignFlags::START => Au::zero(),
1722 AlignFlags::FLEX_START => Au::zero(),
1723 AlignFlags::END => Au::zero(),
1724 AlignFlags::FLEX_END => Au::zero(),
1725 AlignFlags::CENTER => Au::zero(),
1726 AlignFlags::STRETCH => Au::zero(),
1727 AlignFlags::SPACE_BETWEEN => free_space_in_main_axis / (item_count - 1) as i32,
1728 AlignFlags::SPACE_AROUND => free_space_in_main_axis / item_count as i32,
1729 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1730
1731 _ => Au::zero(),
1733 };
1734 let item_main_interval = item_main_interval + main_gap;
1735
1736 let mut all_baselines = Baselines::default();
1737 let mut main_position_cursor = main_start_position;
1738
1739 let items = std::mem::take(&mut self.items);
1740 let item_fragments = izip!(items, item_margins, item_used_cross_sizes.iter())
1741 .map(|(item, item_margin, item_used_cross_size)| {
1742 let item_used_size = FlexRelativeVec2 {
1743 main: item.used_main_size,
1744 cross: *item_used_cross_size,
1745 };
1746 item.collect_fragment(
1747 &self,
1748 item_used_size,
1749 item_margin,
1750 item_main_interval,
1751 final_line_cross_size,
1752 &shared_alignment_baseline,
1753 flex_context,
1754 &mut all_baselines,
1755 &mut main_position_cursor,
1756 )
1757 })
1758 .collect();
1759
1760 FinalFlexLineLayout {
1761 cross_size: final_line_cross_size,
1762 item_fragments,
1763 all_baselines,
1764 shared_alignment_baseline,
1765 }
1766 }
1767}
1768
1769impl FlexItem<'_> {
1770 #[servo_tracing::instrument(
1775 name = "FlexItem::layout",
1776 skip_all,
1777 fields(
1778 self_address = self as *const _ as usize,
1779 box_address = self.box_ as *const _ as usize,
1780 )
1781 )]
1782
1783 fn layout(
1784 &self,
1785 used_main_size: Au,
1786 flex_context: &FlexContext,
1787 used_cross_size_override: Option<Au>,
1788 ) -> FlexItemLayoutResult {
1789 let containing_block = flex_context.containing_block;
1790 let independent_formatting_context = &self.box_.independent_formatting_context;
1791 let is_table = independent_formatting_context.is_table();
1792 let mut positioning_context = PositioningContext::default();
1793 let item_writing_mode = independent_formatting_context.style().writing_mode;
1794 let item_is_horizontal = item_writing_mode.is_horizontal();
1795 let flex_axis = flex_context.config.flex_axis;
1796 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
1797 containing_block.style.writing_mode.is_horizontal(),
1798 item_is_horizontal,
1799 flex_axis,
1800 );
1801
1802 let (inline_size, block_size) = if cross_axis_is_item_block_axis {
1803 let cross_size = match used_cross_size_override {
1804 Some(s) => SizeConstraint::Definite(s),
1805 None => {
1806 let inline_stretch_size =
1807 containing_block.inline_stretch_size(self.pbm_auto_is_zero.main);
1808 let block_stretch_size =
1809 containing_block.block_stretch_size(self.pbm_auto_is_zero.cross);
1810 let tentative_block_content_size = independent_formatting_context
1811 .tentative_block_content_size(
1812 self.preferred_aspect_ratio,
1813 inline_stretch_size,
1814 );
1815 if let Some(block_content_size) = tentative_block_content_size {
1816 SizeConstraint::Definite(self.content_cross_sizes.resolve(
1817 LogicalAxis::Block,
1818 Size::FitContent,
1819 Au::zero,
1820 block_stretch_size,
1821 || block_content_size,
1822 is_table,
1823 ))
1824 } else {
1825 self.content_cross_sizes.resolve_extrinsic(
1826 Size::FitContent,
1827 Au::zero(),
1828 block_stretch_size,
1829 )
1830 }
1831 },
1832 };
1833 (used_main_size, cross_size)
1834 } else {
1835 let cross_size = used_cross_size_override.unwrap_or_else(|| {
1836 let stretch_size =
1837 containing_block.inline_stretch_size(self.pbm_auto_is_zero.cross);
1838 self.content_cross_sizes.resolve(
1839 LogicalAxis::Inline,
1840 Size::FitContent,
1841 Au::zero,
1842 stretch_size,
1843 || self.inline_content_sizes(flex_context, used_main_size),
1844 is_table,
1845 )
1846 });
1847 let is_grid = self.box_.independent_formatting_context.is_grid();
1856 let main_size = if is_grid ||
1857 self.flex_base_size_is_definite ||
1858 flex_context
1859 .container_inner_size_constraint
1860 .main
1861 .is_definite()
1862 {
1863 SizeConstraint::Definite(used_main_size)
1864 } else {
1865 SizeConstraint::MinMax(self.content_min_main_size, self.content_max_main_size)
1866 };
1867 (cross_size, main_size)
1868 };
1869
1870 let item_style = independent_formatting_context.style();
1871 let item_as_containing_block = ContainingBlock {
1872 size: ContainingBlockSize {
1873 inline: inline_size,
1874 block: block_size,
1875 },
1876 style: item_style,
1877 };
1878
1879 let lazy_block_size = if !cross_axis_is_item_block_axis {
1880 used_main_size.into()
1881 } else if let Some(cross_size) = used_cross_size_override {
1882 cross_size.into()
1883 } else {
1884 let stretch_size = containing_block.block_stretch_size(self.pbm_auto_is_zero.cross);
1885 LazySize::new(
1886 &self.content_cross_sizes,
1887 LogicalAxis::Block,
1888 Size::FitContent,
1889 Au::zero,
1890 stretch_size,
1891 is_table,
1892 )
1893 };
1894
1895 let layout = independent_formatting_context.layout(
1896 flex_context.layout_context,
1897 &mut positioning_context,
1898 &item_as_containing_block,
1899 containing_block,
1900 self.preferred_aspect_ratio,
1901 &lazy_block_size,
1902 );
1903 let IndependentFormattingContextLayoutResult {
1904 fragments,
1905 content_block_size,
1906 baselines: content_box_baselines,
1907 depends_on_block_constraints,
1908 specific_layout_info,
1909 ..
1910 } = layout;
1911
1912 let hypothetical_cross_size = if cross_axis_is_item_block_axis {
1913 lazy_block_size.resolve(|| content_block_size)
1914 } else {
1915 inline_size
1916 };
1917
1918 let item_inline_axis_is_horizontal = item_style.writing_mode.is_horizontal();
1919 let container_inline_axis_is_horizontal = flex_context.config.writing_mode.is_horizontal();
1920 let container_main_axis_is_horizontal = match flex_axis {
1921 FlexAxis::Row => container_inline_axis_is_horizontal,
1922 FlexAxis::Column => !container_inline_axis_is_horizontal,
1923 };
1924 let item_inline_axis_parallel_to_container_inline_axis =
1925 item_inline_axis_is_horizontal == container_inline_axis_is_horizontal;
1926 let item_inline_axis_parallel_to_container_main_axis =
1927 item_inline_axis_is_horizontal == container_main_axis_is_horizontal;
1928
1929 let content_baselines_relative_to_margin_box = content_box_baselines.offset(
1930 self.margin.cross_start.auto_is(Au::zero) +
1931 self.padding.cross_start +
1932 self.border.cross_start,
1933 );
1934
1935 let content_baselines_for_parent_relative_to_margin_box =
1936 if item_inline_axis_parallel_to_container_inline_axis {
1937 content_baselines_relative_to_margin_box
1938 } else {
1939 Baselines::default()
1940 };
1941
1942 let flex_alignment_baseline_relative_to_margin_box =
1943 if item_inline_axis_parallel_to_container_main_axis {
1944 match self.align_self.0.value() {
1945 AlignFlags::BASELINE => content_baselines_relative_to_margin_box.first,
1947 AlignFlags::LAST_BASELINE => content_baselines_relative_to_margin_box.last,
1948 _ => None,
1949 }
1950 } else {
1951 None
1952 };
1953
1954 FlexItemLayoutResult {
1955 hypothetical_cross_size,
1956 fragments,
1957 positioning_context,
1958 content_baselines_for_parent_relative_to_margin_box,
1959 flex_alignment_baseline_relative_to_margin_box,
1960 content_block_size,
1961 containing_block_size: item_as_containing_block.size,
1962 depends_on_block_constraints,
1963 specific_layout_info,
1964 }
1965 }
1966
1967 fn synthesized_baseline_relative_to_margin_box(
1970 &self,
1971 content_size: Au,
1972 config: &FlexContainerConfig,
1973 ) -> Au {
1974 let own_writing_mode = self.box_.style().writing_mode;
1975 let distance_from_cross_start_to_line_under = |writing_mode: WritingMode| -> Au {
1984 let line_under_edge_is_on_same_side_as_cross_start = (writing_mode.is_horizontal() &&
1989 config.flex_wrap_is_reversed) ||
1990 (writing_mode.is_vertical_lr() != config.flex_wrap_is_reversed);
1991 if line_under_edge_is_on_same_side_as_cross_start {
1992 self.margin.cross_start.auto_is(Au::zero)
1998 } else {
1999 content_size +
2005 self.margin.cross_start.auto_is(Au::zero) +
2006 self.padding.cross_sum() +
2007 self.border.cross_sum()
2008 }
2009 };
2010
2011 let block_flow_direction_is_orthogonal_to_main_axis = |writing_mode: WritingMode| {
2022 writing_mode.is_vertical() != (config.flex_axis == FlexAxis::Row)
2023 };
2024 if block_flow_direction_is_orthogonal_to_main_axis(own_writing_mode) {
2025 distance_from_cross_start_to_line_under(own_writing_mode)
2026 } else if block_flow_direction_is_orthogonal_to_main_axis(config.writing_mode) {
2027 distance_from_cross_start_to_line_under(config.writing_mode)
2028 } else if own_writing_mode.is_vertical() {
2029 distance_from_cross_start_to_line_under(WritingMode::WRITING_MODE_HORIZONTAL_TB)
2030 } else {
2031 let used_writing_mode = match own_writing_mode.is_bidi_ltr() {
2032 true => WritingMode::WRITING_MODE_VERTICAL_LR,
2033 false => WritingMode::WRITING_MODE_VERTICAL_RL,
2034 };
2035
2036 distance_from_cross_start_to_line_under(used_writing_mode)
2037 }
2038 }
2039
2040 fn subtree_size(&self) -> usize {
2041 self.box_.independent_formatting_context.subtree_size()
2042 }
2043
2044 fn resolve_auto_margins(
2050 &self,
2051 flex_context: &FlexContext,
2052 line_cross_size: Au,
2053 item_cross_content_size: Au,
2054 space_distributed_to_auto_main_margins: Au,
2055 ) -> FlexRelativeSides<Au> {
2056 let main_start = self
2057 .margin
2058 .main_start
2059 .auto_is(|| space_distributed_to_auto_main_margins);
2060 let main_end = self
2061 .margin
2062 .main_end
2063 .auto_is(|| space_distributed_to_auto_main_margins);
2064
2065 let auto_count = match (self.margin.cross_start, self.margin.cross_end) {
2066 (AuOrAuto::LengthPercentage(cross_start), AuOrAuto::LengthPercentage(cross_end)) => {
2067 return FlexRelativeSides {
2068 cross_start,
2069 cross_end,
2070 main_start,
2071 main_end,
2072 };
2073 },
2074 (AuOrAuto::Auto, AuOrAuto::Auto) => 2,
2075 _ => 1,
2076 };
2077 let outer_cross_size = self.pbm_auto_is_zero.cross + item_cross_content_size;
2078 let available = line_cross_size - outer_cross_size;
2079 let cross_start;
2080 let cross_end;
2081 if available > Au::zero() {
2082 let each_auto_margin = available / auto_count;
2083 cross_start = self.margin.cross_start.auto_is(|| each_auto_margin);
2084 cross_end = self.margin.cross_end.auto_is(|| each_auto_margin);
2085 } else {
2086 let flex_wrap = flex_context.containing_block.style.get_position().flex_wrap;
2099 let flex_wrap_reverse = match flex_wrap {
2100 FlexWrap::NOWRAP | FlexWrap::WRAP => false,
2101 FlexWrap::WRAP_REVERSE => true,
2102 _ => unreachable!("FlexWrap::BALANCE should be disabled"),
2103 };
2104 if flex_wrap_reverse {
2108 cross_start = self.margin.cross_start.auto_is(|| available);
2109 cross_end = self.margin.cross_end.auto_is(Au::zero);
2110 } else {
2111 cross_start = self.margin.cross_start.auto_is(Au::zero);
2112 cross_end = self.margin.cross_end.auto_is(|| available);
2113 }
2114 }
2115
2116 FlexRelativeSides {
2117 cross_start,
2118 cross_end,
2119 main_start,
2120 main_end,
2121 }
2122 }
2123
2124 fn align_along_cross_axis(
2126 &self,
2127 margin: &FlexRelativeSides<Au>,
2128 used_cross_size: &Au,
2129 line_cross_size: Au,
2130 propagated_baseline: Au,
2131 max_propagated_baseline: Au,
2132 wrap_reverse: bool,
2133 ) -> Au {
2134 let ending_alignment = line_cross_size - *used_cross_size - self.pbm_auto_is_zero.cross;
2135 let outer_cross_start =
2136 if self.margin.cross_start.is_auto() || self.margin.cross_end.is_auto() {
2137 Au::zero()
2138 } else {
2139 match self.align_self.0.value() {
2140 AlignFlags::STRETCH => Au::zero(),
2141 AlignFlags::CENTER => ending_alignment / 2,
2142 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE => {
2143 max_propagated_baseline - propagated_baseline
2144 },
2145 AlignFlags::START => {
2146 if !wrap_reverse {
2147 Au::zero()
2148 } else {
2149 ending_alignment
2150 }
2151 },
2152 AlignFlags::END => {
2153 if !wrap_reverse {
2154 ending_alignment
2155 } else {
2156 Au::zero()
2157 }
2158 },
2159 _ => Au::zero(),
2160 }
2161 };
2162 outer_cross_start + margin.cross_start + self.border.cross_start + self.padding.cross_start
2163 }
2164
2165 #[inline]
2166 fn inline_content_sizes(&self, flex_context: &FlexContext, block_size: Au) -> ContentSizes {
2167 self.box_.inline_content_sizes(
2168 flex_context,
2169 SizeConstraint::Definite(block_size),
2170 self.preferred_aspect_ratio,
2171 )
2172 }
2173
2174 fn used_cross_size(
2177 &self,
2178 cross_axis: LogicalAxis,
2179 line_cross_size: Au,
2180 get_content_size: impl FnOnce() -> ContentSizes,
2181 ) -> Au {
2182 self.content_cross_sizes.resolve(
2183 cross_axis,
2184 self.automatic_cross_size,
2185 Au::zero,
2186 Some(Au::zero().max(line_cross_size - self.pbm_auto_is_zero.cross)),
2187 get_content_size,
2188 self.box_.independent_formatting_context.is_table() &&
2193 cross_axis == LogicalAxis::Inline,
2194 )
2195 }
2196
2197 fn used_cross_size_before_layout(
2200 &self,
2201 flex_context: &FlexContext,
2202 used_main_size: Au,
2203 line_cross_size: Au,
2204 ) -> Option<Au> {
2205 match flex_context.config.flex_axis {
2206 FlexAxis::Column => Some(self.used_cross_size(
2207 LogicalAxis::Inline,
2208 line_cross_size,
2209 || self.inline_content_sizes(flex_context, used_main_size),
2210 )),
2211 FlexAxis::Row => {
2215 let mut needs_content_size = false;
2216 let used_cross_size =
2217 self.used_cross_size(LogicalAxis::Block, line_cross_size, || {
2218 needs_content_size = true;
2219 ContentSizes::default()
2220 });
2221 (!needs_content_size).then_some(used_cross_size)
2222 },
2223 }
2224 }
2225}
2226
2227impl FlexItemBox {
2228 fn to_flex_item<'a>(
2229 &self,
2230 layout_context: &LayoutContext,
2231 containing_block: &IndefiniteContainingBlock,
2232 content_box_sizes_and_pbm: &ContentBoxSizesAndPBM,
2233 config: &FlexContainerConfig,
2234 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2235 ) -> FlexItem<'_> {
2236 let flex_axis = config.flex_axis;
2237 let style = self.style();
2238 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
2239 containing_block.style.writing_mode.is_horizontal(),
2240 style.writing_mode.is_horizontal(),
2241 flex_axis,
2242 );
2243 let main_axis = if cross_axis_is_item_block_axis {
2244 LogicalAxis::Inline
2245 } else {
2246 LogicalAxis::Block
2247 };
2248 let align_self = AlignItems(config.resolve_align_self_for_child(style));
2249
2250 let ContentBoxSizesAndPBM {
2251 content_box_sizes,
2252 pbm,
2253 depends_on_block_constraints,
2254 preferred_size_computes_to_auto,
2255 } = content_box_sizes_and_pbm;
2256
2257 let preferred_aspect_ratio = self
2258 .independent_formatting_context
2259 .preferred_aspect_ratio(&pbm.padding_border_sums);
2260 let padding = config.sides_to_flex_relative(pbm.padding);
2261 let border = config.sides_to_flex_relative(pbm.border);
2262 let margin = config.sides_to_flex_relative(pbm.margin);
2263 let padding_border = padding.sum_by_axis() + border.sum_by_axis();
2264 let margin_auto_is_zero = config.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
2265 let pbm_auto_is_zero = FlexRelativeVec2 {
2266 main: padding_border.main,
2267 cross: padding_border.cross,
2268 } + margin_auto_is_zero.sum_by_axis();
2269 let (content_main_sizes, content_cross_sizes, cross_size_computes_to_auto) = match flex_axis
2270 {
2271 FlexAxis::Row => (
2272 &content_box_sizes.inline,
2273 &content_box_sizes.block,
2274 preferred_size_computes_to_auto.block,
2275 ),
2276 FlexAxis::Column => (
2277 &content_box_sizes.block,
2278 &content_box_sizes.inline,
2279 preferred_size_computes_to_auto.inline,
2280 ),
2281 };
2282 let automatic_cross_size = if cross_size_computes_to_auto &&
2283 item_with_auto_cross_size_stretches_to_line_size(align_self, &margin)
2284 {
2285 Size::Stretch
2286 } else {
2287 Size::FitContent
2288 };
2289 let automatic_cross_size_for_intrinsic_sizing = if config.container_is_single_line {
2290 automatic_cross_size
2291 } else {
2292 Size::FitContent
2293 };
2294 let containing_block_size = flex_axis.vec2_to_flex_relative(containing_block.size);
2295 let stretch_size = FlexRelativeVec2 {
2296 main: containing_block_size
2297 .main
2298 .map(|v| Au::zero().max(v - pbm_auto_is_zero.main)),
2299 cross: containing_block_size
2300 .cross
2301 .map(|v| Au::zero().max(v - pbm_auto_is_zero.cross)),
2302 };
2303
2304 let is_table = self.independent_formatting_context.is_table();
2305 let tentative_cross_content_size = if cross_axis_is_item_block_axis {
2306 self.independent_formatting_context
2307 .tentative_block_content_size(preferred_aspect_ratio, stretch_size.main)
2308 } else {
2309 None
2310 };
2311 let (preferred_cross_size, min_cross_size, max_cross_size) =
2312 if let Some(cross_content_size) = tentative_cross_content_size {
2313 let (preferred, min, max) = content_cross_sizes.resolve_each(
2314 automatic_cross_size_for_intrinsic_sizing,
2315 Au::zero,
2316 stretch_size.cross,
2317 || cross_content_size,
2318 is_table,
2319 );
2320 (Some(preferred), min, max)
2321 } else {
2322 content_cross_sizes.resolve_each_extrinsic(
2323 automatic_cross_size_for_intrinsic_sizing,
2324 Au::zero(),
2325 stretch_size.cross,
2326 )
2327 };
2328 let cross_size = SizeConstraint::new(preferred_cross_size, min_cross_size, max_cross_size);
2329
2330 let transferred_size_suggestion =
2335 LazyCell::new(|| match (preferred_aspect_ratio, cross_size) {
2336 (Some(ratio), SizeConstraint::Definite(cross_size)) => {
2337 Some(ratio.compute_dependent_size(main_axis, cross_size))
2338 },
2339 _ => None,
2340 });
2341
2342 let flex_base_size_is_definite = Cell::new(true);
2344 let main_content_sizes = LazyCell::new(|| {
2345 let flex_item = &self.independent_formatting_context;
2346 if let Some(transferred_size_suggestion) = *transferred_size_suggestion {
2352 return transferred_size_suggestion.into();
2353 }
2354
2355 flex_base_size_is_definite.set(false);
2356
2357 if cross_axis_is_item_block_axis {
2366 let constraint_space =
2369 ConstraintSpace::new(cross_size, style, preferred_aspect_ratio);
2370 let content_sizes = flex_item
2371 .inline_content_sizes(layout_context, &constraint_space)
2372 .sizes;
2373 if let Some(ratio) = preferred_aspect_ratio {
2374 let transferred_min = ratio.compute_dependent_size(main_axis, min_cross_size);
2375 let transferred_max =
2376 max_cross_size.map(|v| ratio.compute_dependent_size(main_axis, v));
2377 content_sizes
2378 .map(|size| size.clamp_between_extremums(transferred_min, transferred_max))
2379 } else {
2380 content_sizes
2381 }
2382 } else {
2383 self.layout_for_block_content_size(
2384 flex_context_getter(),
2385 &pbm_auto_is_zero,
2386 content_box_sizes,
2387 preferred_aspect_ratio,
2388 automatic_cross_size_for_intrinsic_sizing,
2389 IntrinsicSizingMode::Size,
2390 )
2391 .into()
2392 }
2393 });
2394
2395 let flex_base_size = self
2396 .flex_basis(
2397 containing_block_size.main,
2398 content_main_sizes.preferred,
2399 padding_border.main,
2400 )
2401 .resolve_for_preferred(Size::MaxContent, stretch_size.main, &main_content_sizes);
2402 let flex_base_size_is_definite = flex_base_size_is_definite.take();
2403
2404 let content_max_main_size = content_main_sizes
2405 .max
2406 .resolve_for_max(stretch_size.main, &main_content_sizes);
2407
2408 let get_automatic_minimum_size = || {
2409 if style.establishes_scroll_container(self.base_fragment_info().flags) {
2411 return Au::zero();
2412 }
2413
2414 let specified_size_suggestion = content_main_sizes
2418 .preferred
2419 .maybe_resolve_extrinsic(stretch_size.main);
2420
2421 let is_replaced = self.independent_formatting_context.is_replaced();
2422
2423 let content_size_suggestion = match preferred_aspect_ratio {
2428 Some(ratio) => main_content_sizes.min_content.clamp_between_extremums(
2429 ratio.compute_dependent_size(main_axis, min_cross_size),
2430 max_cross_size.map(|l| ratio.compute_dependent_size(main_axis, l)),
2431 ),
2432 None => main_content_sizes.min_content,
2433 };
2434
2435 match (specified_size_suggestion, *transferred_size_suggestion) {
2442 (Some(specified), _) => specified.min(content_size_suggestion),
2443 (_, Some(transferred)) if is_replaced => transferred.min(content_size_suggestion),
2444 _ => content_size_suggestion,
2445 }
2446 .clamp_below_max(content_max_main_size)
2447 };
2448 let content_min_main_size = content_main_sizes.min.resolve_for_min(
2449 get_automatic_minimum_size,
2450 stretch_size.main,
2451 &main_content_sizes,
2452 is_table,
2453 );
2454
2455 FlexItem {
2456 box_: self,
2457 content_cross_sizes: content_cross_sizes.clone(),
2458 padding,
2459 border,
2460 margin: config.sides_to_flex_relative(pbm.margin),
2461 pbm_auto_is_zero,
2462 flex_base_size,
2463 flex_base_size_is_definite,
2464 hypothetical_main_size: flex_base_size
2465 .clamp_between_extremums(content_min_main_size, content_max_main_size),
2466 content_min_main_size,
2467 content_max_main_size,
2468 align_self,
2469 depends_on_block_constraints: *depends_on_block_constraints,
2470 preferred_aspect_ratio,
2471 automatic_cross_size,
2472 automatic_cross_size_for_intrinsic_sizing,
2473 }
2474 }
2475
2476 fn main_content_size_info<'a>(
2477 &self,
2478 layout_context: &LayoutContext,
2479 containing_block: &IndefiniteContainingBlock,
2480 config: &FlexContainerConfig,
2481 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2482 ) -> FlexItemBoxInlineContentSizesInfo {
2483 let content_box_sizes_and_pbm = self
2484 .independent_formatting_context
2485 .layout_style()
2486 .content_box_sizes_and_padding_border_margin(containing_block);
2487
2488 let FlexItem {
2491 flex_base_size,
2492 content_min_main_size,
2493 content_max_main_size,
2494 pbm_auto_is_zero,
2495 preferred_aspect_ratio,
2496 automatic_cross_size_for_intrinsic_sizing,
2497 ..
2498 } = self.to_flex_item(
2499 layout_context,
2500 containing_block,
2501 &content_box_sizes_and_pbm,
2502 config,
2503 flex_context_getter,
2504 );
2505
2506 let (content_contribution_sizes, depends_on_block_constraints) = match config.flex_axis {
2509 FlexAxis::Row => {
2510 let auto_minimum = LogicalVec2 {
2511 inline: content_min_main_size,
2512 block: Au::zero(),
2513 };
2514 let InlineContentSizesResult {
2515 sizes,
2516 depends_on_block_constraints,
2517 } = self
2518 .independent_formatting_context
2519 .outer_inline_content_sizes(
2520 layout_context,
2521 containing_block,
2522 &auto_minimum,
2523 automatic_cross_size_for_intrinsic_sizing == Size::Stretch,
2524 false, );
2526 (sizes, depends_on_block_constraints)
2527 },
2528 FlexAxis::Column => {
2529 let size = self.layout_for_block_content_size(
2530 flex_context_getter(),
2531 &pbm_auto_is_zero,
2532 &content_box_sizes_and_pbm.content_box_sizes,
2533 preferred_aspect_ratio,
2534 automatic_cross_size_for_intrinsic_sizing,
2535 IntrinsicSizingMode::Contribution,
2536 );
2537 (size.into(), true)
2538 },
2539 };
2540
2541 let outer_flex_base_size = flex_base_size + pbm_auto_is_zero.main;
2542 let outer_min_main_size = content_min_main_size + pbm_auto_is_zero.main;
2543 let outer_max_main_size = content_max_main_size.map(|v| v + pbm_auto_is_zero.main);
2544 let max_flex_factors = self.desired_flex_factors_for_preferred_width(
2545 content_contribution_sizes.max_content,
2546 flex_base_size,
2547 outer_flex_base_size,
2548 );
2549
2550 let min_flex_factors = self.desired_flex_factors_for_preferred_width(
2554 content_contribution_sizes.min_content,
2555 flex_base_size,
2556 outer_flex_base_size,
2557 );
2558
2559 let mut min_content_main_size_for_multiline_container =
2566 content_contribution_sizes.min_content;
2567 let style_position = &self.style().get_position();
2568 if style_position.flex_grow.is_zero() {
2569 min_content_main_size_for_multiline_container.min_assign(outer_flex_base_size);
2570 }
2571 if style_position.flex_shrink.is_zero() {
2572 min_content_main_size_for_multiline_container.max_assign(outer_flex_base_size);
2573 }
2574 min_content_main_size_for_multiline_container =
2575 min_content_main_size_for_multiline_container
2576 .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
2577
2578 FlexItemBoxInlineContentSizesInfo {
2579 outer_flex_base_size,
2580 outer_min_main_size,
2581 outer_max_main_size,
2582 min_flex_factors,
2583 max_flex_factors,
2584 min_content_main_size_for_multiline_container,
2585 depends_on_block_constraints,
2586 }
2587 }
2588
2589 fn desired_flex_factors_for_preferred_width(
2590 &self,
2591 preferred_width: Au,
2592 flex_base_size: Au,
2593 outer_flex_base_size: Au,
2594 ) -> DesiredFlexFractionAndGrowOrShrinkFactor {
2595 let difference = (preferred_width - outer_flex_base_size).to_f32_px();
2596 let (flex_grow_or_scaled_flex_shrink_factor, desired_flex_fraction) = if difference > 0.0 {
2597 let flex_grow_factor = self.style().get_position().flex_grow.0;
2601
2602 (
2603 flex_grow_factor,
2604 if flex_grow_factor >= 1.0 {
2605 difference / flex_grow_factor
2606 } else {
2607 difference * flex_grow_factor
2608 },
2609 )
2610 } else if difference < 0.0 {
2611 let flex_shrink_factor = self.style().get_position().flex_shrink.0;
2615 let scaled_flex_shrink_factor = flex_shrink_factor * flex_base_size.to_f32_px();
2616
2617 (
2618 scaled_flex_shrink_factor,
2619 if scaled_flex_shrink_factor != 0.0 {
2620 difference / scaled_flex_shrink_factor
2621 } else {
2622 f32::NEG_INFINITY
2623 },
2624 )
2625 } else {
2626 (0.0, 0.0)
2627 };
2628
2629 DesiredFlexFractionAndGrowOrShrinkFactor {
2630 desired_flex_fraction,
2631 flex_grow_or_shrink_factor: flex_grow_or_scaled_flex_shrink_factor,
2632 }
2633 }
2634
2635 fn flex_basis(
2641 &self,
2642 container_definite_main_size: Option<Au>,
2643 main_preferred_size: Size<Au>,
2644 main_padding_border_sum: Au,
2645 ) -> Size<Au> {
2646 let style_position = &self.independent_formatting_context.style().get_position();
2647 match &style_position.flex_basis {
2648 FlexBasis::Content => Size::Initial,
2651
2652 FlexBasis::Size(size) => match Size::<LengthPercentage>::from(size) {
2653 Size::Initial => main_preferred_size,
2657
2658 size => {
2668 let apply_box_sizing = |length: Au| {
2669 match style_position.box_sizing {
2670 BoxSizing::ContentBox => length,
2671 BoxSizing::BorderBox => length - main_padding_border_sum,
2674 }
2675 };
2676 size.resolve_percentages_for_preferred(container_definite_main_size)
2677 .map(apply_box_sizing)
2678 },
2679 },
2680 }
2681 }
2682
2683 #[servo_tracing::instrument(name = "FlexContainer::layout_for_block_content_size", skip_all)]
2684 fn layout_for_block_content_size(
2685 &self,
2686 flex_context: &FlexContext,
2687 pbm_auto_is_zero: &FlexRelativeVec2<Au>,
2688 content_box_sizes: &LogicalVec2<Sizes>,
2689 preferred_aspect_ratio: Option<AspectRatio>,
2690 automatic_inline_size: Size<Au>,
2691 intrinsic_sizing_mode: IntrinsicSizingMode,
2692 ) -> Au {
2693 let content_block_size = || {
2694 let mut positioning_context = PositioningContext::default();
2695
2696 let tentative_block_size = SizeConstraint::default();
2700
2701 let inline_size = {
2704 let stretch_size = flex_context
2705 .containing_block
2706 .inline_stretch_size(pbm_auto_is_zero.cross);
2707 let get_content_size = || {
2708 self.inline_content_sizes(
2709 flex_context,
2710 tentative_block_size,
2711 preferred_aspect_ratio,
2712 )
2713 };
2714 content_box_sizes.inline.resolve(
2715 LogicalAxis::Inline,
2716 automatic_inline_size,
2717 Au::zero,
2718 stretch_size,
2719 get_content_size,
2720 false,
2721 )
2722 };
2723
2724 self.independent_formatting_context
2725 .content_block_size_under_default_constraint(
2726 flex_context.layout_context,
2727 &mut positioning_context,
2728 inline_size,
2729 flex_context.containing_block,
2730 preferred_aspect_ratio,
2731 &LazySize::intrinsic(),
2732 )
2733 };
2734 match intrinsic_sizing_mode {
2735 IntrinsicSizingMode::Contribution => {
2736 let stretch_size = flex_context
2737 .containing_block
2738 .block_stretch_size(pbm_auto_is_zero.main);
2739 let inner_block_size = content_box_sizes.block.resolve(
2740 LogicalAxis::Block,
2741 Size::FitContent,
2742 Au::zero,
2743 stretch_size,
2744 || ContentSizes::from(content_block_size()),
2745 false, );
2753 inner_block_size + pbm_auto_is_zero.main
2754 },
2755 IntrinsicSizingMode::Size => content_block_size(),
2756 }
2757 }
2758
2759 fn inline_content_sizes(
2760 &self,
2761 flex_context: &FlexContext,
2762 block_size: SizeConstraint,
2763 preferred_aspect_ratio: Option<AspectRatio>,
2764 ) -> ContentSizes {
2765 let style = self.independent_formatting_context.style();
2766 let constraint_space = ConstraintSpace::new(block_size, style, preferred_aspect_ratio);
2767 self.independent_formatting_context
2768 .inline_content_sizes(flex_context.layout_context, &constraint_space)
2769 .sizes
2770 }
2771}