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