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::formatting_contexts::Baselines;
31use crate::fragment_tree::{
32 BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
33};
34use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
35use crate::layout_box_base::CacheableLayoutResult;
36use crate::positioned::{
37 AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
38};
39use crate::sizing::{
40 ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, IntrinsicSizingMode,
41 LazySize, Size, SizeConstraint, Sizes,
42};
43use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, LayoutStyle};
44use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
45
46struct FlexContext<'a> {
49 config: FlexContainerConfig,
50 layout_context: &'a LayoutContext<'a>,
51 containing_block: &'a ContainingBlock<'a>, container_inner_size_constraint: FlexRelativeVec2<SizeConstraint>,
53}
54
55struct FlexItem<'a> {
57 box_: &'a FlexItemBox,
58
59 content_cross_sizes: Sizes,
61
62 padding: FlexRelativeSides<Au>,
63 border: FlexRelativeSides<Au>,
64 margin: FlexRelativeSides<AuOrAuto>,
65
66 pbm_auto_is_zero: FlexRelativeVec2<Au>,
69
70 flex_base_size: Au,
72
73 flex_base_size_is_definite: bool,
77
78 hypothetical_main_size: Au,
80
81 content_min_main_size: Au,
84
85 content_max_main_size: Option<Au>,
88
89 align_self: AlignItems,
91
92 depends_on_block_constraints: bool,
94
95 preferred_aspect_ratio: Option<AspectRatio>,
97
98 automatic_cross_size: Size<Au>,
101 automatic_cross_size_for_intrinsic_sizing: Size<Au>,
102}
103
104enum FlexContent {
107 AbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
108 FlexItemPlaceholder,
109}
110
111struct FlexItemLayoutResult {
113 hypothetical_cross_size: Au,
114 fragments: Vec<Fragment>,
115 positioning_context: PositioningContext,
116
117 baseline_relative_to_margin_box: Option<Au>,
119
120 content_block_size: Au,
123
124 containing_block_size: ContainingBlockSize,
126
127 depends_on_block_constraints: bool,
129
130 specific_layout_info: Option<SpecificLayoutInfo>,
132}
133
134struct FlexLineItem<'a> {
137 item: FlexItem<'a>,
139
140 layout_result: FlexItemLayoutResult,
143
144 used_main_size: Au,
146}
147
148impl FlexLineItem<'_> {
149 fn get_or_synthesize_baseline_with_cross_size(&self, cross_size: Au) -> Au {
150 self.layout_result
151 .baseline_relative_to_margin_box
152 .unwrap_or_else(|| {
153 self.item
154 .synthesized_baseline_relative_to_margin_box(cross_size)
155 })
156 }
157
158 #[allow(clippy::too_many_arguments)]
159 fn collect_fragment(
160 mut self,
161 initial_flex_layout: &InitialFlexLineLayout,
162 item_used_size: FlexRelativeVec2<Au>,
163 item_margin: FlexRelativeSides<Au>,
164 item_main_interval: Au,
165 final_line_cross_size: Au,
166 shared_alignment_baseline: &Option<Au>,
167 flex_context: &mut FlexContext,
168 all_baselines: &mut Baselines,
169 main_position_cursor: &mut Au,
170 ) -> (ArcRefCell<BoxFragment>, PositioningContext) {
171 *main_position_cursor +=
174 item_margin.main_start + self.item.border.main_start + self.item.padding.main_start;
175 let item_content_main_start_position = *main_position_cursor;
176
177 *main_position_cursor += item_used_size.main +
178 self.item.padding.main_end +
179 self.item.border.main_end +
180 item_margin.main_end +
181 item_main_interval;
182
183 let item_content_cross_start_position = self.item.align_along_cross_axis(
185 &item_margin,
186 &item_used_size.cross,
187 final_line_cross_size,
188 self.layout_result
189 .baseline_relative_to_margin_box
190 .unwrap_or_default(),
191 shared_alignment_baseline.unwrap_or_default(),
192 flex_context.config.flex_wrap_is_reversed,
193 );
194
195 let start_corner = FlexRelativeVec2 {
196 main: item_content_main_start_position,
197 cross: item_content_cross_start_position,
198 };
199
200 let final_line_size = FlexRelativeVec2 {
202 main: initial_flex_layout.line_size.main,
203 cross: final_line_cross_size,
204 };
205 let content_rect = flex_context.rect_to_flow_relative(
206 final_line_size,
207 FlexRelativeRect {
208 start_corner,
209 size: item_used_size,
210 },
211 );
212
213 if let Some(item_baseline) = self.layout_result.baseline_relative_to_margin_box.as_ref() {
214 let item_baseline = *item_baseline + item_content_cross_start_position -
215 self.item.border.cross_start -
216 self.item.padding.cross_start -
217 item_margin.cross_start;
218 all_baselines.first.get_or_insert(item_baseline);
219 all_baselines.last = Some(item_baseline);
220 }
221
222 let mut fragment_info = self.item.box_.base_fragment_info();
223 fragment_info
224 .flags
225 .insert(FragmentFlags::IS_FLEX_OR_GRID_ITEM);
226 if self.item.depends_on_block_constraints {
227 fragment_info.flags.insert(
228 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
229 );
230 }
231 let flags = fragment_info.flags;
232
233 let containing_block = flex_context.containing_block;
234 let container_writing_mode = containing_block.style.writing_mode;
235 let style = self.item.box_.style();
236
237 let mut fragment = BoxFragment::new(
238 fragment_info,
239 style.clone(),
240 self.layout_result.fragments,
241 content_rect.as_physical(Some(flex_context.containing_block)),
242 flex_context
243 .sides_to_flow_relative(self.item.padding)
244 .to_physical(container_writing_mode),
245 flex_context
246 .sides_to_flow_relative(self.item.border)
247 .to_physical(container_writing_mode),
248 flex_context
249 .sides_to_flow_relative(item_margin)
250 .to_physical(container_writing_mode),
251 self.layout_result.specific_layout_info,
252 );
253
254 if style.establishes_containing_block_for_absolute_descendants(flags) {
258 self.layout_result
259 .positioning_context
260 .layout_collected_children(flex_context.layout_context, &mut fragment);
261 }
262
263 if style.clone_position() == Position::Relative {
264 fragment.content_rect.origin += relative_adjustement(style, containing_block)
265 .to_physical_size(containing_block.style.writing_mode)
266 }
267
268 let fragment = ArcRefCell::new(fragment);
269 self.item
270 .box_
271 .independent_formatting_context
272 .base
273 .set_fragment(Fragment::Box(fragment.clone()));
274 (fragment, self.layout_result.positioning_context)
275 }
276}
277
278struct FinalFlexLineLayout {
281 cross_size: Au,
283 item_fragments: Vec<(ArcRefCell<BoxFragment>, PositioningContext)>,
286 shared_alignment_baseline: Option<Au>,
289 all_baselines: Baselines,
294}
295
296impl FlexContainerConfig {
297 fn resolve_reversable_flex_alignment(
298 &self,
299 align_flags: AlignFlags,
300 reversed: bool,
301 ) -> AlignFlags {
302 match (align_flags.value(), reversed) {
303 (AlignFlags::FLEX_START, false) => AlignFlags::START | align_flags.flags(),
304 (AlignFlags::FLEX_START, true) => AlignFlags::END | align_flags.flags(),
305 (AlignFlags::FLEX_END, false) => AlignFlags::END | align_flags.flags(),
306 (AlignFlags::FLEX_END, true) => AlignFlags::START | align_flags.flags(),
307 (_, _) => align_flags,
308 }
309 }
310
311 fn resolve_align_self_for_child(&self, child_style: &ComputedValues) -> AlignFlags {
312 self.resolve_reversable_flex_alignment(
313 child_style
314 .resolve_align_self(self.align_items, AlignItems(AlignFlags::STRETCH))
315 .0,
316 self.flex_wrap_is_reversed,
317 )
318 }
319
320 fn resolve_justify_content_for_child(&self) -> AlignFlags {
321 self.resolve_reversable_flex_alignment(
322 self.justify_content.0.primary(),
323 self.flex_direction_is_reversed,
324 )
325 }
326
327 fn sides_to_flex_relative<T>(&self, sides: LogicalSides<T>) -> FlexRelativeSides<T> {
328 self.main_start_cross_start_sides_are
329 .sides_to_flex_relative(sides)
330 }
331
332 fn sides_to_flow_relative<T>(&self, sides: FlexRelativeSides<T>) -> LogicalSides<T> {
333 self.main_start_cross_start_sides_are
334 .sides_to_flow_relative(sides)
335 }
336}
337
338impl FlexContext<'_> {
339 #[inline]
340 fn sides_to_flow_relative<T>(&self, x: FlexRelativeSides<T>) -> LogicalSides<T> {
341 self.config.sides_to_flow_relative(x)
342 }
343
344 #[inline]
345 fn rect_to_flow_relative(
346 &self,
347 base_rect_size: FlexRelativeVec2<Au>,
348 rect: FlexRelativeRect<Au>,
349 ) -> LogicalRect<Au> {
350 super::geom::rect_to_flow_relative(
351 self.config.flex_axis,
352 self.config.main_start_cross_start_sides_are,
353 base_rect_size,
354 rect,
355 )
356 }
357}
358
359#[derive(Debug, Default)]
360struct DesiredFlexFractionAndGrowOrShrinkFactor {
361 desired_flex_fraction: f32,
362 flex_grow_or_shrink_factor: f32,
363}
364
365#[derive(Default)]
366struct FlexItemBoxInlineContentSizesInfo {
367 outer_flex_base_size: Au,
368 outer_min_main_size: Au,
369 outer_max_main_size: Option<Au>,
370 min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
371 max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
372 min_content_main_size_for_multiline_container: Au,
373 depends_on_block_constraints: bool,
374}
375
376impl ComputeInlineContentSizes for FlexContainer {
377 #[servo_tracing::instrument(name = "FlexContainer::compute_inline_content_sizes", skip_all)]
378 fn compute_inline_content_sizes(
379 &self,
380 layout_context: &LayoutContext,
381 constraint_space: &ConstraintSpace,
382 ) -> InlineContentSizesResult {
383 match self.config.flex_axis {
384 FlexAxis::Row => {
385 self.main_content_sizes(layout_context, &constraint_space.into(), || {
386 unreachable!(
387 "Unexpected FlexContext query during row flex intrinsic size calculation."
388 )
389 })
390 },
391 FlexAxis::Column => self.cross_content_sizes(layout_context, &constraint_space.into()),
392 }
393 }
394}
395
396impl FlexContainer {
397 fn cross_content_sizes(
398 &self,
399 layout_context: &LayoutContext,
400 containing_block_for_children: &IndefiniteContainingBlock,
401 ) -> InlineContentSizesResult {
402 assert_eq!(
404 self.config.flex_axis,
405 FlexAxis::Column,
406 "The cross axis should be the inline one"
407 );
408 let mut sizes = ContentSizes::zero();
409 let mut depends_on_block_constraints = false;
410 for kid in self.children.iter() {
411 let kid = &*kid.borrow();
412 match kid {
413 FlexLevelBox::FlexItem(item) => {
414 let ifc = &item.independent_formatting_context;
418 let result = ifc.outer_inline_content_sizes(
419 layout_context,
420 containing_block_for_children,
421 &LogicalVec2::zero(),
422 false, );
424 sizes.max_assign(result.sizes);
425 depends_on_block_constraints |= result.depends_on_block_constraints;
426 },
427 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
428 }
429 }
430 InlineContentSizesResult {
431 sizes,
432 depends_on_block_constraints,
433 }
434 }
435
436 fn main_content_sizes<'a>(
437 &self,
438 layout_context: &LayoutContext,
439 containing_block_for_children: &IndefiniteContainingBlock,
440 flex_context_getter: impl Fn() -> &'a FlexContext<'a>,
441 ) -> InlineContentSizesResult {
442 let mut chosen_max_flex_fraction = f32::NEG_INFINITY;
451 let mut chosen_min_flex_fraction = f32::NEG_INFINITY;
452 let mut sum_of_flex_grow_factors = 0.0;
453 let mut sum_of_flex_shrink_factors = 0.0;
454 let mut item_infos = vec![];
455
456 for kid in self.children.iter() {
457 let kid = &*kid.borrow();
458 match kid {
459 FlexLevelBox::FlexItem(item) => {
460 sum_of_flex_grow_factors += item.style().get_position().flex_grow.0;
461 sum_of_flex_shrink_factors += item.style().get_position().flex_shrink.0;
462
463 let info = item.main_content_size_info(
464 layout_context,
465 containing_block_for_children,
466 &self.config,
467 &flex_context_getter,
468 );
469
470 chosen_max_flex_fraction =
475 chosen_max_flex_fraction.max(info.max_flex_factors.desired_flex_fraction);
476 chosen_min_flex_fraction =
477 chosen_min_flex_fraction.max(info.min_flex_factors.desired_flex_fraction);
478
479 item_infos.push(info)
480 },
481 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
482 }
483 }
484
485 let normalize_flex_fraction = |chosen_flex_fraction| {
486 if chosen_flex_fraction > 0.0 && sum_of_flex_grow_factors < 1.0 {
487 chosen_flex_fraction / sum_of_flex_grow_factors
491 } else if chosen_flex_fraction < 0.0 && sum_of_flex_shrink_factors < 1.0 {
492 chosen_flex_fraction * sum_of_flex_shrink_factors
496 } else {
497 chosen_flex_fraction
498 }
499 };
500
501 let chosen_min_flex_fraction = normalize_flex_fraction(chosen_min_flex_fraction);
502 let chosen_max_flex_fraction = normalize_flex_fraction(chosen_max_flex_fraction);
503
504 let main_gap = match self.config.flex_axis {
505 FlexAxis::Row => self.style.clone_column_gap(),
506 FlexAxis::Column => self.style.clone_row_gap(),
507 };
508 let main_gap = match main_gap {
509 LengthPercentageOrNormal::LengthPercentage(length_percentage) => {
510 length_percentage.to_used_value(Au::zero())
511 },
512 LengthPercentageOrNormal::Normal => Au::zero(),
513 };
514 let extra_space_from_main_gap = main_gap * (item_infos.len() as i32 - 1);
515 let mut container_max_content_size = extra_space_from_main_gap;
516 let mut container_min_content_size = if self.config.flex_wrap == FlexWrap::Nowrap {
517 extra_space_from_main_gap
518 } else {
519 Au::zero()
520 };
521 let mut container_depends_on_block_constraints = false;
522
523 for FlexItemBoxInlineContentSizesInfo {
524 outer_flex_base_size,
525 outer_min_main_size,
526 outer_max_main_size,
527 min_flex_factors,
528 max_flex_factors,
529 min_content_main_size_for_multiline_container,
530 depends_on_block_constraints,
531 } in item_infos.iter()
532 {
533 container_max_content_size += (*outer_flex_base_size +
539 Au::from_f32_px(
540 max_flex_factors.flex_grow_or_shrink_factor * chosen_max_flex_fraction,
541 ))
542 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
543
544 if self.config.flex_wrap == FlexWrap::Nowrap {
555 container_min_content_size += (*outer_flex_base_size +
556 Au::from_f32_px(
557 min_flex_factors.flex_grow_or_shrink_factor * chosen_min_flex_fraction,
558 ))
559 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
560 } else {
561 container_min_content_size
562 .max_assign(*min_content_main_size_for_multiline_container);
563 }
564
565 container_depends_on_block_constraints |= depends_on_block_constraints;
566 }
567
568 InlineContentSizesResult {
569 sizes: ContentSizes {
570 min_content: container_min_content_size,
571 max_content: container_max_content_size,
572 },
573 depends_on_block_constraints: container_depends_on_block_constraints,
574 }
575 }
576
577 #[servo_tracing::instrument(
579 name = "FlexContainer::layout",
580 skip_all,
581 fields(self_address = self as *const _ as usize)
582 )]
583 pub(crate) fn layout(
584 &self,
585 layout_context: &LayoutContext,
586 positioning_context: &mut PositioningContext,
587 containing_block: &ContainingBlock,
588 lazy_block_size: &LazySize,
589 ) -> CacheableLayoutResult {
590 let mut flex_context = FlexContext {
591 config: self.config.clone(),
592 layout_context,
593 containing_block,
594 container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative(
596 LogicalVec2 {
597 inline: SizeConstraint::Definite(containing_block.size.inline),
598 block: containing_block.size.block,
599 },
600 ),
601 };
602
603 let container_main_size = match self.config.flex_axis {
606 FlexAxis::Row => containing_block.size.inline,
607 FlexAxis::Column => lazy_block_size.resolve(|| {
608 let mut containing_block = IndefiniteContainingBlock::from(containing_block);
609 containing_block.size.block = None;
610 self.main_content_sizes(layout_context, &containing_block, || &flex_context)
611 .sizes
612 .max_content
613 }),
614 };
615
616 let mut flex_items = Vec::with_capacity(self.children.len());
618
619 let absolutely_positioned_items_with_original_order = self
627 .children
628 .iter()
629 .map(|arcrefcell| {
630 let borrowed = arcrefcell.borrow();
631 match &*borrowed {
632 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned) => {
633 FlexContent::AbsolutelyPositionedBox(absolutely_positioned.clone())
634 },
635 FlexLevelBox::FlexItem(_) => {
636 let item = AtomicRef::map(borrowed, |child| match child {
637 FlexLevelBox::FlexItem(item) => item,
638 _ => unreachable!(),
639 });
640 flex_items.push(item);
641 FlexContent::FlexItemPlaceholder
642 },
643 }
644 })
645 .collect::<Vec<_>>();
646
647 let flex_item_boxes = flex_items.iter().map(|child| &**child);
648 let flex_items = flex_item_boxes
649 .map(|flex_item_box| FlexItem::new(&flex_context, flex_item_box))
650 .collect::<Vec<_>>();
651
652 let row_gap = self.style.clone_row_gap();
653 let column_gap = self.style.clone_column_gap();
654 let (cross_gap, main_gap) = match flex_context.config.flex_axis {
655 FlexAxis::Row => (row_gap, column_gap),
656 FlexAxis::Column => (column_gap, row_gap),
657 };
658 let cross_gap = match cross_gap {
659 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
660 .maybe_to_used_value(
661 flex_context
662 .container_inner_size_constraint
663 .cross
664 .to_definite(),
665 )
666 .unwrap_or_default(),
667 LengthPercentageOrNormal::Normal => Au::zero(),
668 };
669 let main_gap = match main_gap {
670 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
671 .maybe_to_used_value(
672 flex_context
673 .container_inner_size_constraint
674 .main
675 .to_definite(),
676 )
677 .unwrap_or_default(),
678 LengthPercentageOrNormal::Normal => Au::zero(),
679 };
680
681 let initial_line_layouts = do_initial_flex_line_layout(
684 &mut flex_context,
685 container_main_size,
686 flex_items,
687 main_gap,
688 );
689
690 let line_count = initial_line_layouts.len();
691 let content_cross_size = initial_line_layouts
692 .iter()
693 .map(|layout| layout.line_size.cross)
694 .sum::<Au>() +
695 cross_gap * (line_count as i32 - 1);
696 let content_block_size = match self.config.flex_axis {
697 FlexAxis::Row => content_cross_size,
698 FlexAxis::Column => container_main_size,
699 };
700
701 let container_cross_size = match self.config.flex_axis {
703 FlexAxis::Row => lazy_block_size.resolve(|| content_cross_size),
704 FlexAxis::Column => containing_block.size.inline,
705 };
706
707 let container_size = FlexRelativeVec2 {
708 main: container_main_size,
709 cross: container_cross_size,
710 };
711
712 let mut remaining_free_cross_space = container_cross_size - content_cross_size;
713
714 let num_lines = initial_line_layouts.len();
719 let resolved_align_content: AlignFlags = {
720 let align_content_style = flex_context.config.align_content.0.primary();
722 let mut is_safe = align_content_style.flags() == AlignFlags::SAFE;
723
724 let mut resolved_align_content = match align_content_style.value() {
727 AlignFlags::NORMAL => AlignFlags::STRETCH,
728 align_content => align_content,
729 };
730
731 let fallback_is_needed = match resolved_align_content {
739 _ if remaining_free_cross_space <= Au::zero() => true,
740 AlignFlags::STRETCH => num_lines < 1,
741 AlignFlags::SPACE_BETWEEN | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
742 num_lines < 2
743 },
744 _ => false,
745 };
746
747 if fallback_is_needed {
748 (resolved_align_content, is_safe) = match resolved_align_content {
749 AlignFlags::STRETCH => (AlignFlags::FLEX_START, false),
750 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
751 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
752 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
753 _ => (resolved_align_content, is_safe),
754 }
755 };
756
757 if remaining_free_cross_space <= Au::zero() && is_safe {
759 resolved_align_content = AlignFlags::START;
760 }
761
762 resolved_align_content
763 };
764
765 let flex_wrap_is_reversed = flex_context.config.flex_wrap_is_reversed;
767 let resolved_align_content = self
768 .config
769 .resolve_reversable_flex_alignment(resolved_align_content, flex_wrap_is_reversed);
770 let mut cross_start_position_cursor = match resolved_align_content {
771 AlignFlags::START if flex_wrap_is_reversed => remaining_free_cross_space,
772 AlignFlags::START => Au::zero(),
773 AlignFlags::END if flex_wrap_is_reversed => Au::zero(),
774 AlignFlags::END => remaining_free_cross_space,
775 AlignFlags::CENTER => remaining_free_cross_space / 2,
776 AlignFlags::STRETCH => Au::zero(),
777 AlignFlags::SPACE_BETWEEN => Au::zero(),
778 AlignFlags::SPACE_AROUND => remaining_free_cross_space / num_lines as i32 / 2,
779 AlignFlags::SPACE_EVENLY => remaining_free_cross_space / (num_lines as i32 + 1),
780
781 _ => Au::zero(),
783 };
784
785 let inline_axis_is_main_axis = self.config.flex_axis == FlexAxis::Row;
786 let mut baseline_alignment_participating_baselines = Baselines::default();
787 let mut all_baselines = Baselines::default();
788 let flex_item_fragments: Vec<_> = initial_line_layouts
789 .into_iter()
790 .enumerate()
791 .flat_map(|(index, initial_line_layout)| {
792 let (space_to_add_to_line, space_to_add_after_line) =
796 allocate_free_cross_space_for_flex_line(
797 resolved_align_content,
798 remaining_free_cross_space,
799 (num_lines - index) as i32,
800 );
801 remaining_free_cross_space -= space_to_add_to_line + space_to_add_after_line;
802
803 let final_line_cross_size =
804 initial_line_layout.line_size.cross + space_to_add_to_line;
805 let mut final_line_layout = initial_line_layout.finish_with_final_cross_size(
806 &mut flex_context,
807 main_gap,
808 final_line_cross_size,
809 );
810
811 let line_cross_start_position = cross_start_position_cursor;
812 cross_start_position_cursor = line_cross_start_position +
813 final_line_cross_size +
814 space_to_add_after_line +
815 cross_gap;
816
817 let flow_relative_line_position =
818 match (self.config.flex_axis, flex_wrap_is_reversed) {
819 (FlexAxis::Row, false) => LogicalVec2 {
820 block: line_cross_start_position,
821 inline: Au::zero(),
822 },
823 (FlexAxis::Row, true) => LogicalVec2 {
824 block: container_cross_size -
825 line_cross_start_position -
826 final_line_layout.cross_size,
827 inline: Au::zero(),
828 },
829 (FlexAxis::Column, false) => LogicalVec2 {
830 block: Au::zero(),
831 inline: line_cross_start_position,
832 },
833 (FlexAxis::Column, true) => LogicalVec2 {
834 block: Au::zero(),
835 inline: container_cross_size -
836 line_cross_start_position -
837 final_line_cross_size,
838 },
839 };
840
841 if inline_axis_is_main_axis {
842 let line_shared_alignment_baseline = final_line_layout
843 .shared_alignment_baseline
844 .map(|baseline| baseline + flow_relative_line_position.block);
845 if index == 0 {
846 baseline_alignment_participating_baselines.first =
847 line_shared_alignment_baseline;
848 }
849 if index == num_lines - 1 {
850 baseline_alignment_participating_baselines.last =
851 line_shared_alignment_baseline;
852 }
853 }
854
855 let line_all_baselines = final_line_layout
856 .all_baselines
857 .offset(flow_relative_line_position.block);
858 if index == 0 {
859 all_baselines.first = line_all_baselines.first;
860 }
861 if index == num_lines - 1 {
862 all_baselines.last = line_all_baselines.last;
863 }
864
865 let physical_line_position =
866 flow_relative_line_position.to_physical_size(self.style.writing_mode);
867 for (fragment, _) in &mut final_line_layout.item_fragments {
868 fragment.borrow_mut().content_rect.origin += physical_line_position;
869 }
870 final_line_layout.item_fragments
871 })
872 .collect();
873
874 let mut flex_item_fragments = flex_item_fragments.into_iter();
875 let fragments = absolutely_positioned_items_with_original_order
876 .into_iter()
877 .map(|child_as_abspos| match child_as_abspos {
878 FlexContent::AbsolutelyPositionedBox(absolutely_positioned_box) => self
879 .create_absolutely_positioned_flex_child_fragment(
880 absolutely_positioned_box,
881 containing_block,
882 container_size,
883 positioning_context,
884 ),
885 FlexContent::FlexItemPlaceholder => {
886 let (fragment, mut child_positioning_context) =
889 flex_item_fragments.next().unwrap();
890 let fragment = Fragment::Box(fragment);
891 child_positioning_context.adjust_static_position_of_hoisted_fragments(
892 &fragment,
893 PositioningContextLength::zero(),
894 );
895 positioning_context.append(child_positioning_context);
896 fragment
897 },
898 })
899 .collect::<Vec<_>>();
900
901 assert!(flex_item_fragments.next().is_none());
903
904 let baselines = Baselines {
905 first: baseline_alignment_participating_baselines
906 .first
907 .or(all_baselines.first),
908 last: baseline_alignment_participating_baselines
909 .last
910 .or(all_baselines.last),
911 };
912
913 let depends_on_block_constraints = true;
926
927 CacheableLayoutResult {
928 fragments,
929 content_block_size,
930 content_inline_size_for_table: None,
931 baselines,
932 depends_on_block_constraints,
933 specific_layout_info: None,
934 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
935 }
936 }
937
938 fn create_absolutely_positioned_flex_child_fragment(
954 &self,
955 absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
956 containing_block: &ContainingBlock,
957 container_size: FlexRelativeVec2<Au>,
958 positioning_context: &mut PositioningContext,
959 ) -> Fragment {
960 let alignment = {
961 let fragment = absolutely_positioned_box.borrow();
962 let make_flex_only_values_directional_for_absolutes =
963 |value: AlignFlags, reversed: bool| match (value.value(), reversed) {
964 (AlignFlags::NORMAL | AlignFlags::AUTO | AlignFlags::STRETCH, true) => {
965 AlignFlags::END | AlignFlags::SAFE
966 },
967 (AlignFlags::STRETCH, false) => AlignFlags::START | AlignFlags::SAFE,
968 (AlignFlags::SPACE_BETWEEN, false) => AlignFlags::START | AlignFlags::SAFE,
969 (AlignFlags::SPACE_BETWEEN, true) => AlignFlags::END | AlignFlags::SAFE,
970 _ => value,
971 };
972 let cross = make_flex_only_values_directional_for_absolutes(
973 self.config
974 .resolve_align_self_for_child(fragment.context.style()),
975 self.config.flex_wrap_is_reversed,
976 );
977 let main = make_flex_only_values_directional_for_absolutes(
978 self.config.resolve_justify_content_for_child(),
979 self.config.flex_direction_is_reversed,
980 );
981
982 FlexRelativeVec2 { cross, main }
983 };
984 let logical_alignment = self.config.flex_axis.vec2_to_flow_relative(alignment);
985
986 let static_position_rect = LogicalRect {
987 start_corner: LogicalVec2::zero(),
988 size: self.config.flex_axis.vec2_to_flow_relative(container_size),
989 }
990 .as_physical(Some(containing_block));
991
992 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
993 absolutely_positioned_box,
994 static_position_rect,
995 logical_alignment,
996 self.config.writing_mode,
997 );
998 let hoisted_fragment = hoisted_box.fragment.clone();
999 positioning_context.push(hoisted_box);
1000 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
1001 }
1002
1003 #[inline]
1004 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
1005 LayoutStyle::Default(&self.style)
1006 }
1007}
1008
1009fn allocate_free_cross_space_for_flex_line(
1013 resolved_align_content: AlignFlags,
1014 remaining_free_cross_space: Au,
1015 remaining_line_count: i32,
1016) -> (Au, Au) {
1017 if remaining_free_cross_space.is_zero() {
1018 return (Au::zero(), Au::zero());
1019 }
1020
1021 match resolved_align_content {
1022 AlignFlags::START => (Au::zero(), Au::zero()),
1023 AlignFlags::FLEX_START => (Au::zero(), Au::zero()),
1024 AlignFlags::END => (Au::zero(), Au::zero()),
1025 AlignFlags::FLEX_END => (Au::zero(), Au::zero()),
1026 AlignFlags::CENTER => (Au::zero(), Au::zero()),
1027 AlignFlags::STRETCH => (
1028 remaining_free_cross_space / remaining_line_count,
1029 Au::zero(),
1030 ),
1031 AlignFlags::SPACE_BETWEEN => {
1032 if remaining_line_count > 1 {
1033 (
1034 Au::zero(),
1035 remaining_free_cross_space / (remaining_line_count - 1),
1036 )
1037 } else {
1038 (Au::zero(), Au::zero())
1039 }
1040 },
1041 AlignFlags::SPACE_AROUND => (
1042 Au::zero(),
1043 remaining_free_cross_space / remaining_line_count,
1044 ),
1045 AlignFlags::SPACE_EVENLY => (
1046 Au::zero(),
1047 remaining_free_cross_space / (remaining_line_count + 1),
1048 ),
1049
1050 _ => (Au::zero(), Au::zero()),
1052 }
1053}
1054
1055impl<'a> FlexItem<'a> {
1056 fn new(flex_context: &FlexContext, box_: &'a FlexItemBox) -> Self {
1057 let containing_block = IndefiniteContainingBlock::from(flex_context.containing_block);
1058 let content_box_sizes_and_pbm = box_
1059 .independent_formatting_context
1060 .layout_style()
1061 .content_box_sizes_and_padding_border_margin(&containing_block);
1062 box_.to_flex_item(
1063 flex_context.layout_context,
1064 &containing_block,
1065 &content_box_sizes_and_pbm,
1066 &flex_context.config,
1067 &|| flex_context,
1068 )
1069 }
1070}
1071
1072fn cross_axis_is_item_block_axis(
1073 container_is_horizontal: bool,
1074 item_is_horizontal: bool,
1075 flex_axis: FlexAxis,
1076) -> bool {
1077 let item_is_orthogonal = item_is_horizontal != container_is_horizontal;
1078 let container_is_row = flex_axis == FlexAxis::Row;
1079
1080 container_is_row ^ item_is_orthogonal
1081}
1082
1083fn item_with_auto_cross_size_stretches_to_line_size(
1087 align_self: AlignItems,
1088 margin: &FlexRelativeSides<AuOrAuto>,
1089) -> bool {
1090 align_self.0.value() == AlignFlags::STRETCH &&
1091 !margin.cross_start.is_auto() &&
1092 !margin.cross_end.is_auto()
1093}
1094
1095fn do_initial_flex_line_layout<'items>(
1098 flex_context: &mut FlexContext,
1099 container_main_size: Au,
1100 mut items: Vec<FlexItem<'items>>,
1101 main_gap: Au,
1102) -> Vec<InitialFlexLineLayout<'items>> {
1103 let construct_line = |(items, outer_hypothetical_main_size)| {
1104 InitialFlexLineLayout::new(
1105 flex_context,
1106 items,
1107 outer_hypothetical_main_size,
1108 container_main_size,
1109 main_gap,
1110 )
1111 };
1112
1113 if flex_context.config.container_is_single_line {
1114 let outer_hypothetical_main_sizes_sum = items
1115 .iter()
1116 .map(|item| item.hypothetical_main_size + item.pbm_auto_is_zero.main)
1117 .sum();
1118 return vec![construct_line((items, outer_hypothetical_main_sizes_sum))];
1119 }
1120
1121 let mut lines = Vec::new();
1122 let mut line_size_so_far = Au::zero();
1123 let mut line_so_far_is_empty = true;
1124 let mut index = 0;
1125
1126 while let Some(item) = items.get(index) {
1127 let item_size = item.hypothetical_main_size + item.pbm_auto_is_zero.main;
1128 let mut line_size_would_be = line_size_so_far + item_size;
1129 if !line_so_far_is_empty {
1130 line_size_would_be += main_gap;
1131 }
1132 let item_fits = line_size_would_be <= container_main_size;
1133 if item_fits || line_so_far_is_empty {
1134 line_size_so_far = line_size_would_be;
1135 line_so_far_is_empty = false;
1136 index += 1;
1137 continue;
1138 }
1139
1140 let remaining = items.split_off(index);
1142 lines.push((items, line_size_so_far));
1143 items = remaining;
1144
1145 line_size_so_far = item_size;
1147 index = 1;
1148 }
1149
1150 lines.push((items, line_size_so_far));
1152
1153 if flex_context.layout_context.use_rayon {
1154 lines.par_drain(..).map(construct_line).collect()
1155 } else {
1156 lines.drain(..).map(construct_line).collect()
1157 }
1158}
1159
1160struct InitialFlexLineLayout<'a> {
1164 items: Vec<FlexLineItem<'a>>,
1166
1167 line_size: FlexRelativeVec2<Au>,
1169
1170 free_space_in_main_axis: Au,
1172}
1173
1174impl InitialFlexLineLayout<'_> {
1175 fn new<'items>(
1176 flex_context: &FlexContext,
1177 items: Vec<FlexItem<'items>>,
1178 outer_hypothetical_main_sizes_sum: Au,
1179 container_main_size: Au,
1180 main_gap: Au,
1181 ) -> InitialFlexLineLayout<'items> {
1182 let item_count = items.len();
1183 let (item_used_main_sizes, free_space_in_main_axis) = Self::resolve_flexible_lengths(
1184 &items,
1185 outer_hypothetical_main_sizes_sum,
1186 container_main_size - main_gap * (item_count as i32 - 1),
1187 );
1188
1189 let layout_results: Vec<_> = if flex_context.layout_context.use_rayon {
1191 items
1192 .par_iter()
1193 .zip(&item_used_main_sizes)
1194 .map(|(item, used_main_size)| item.layout(*used_main_size, flex_context, None))
1195 .collect()
1196 } else {
1197 items
1198 .iter()
1199 .zip(&item_used_main_sizes)
1200 .map(|(item, used_main_size)| item.layout(*used_main_size, flex_context, None))
1201 .collect()
1202 };
1203
1204 let items: Vec<_> = izip!(
1205 items.into_iter(),
1206 layout_results.into_iter(),
1207 item_used_main_sizes.into_iter()
1208 )
1209 .map(|(item, layout_result, used_main_size)| FlexLineItem {
1210 item,
1211 layout_result,
1212 used_main_size,
1213 })
1214 .collect();
1215
1216 let line_cross_size = Self::cross_size(&items, flex_context);
1218 let line_size = FlexRelativeVec2 {
1219 main: container_main_size,
1220 cross: line_cross_size,
1221 };
1222
1223 InitialFlexLineLayout {
1224 items,
1225 line_size,
1226 free_space_in_main_axis,
1227 }
1228 }
1229
1230 fn resolve_flexible_lengths<'items>(
1233 items: &'items [FlexItem<'items>],
1234 outer_hypothetical_main_sizes_sum: Au,
1235 container_main_size: Au,
1236 ) -> (Vec<Au>, Au) {
1237 struct FlexibleLengthResolutionItem<'items> {
1238 item: &'items FlexItem<'items>,
1239 frozen: Cell<bool>,
1240 target_main_size: Cell<Au>,
1241 flex_factor: f32,
1242 }
1243
1244 let grow = outer_hypothetical_main_sizes_sum < container_main_size;
1249
1250 let mut frozen_count = 0;
1251 let items: Vec<_> = items
1252 .iter()
1253 .map(|item| {
1254 let target_main_size = Cell::new(item.flex_base_size);
1257
1258 let flex_factor = if grow {
1265 item.box_.style().get_position().flex_grow.0
1266 } else {
1267 item.box_.style().get_position().flex_shrink.0
1268 };
1269
1270 let is_inflexible = flex_factor == 0. ||
1271 if grow {
1272 item.flex_base_size > item.hypothetical_main_size
1273 } else {
1274 item.flex_base_size < item.hypothetical_main_size
1275 };
1276
1277 let frozen = Cell::new(false);
1278 if is_inflexible {
1279 frozen_count += 1;
1280 frozen.set(true);
1281 target_main_size.set(item.hypothetical_main_size);
1282 }
1283
1284 FlexibleLengthResolutionItem {
1285 item,
1286 frozen,
1287 target_main_size,
1288 flex_factor,
1289 }
1290 })
1291 .collect();
1292
1293 let unfrozen_items = || items.iter().filter(|item| !item.frozen.get());
1294 let main_sizes = |items: Vec<FlexibleLengthResolutionItem>| {
1295 items
1296 .into_iter()
1297 .map(|item| item.target_main_size.get())
1298 .collect()
1299 };
1300
1301 let free_space = |all_items_frozen| {
1306 let items_size = items
1307 .iter()
1308 .map(|item| {
1309 item.item.pbm_auto_is_zero.main +
1310 if all_items_frozen || item.frozen.get() {
1311 item.target_main_size.get()
1312 } else {
1313 item.item.flex_base_size
1314 }
1315 })
1316 .sum();
1317 container_main_size - items_size
1318 };
1319
1320 let initial_free_space = free_space(false);
1321 loop {
1322 let mut remaining_free_space = free_space(false);
1324 if frozen_count >= items.len() {
1327 return (main_sizes(items), remaining_free_space);
1328 }
1329
1330 let unfrozen_items_flex_factor_sum =
1336 unfrozen_items().map(|item| item.flex_factor).sum();
1337 if unfrozen_items_flex_factor_sum < 1. {
1338 let multiplied = initial_free_space.scale_by(unfrozen_items_flex_factor_sum);
1339 if multiplied.abs() < remaining_free_space.abs() {
1340 remaining_free_space = multiplied
1341 }
1342 }
1343
1344 if !remaining_free_space.is_zero() {
1350 if grow {
1356 for item in unfrozen_items() {
1357 let ratio = item.flex_factor / unfrozen_items_flex_factor_sum;
1358 item.target_main_size
1359 .set(item.item.flex_base_size + remaining_free_space.scale_by(ratio));
1360 }
1361 } else {
1370 let scaled_shrink_factor = |item: &FlexibleLengthResolutionItem| {
1372 item.item.flex_base_size.scale_by(item.flex_factor)
1373 };
1374 let scaled_shrink_factors_sum: Au =
1375 unfrozen_items().map(scaled_shrink_factor).sum();
1376 if scaled_shrink_factors_sum > Au::zero() {
1377 for item in unfrozen_items() {
1378 let ratio = scaled_shrink_factor(item).0 as f32 /
1379 scaled_shrink_factors_sum.0 as f32;
1380 item.target_main_size.set(
1381 item.item.flex_base_size -
1382 remaining_free_space.abs().scale_by(ratio),
1383 );
1384 }
1385 }
1386 }
1387 }
1388
1389 let violation = |item: &FlexibleLengthResolutionItem| {
1395 let size = item.target_main_size.get();
1396 let clamped = size.clamp_between_extremums(
1397 item.item.content_min_main_size,
1398 item.item.content_max_main_size,
1399 );
1400 clamped - size
1401 };
1402
1403 let total_violation: Au = unfrozen_items().map(violation).sum();
1410 match total_violation.cmp(&Au::zero()) {
1411 Ordering::Equal => {
1412 let remaining_free_space = free_space(true);
1415 return (main_sizes(items), remaining_free_space);
1416 },
1417 Ordering::Greater => {
1418 for item in items.iter() {
1422 if violation(item) > Au::zero() {
1423 item.target_main_size.set(item.item.content_min_main_size);
1424 item.frozen.set(true);
1425 frozen_count += 1;
1426 }
1427 }
1428 },
1429 Ordering::Less => {
1430 for item in items.iter() {
1435 if violation(item) < Au::zero() {
1436 let Some(max_size) = item.item.content_max_main_size else {
1437 unreachable!()
1438 };
1439 item.target_main_size.set(max_size);
1440 item.frozen.set(true);
1441 frozen_count += 1;
1442 }
1443 }
1444 },
1445 }
1446 }
1447 }
1448
1449 fn cross_size<'items>(items: &'items [FlexLineItem<'items>], flex_context: &FlexContext) -> Au {
1451 if flex_context.config.container_is_single_line {
1452 if let SizeConstraint::Definite(size) =
1453 flex_context.container_inner_size_constraint.cross
1454 {
1455 return size;
1456 }
1457 }
1458
1459 let mut max_ascent = Au::zero();
1460 let mut max_descent = Au::zero();
1461 let mut max_outer_hypothetical_cross_size = Au::zero();
1462 for item in items.iter() {
1463 if matches!(
1465 item.item.align_self.0.value(),
1466 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1467 ) {
1468 let baseline = item.get_or_synthesize_baseline_with_cross_size(
1469 item.layout_result.hypothetical_cross_size,
1470 );
1471 let hypothetical_margin_box_cross_size =
1472 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross;
1473 max_ascent = max_ascent.max(baseline);
1474 max_descent = max_descent.max(hypothetical_margin_box_cross_size - baseline);
1475 } else {
1476 max_outer_hypothetical_cross_size = max_outer_hypothetical_cross_size.max(
1477 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross,
1478 );
1479 }
1480 }
1481
1482 let largest = max_outer_hypothetical_cross_size.max(max_ascent + max_descent);
1484 match flex_context.container_inner_size_constraint.cross {
1485 SizeConstraint::MinMax(min, max) if flex_context.config.container_is_single_line => {
1486 largest.clamp_between_extremums(min, max)
1487 },
1488 _ => largest,
1489 }
1490 }
1491
1492 fn finish_with_final_cross_size(
1493 mut self,
1494 flex_context: &mut FlexContext,
1495 main_gap: Au,
1496 final_line_cross_size: Au,
1497 ) -> FinalFlexLineLayout {
1498 let auto_margins_count = self
1506 .items
1507 .iter()
1508 .map(|item| {
1509 item.item.margin.main_start.is_auto() as u32 +
1510 item.item.margin.main_end.is_auto() as u32
1511 })
1512 .sum::<u32>();
1513 let (space_distributed_to_auto_main_margins, free_space_in_main_axis) =
1514 if self.free_space_in_main_axis > Au::zero() && auto_margins_count > 0 {
1515 (
1516 self.free_space_in_main_axis / auto_margins_count as i32,
1517 Au::zero(),
1518 )
1519 } else {
1520 (Au::zero(), self.free_space_in_main_axis)
1521 };
1522
1523 let item_count = self.items.len();
1526 let mut shared_alignment_baseline = None;
1527 let mut item_used_cross_sizes = Vec::with_capacity(item_count);
1528 let mut item_margins = Vec::with_capacity(item_count);
1529 for item in self.items.iter_mut() {
1530 let cross_axis = match flex_context.config.flex_axis {
1531 FlexAxis::Row => Direction::Block,
1532 FlexAxis::Column => Direction::Inline,
1533 };
1534 let layout = &mut item.layout_result;
1535 let get_content_size = || match cross_axis {
1536 Direction::Block => layout.content_block_size.into(),
1537 Direction::Inline => item
1538 .item
1539 .inline_content_sizes(flex_context, item.used_main_size),
1540 };
1541 let used_cross_size = item.item.content_cross_sizes.resolve(
1542 cross_axis,
1543 item.item.automatic_cross_size,
1544 Au::zero,
1545 Some(Au::zero().max(final_line_cross_size - item.item.pbm_auto_is_zero.cross)),
1546 get_content_size,
1547 item.item.box_.independent_formatting_context.is_table() &&
1552 cross_axis == Direction::Inline,
1553 );
1554 item_used_cross_sizes.push(used_cross_size);
1555
1556 let needs_new_layout = match cross_axis {
1563 Direction::Block => {
1564 (match item.item.content_cross_sizes.preferred {
1565 Size::Initial => item.item.automatic_cross_size == Size::Stretch,
1566 Size::Stretch => true,
1567 _ => false,
1568 }) && SizeConstraint::Definite(used_cross_size) !=
1569 layout.containing_block_size.block &&
1570 layout.depends_on_block_constraints
1571 },
1572 Direction::Inline => used_cross_size != layout.containing_block_size.inline,
1573 };
1574 if needs_new_layout {
1575 #[cfg(feature = "tracing")]
1576 tracing::warn!(
1577 name: "Flex item stretch cache miss",
1578 cached_inline = ?layout.containing_block_size.inline,
1579 cached_block = ?layout.containing_block_size.block,
1580 required_cross_size = ?used_cross_size,
1581 cross_axis = ?cross_axis,
1582 depends_on_block_constraints = layout.depends_on_block_constraints,
1583 );
1584 *layout =
1585 item.item
1586 .layout(item.used_main_size, flex_context, Some(used_cross_size));
1587 }
1588
1589 let baseline = item.get_or_synthesize_baseline_with_cross_size(used_cross_size);
1590 if matches!(
1591 item.item.align_self.0.value(),
1592 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1593 ) {
1594 shared_alignment_baseline =
1595 Some(shared_alignment_baseline.unwrap_or(baseline).max(baseline));
1596 }
1597 item.layout_result.baseline_relative_to_margin_box = Some(baseline);
1598
1599 item_margins.push(item.item.resolve_auto_margins(
1600 flex_context,
1601 final_line_cross_size,
1602 used_cross_size,
1603 space_distributed_to_auto_main_margins,
1604 ));
1605 }
1606
1607 let resolved_justify_content: AlignFlags = {
1613 let justify_content_style = flex_context.config.justify_content.0.primary();
1614
1615 let mut resolved_justify_content = justify_content_style.value();
1617 let mut is_safe = justify_content_style.flags() == AlignFlags::SAFE;
1618
1619 if item_count <= 1 || free_space_in_main_axis <= Au::zero() {
1624 (resolved_justify_content, is_safe) = match resolved_justify_content {
1625 AlignFlags::STRETCH => (AlignFlags::FLEX_START, true),
1626 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
1627 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
1628 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
1629 _ => (resolved_justify_content, is_safe),
1630 }
1631 };
1632
1633 if free_space_in_main_axis <= Au::zero() && is_safe {
1635 resolved_justify_content = AlignFlags::START;
1636 }
1637
1638 resolved_justify_content
1639 };
1640
1641 let main_start_position = match resolved_justify_content {
1643 AlignFlags::START => Au::zero(),
1644 AlignFlags::FLEX_START => {
1645 if flex_context.config.flex_direction_is_reversed {
1646 free_space_in_main_axis
1647 } else {
1648 Au::zero()
1649 }
1650 },
1651 AlignFlags::END => free_space_in_main_axis,
1652 AlignFlags::FLEX_END => {
1653 if flex_context.config.flex_direction_is_reversed {
1654 Au::zero()
1655 } else {
1656 free_space_in_main_axis
1657 }
1658 },
1659 AlignFlags::CENTER => free_space_in_main_axis / 2,
1660 AlignFlags::STRETCH => Au::zero(),
1661 AlignFlags::SPACE_BETWEEN => Au::zero(),
1662 AlignFlags::SPACE_AROUND => (free_space_in_main_axis / item_count as i32) / 2,
1663 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1664
1665 _ => Au::zero(),
1667 };
1668
1669 let item_main_interval = match resolved_justify_content {
1670 AlignFlags::START => Au::zero(),
1671 AlignFlags::FLEX_START => Au::zero(),
1672 AlignFlags::END => Au::zero(),
1673 AlignFlags::FLEX_END => Au::zero(),
1674 AlignFlags::CENTER => Au::zero(),
1675 AlignFlags::STRETCH => Au::zero(),
1676 AlignFlags::SPACE_BETWEEN => free_space_in_main_axis / (item_count - 1) as i32,
1677 AlignFlags::SPACE_AROUND => free_space_in_main_axis / item_count as i32,
1678 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1679
1680 _ => Au::zero(),
1682 };
1683 let item_main_interval = item_main_interval + main_gap;
1684
1685 let mut all_baselines = Baselines::default();
1686 let mut main_position_cursor = main_start_position;
1687
1688 let items = std::mem::take(&mut self.items);
1689 let item_fragments = izip!(items, item_margins, item_used_cross_sizes.iter())
1690 .map(|(item, item_margin, item_used_cross_size)| {
1691 let item_used_size = FlexRelativeVec2 {
1692 main: item.used_main_size,
1693 cross: *item_used_cross_size,
1694 };
1695 item.collect_fragment(
1696 &self,
1697 item_used_size,
1698 item_margin,
1699 item_main_interval,
1700 final_line_cross_size,
1701 &shared_alignment_baseline,
1702 flex_context,
1703 &mut all_baselines,
1704 &mut main_position_cursor,
1705 )
1706 })
1707 .collect();
1708
1709 FinalFlexLineLayout {
1710 cross_size: final_line_cross_size,
1711 item_fragments,
1712 all_baselines,
1713 shared_alignment_baseline,
1714 }
1715 }
1716}
1717
1718impl FlexItem<'_> {
1719 #[servo_tracing::instrument(
1724 name = "FlexItem::layout",
1725 skip_all,
1726 fields(
1727 self_address = self as *const _ as usize,
1728 box_address = self.box_ as *const _ as usize,
1729 )
1730 )]
1731 #[allow(clippy::too_many_arguments)]
1732 fn layout(
1733 &self,
1734 used_main_size: Au,
1735 flex_context: &FlexContext,
1736 used_cross_size_override: Option<Au>,
1737 ) -> FlexItemLayoutResult {
1738 let containing_block = flex_context.containing_block;
1739 let independent_formatting_context = &self.box_.independent_formatting_context;
1740 let is_table = independent_formatting_context.is_table();
1741 let mut positioning_context = PositioningContext::default();
1742 let item_writing_mode = independent_formatting_context.style().writing_mode;
1743 let item_is_horizontal = item_writing_mode.is_horizontal();
1744 let flex_axis = flex_context.config.flex_axis;
1745 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
1746 containing_block.style.writing_mode.is_horizontal(),
1747 item_is_horizontal,
1748 flex_axis,
1749 );
1750
1751 let (inline_size, block_size) = if cross_axis_is_item_block_axis {
1752 let cross_size = match used_cross_size_override {
1753 Some(s) => SizeConstraint::Definite(s),
1754 None => {
1755 let stretch_size = containing_block
1756 .size
1757 .block
1758 .to_definite()
1759 .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
1760 let tentative_block_content_size = independent_formatting_context
1761 .tentative_block_content_size(self.preferred_aspect_ratio);
1762 if let Some(block_content_size) = tentative_block_content_size {
1763 SizeConstraint::Definite(self.content_cross_sizes.resolve(
1764 Direction::Block,
1765 Size::FitContent,
1766 Au::zero,
1767 stretch_size,
1768 || block_content_size,
1769 is_table,
1770 ))
1771 } else {
1772 self.content_cross_sizes.resolve_extrinsic(
1773 Size::FitContent,
1774 Au::zero(),
1775 stretch_size,
1776 )
1777 }
1778 },
1779 };
1780 (used_main_size, cross_size)
1781 } else {
1782 let cross_size = used_cross_size_override.unwrap_or_else(|| {
1783 let stretch_size =
1784 Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.cross);
1785 self.content_cross_sizes.resolve(
1786 Direction::Inline,
1787 Size::FitContent,
1788 Au::zero,
1789 Some(stretch_size),
1790 || self.inline_content_sizes(flex_context, used_main_size),
1791 is_table,
1792 )
1793 });
1794 let main_size = if self.flex_base_size_is_definite ||
1798 flex_context
1799 .container_inner_size_constraint
1800 .main
1801 .is_definite()
1802 {
1803 SizeConstraint::Definite(used_main_size)
1804 } else {
1805 SizeConstraint::default()
1806 };
1807 (cross_size, main_size)
1808 };
1809
1810 let item_style = independent_formatting_context.style();
1811 let item_as_containing_block = ContainingBlock {
1812 size: ContainingBlockSize {
1813 inline: inline_size,
1814 block: block_size,
1815 },
1816 style: item_style,
1817 };
1818
1819 let lazy_block_size = if !cross_axis_is_item_block_axis {
1820 used_main_size.into()
1821 } else if let Some(cross_size) = used_cross_size_override {
1822 cross_size.into()
1823 } else {
1824 let stretch_size = containing_block
1825 .size
1826 .block
1827 .to_definite()
1828 .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
1829 LazySize::new(
1830 &self.content_cross_sizes,
1831 Direction::Block,
1832 Size::FitContent,
1833 Au::zero,
1834 stretch_size,
1835 is_table,
1836 )
1837 };
1838
1839 let layout = independent_formatting_context.layout(
1840 flex_context.layout_context,
1841 &mut positioning_context,
1842 &item_as_containing_block,
1843 containing_block,
1844 self.preferred_aspect_ratio,
1845 &lazy_block_size,
1846 );
1847 let CacheableLayoutResult {
1848 fragments,
1849 content_block_size,
1850 baselines: content_box_baselines,
1851 depends_on_block_constraints,
1852 specific_layout_info,
1853 ..
1854 } = layout;
1855
1856 let hypothetical_cross_size = if cross_axis_is_item_block_axis {
1857 lazy_block_size.resolve(|| content_block_size)
1858 } else {
1859 inline_size
1860 };
1861
1862 let item_writing_mode_is_orthogonal_to_container_writing_mode =
1863 flex_context.config.writing_mode.is_horizontal() !=
1864 item_style.writing_mode.is_horizontal();
1865 let has_compatible_baseline = match flex_axis {
1866 FlexAxis::Row => !item_writing_mode_is_orthogonal_to_container_writing_mode,
1867 FlexAxis::Column => item_writing_mode_is_orthogonal_to_container_writing_mode,
1868 };
1869
1870 let baselines_relative_to_margin_box = if has_compatible_baseline {
1871 content_box_baselines.offset(
1872 self.margin.cross_start.auto_is(Au::zero) +
1873 self.padding.cross_start +
1874 self.border.cross_start,
1875 )
1876 } else {
1877 Baselines::default()
1878 };
1879
1880 let baseline_relative_to_margin_box = match self.align_self.0.value() {
1881 AlignFlags::BASELINE => baselines_relative_to_margin_box.first,
1883 AlignFlags::LAST_BASELINE => baselines_relative_to_margin_box.last,
1884 _ => None,
1885 };
1886
1887 FlexItemLayoutResult {
1888 hypothetical_cross_size,
1889 fragments,
1890 positioning_context,
1891 baseline_relative_to_margin_box,
1892 content_block_size,
1893 containing_block_size: item_as_containing_block.size,
1894 depends_on_block_constraints,
1895 specific_layout_info,
1896 }
1897 }
1898
1899 fn synthesized_baseline_relative_to_margin_box(&self, content_size: Au) -> Au {
1900 content_size +
1904 self.margin.cross_start.auto_is(Au::zero) +
1905 self.padding.cross_start +
1906 self.border.cross_start +
1907 self.border.cross_end +
1908 self.padding.cross_end
1909 }
1910
1911 fn resolve_auto_margins(
1917 &self,
1918 flex_context: &FlexContext,
1919 line_cross_size: Au,
1920 item_cross_content_size: Au,
1921 space_distributed_to_auto_main_margins: Au,
1922 ) -> FlexRelativeSides<Au> {
1923 let main_start = self
1924 .margin
1925 .main_start
1926 .auto_is(|| space_distributed_to_auto_main_margins);
1927 let main_end = self
1928 .margin
1929 .main_end
1930 .auto_is(|| space_distributed_to_auto_main_margins);
1931
1932 let auto_count = match (self.margin.cross_start, self.margin.cross_end) {
1933 (AuOrAuto::LengthPercentage(cross_start), AuOrAuto::LengthPercentage(cross_end)) => {
1934 return FlexRelativeSides {
1935 cross_start,
1936 cross_end,
1937 main_start,
1938 main_end,
1939 };
1940 },
1941 (AuOrAuto::Auto, AuOrAuto::Auto) => 2,
1942 _ => 1,
1943 };
1944 let outer_cross_size = self.pbm_auto_is_zero.cross + item_cross_content_size;
1945 let available = line_cross_size - outer_cross_size;
1946 let cross_start;
1947 let cross_end;
1948 if available > Au::zero() {
1949 let each_auto_margin = available / auto_count;
1950 cross_start = self.margin.cross_start.auto_is(|| each_auto_margin);
1951 cross_end = self.margin.cross_end.auto_is(|| each_auto_margin);
1952 } else {
1953 let flex_wrap = flex_context.containing_block.style.get_position().flex_wrap;
1966 let flex_wrap_reverse = match flex_wrap {
1967 FlexWrap::Nowrap | FlexWrap::Wrap => false,
1968 FlexWrap::WrapReverse => true,
1969 };
1970 if flex_wrap_reverse {
1974 cross_start = self.margin.cross_start.auto_is(|| available);
1975 cross_end = self.margin.cross_end.auto_is(Au::zero);
1976 } else {
1977 cross_start = self.margin.cross_start.auto_is(Au::zero);
1978 cross_end = self.margin.cross_end.auto_is(|| available);
1979 }
1980 }
1981
1982 FlexRelativeSides {
1983 cross_start,
1984 cross_end,
1985 main_start,
1986 main_end,
1987 }
1988 }
1989
1990 fn align_along_cross_axis(
1992 &self,
1993 margin: &FlexRelativeSides<Au>,
1994 used_cross_size: &Au,
1995 line_cross_size: Au,
1996 propagated_baseline: Au,
1997 max_propagated_baseline: Au,
1998 wrap_reverse: bool,
1999 ) -> Au {
2000 let ending_alignment = line_cross_size - *used_cross_size - self.pbm_auto_is_zero.cross;
2001 let outer_cross_start =
2002 if self.margin.cross_start.is_auto() || self.margin.cross_end.is_auto() {
2003 Au::zero()
2004 } else {
2005 match self.align_self.0.value() {
2006 AlignFlags::STRETCH => Au::zero(),
2007 AlignFlags::CENTER => ending_alignment / 2,
2008 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE => {
2009 max_propagated_baseline - propagated_baseline
2010 },
2011 AlignFlags::START => {
2012 if !wrap_reverse {
2013 Au::zero()
2014 } else {
2015 ending_alignment
2016 }
2017 },
2018 AlignFlags::END => {
2019 if !wrap_reverse {
2020 ending_alignment
2021 } else {
2022 Au::zero()
2023 }
2024 },
2025 _ => Au::zero(),
2026 }
2027 };
2028 outer_cross_start + margin.cross_start + self.border.cross_start + self.padding.cross_start
2029 }
2030
2031 #[inline]
2032 fn inline_content_sizes(&self, flex_context: &FlexContext, block_size: Au) -> ContentSizes {
2033 self.box_.inline_content_sizes(
2034 flex_context,
2035 SizeConstraint::Definite(block_size),
2036 self.preferred_aspect_ratio,
2037 )
2038 }
2039}
2040
2041impl FlexItemBox {
2042 fn to_flex_item<'a>(
2043 &self,
2044 layout_context: &LayoutContext,
2045 containing_block: &IndefiniteContainingBlock,
2046 content_box_sizes_and_pbm: &ContentBoxSizesAndPBM,
2047 config: &FlexContainerConfig,
2048 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2049 ) -> FlexItem<'_> {
2050 let flex_axis = config.flex_axis;
2051 let style = self.style();
2052 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
2053 containing_block.writing_mode.is_horizontal(),
2054 style.writing_mode.is_horizontal(),
2055 flex_axis,
2056 );
2057 let main_axis = if cross_axis_is_item_block_axis {
2058 Direction::Inline
2059 } else {
2060 Direction::Block
2061 };
2062 let align_self = AlignItems(config.resolve_align_self_for_child(style));
2063
2064 let ContentBoxSizesAndPBM {
2065 content_box_sizes,
2066 pbm,
2067 depends_on_block_constraints,
2068 preferred_size_computes_to_auto,
2069 } = content_box_sizes_and_pbm;
2070
2071 let preferred_aspect_ratio = self
2072 .independent_formatting_context
2073 .preferred_aspect_ratio(&pbm.padding_border_sums);
2074 let padding = config.sides_to_flex_relative(pbm.padding);
2075 let border = config.sides_to_flex_relative(pbm.border);
2076 let margin = config.sides_to_flex_relative(pbm.margin);
2077 let padding_border = padding.sum_by_axis() + border.sum_by_axis();
2078 let margin_auto_is_zero = config.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
2079 let pbm_auto_is_zero = FlexRelativeVec2 {
2080 main: padding_border.main,
2081 cross: padding_border.cross,
2082 } + margin_auto_is_zero.sum_by_axis();
2083 let (content_main_sizes, content_cross_sizes, cross_size_computes_to_auto) = match flex_axis
2084 {
2085 FlexAxis::Row => (
2086 &content_box_sizes.inline,
2087 &content_box_sizes.block,
2088 preferred_size_computes_to_auto.block,
2089 ),
2090 FlexAxis::Column => (
2091 &content_box_sizes.block,
2092 &content_box_sizes.inline,
2093 preferred_size_computes_to_auto.inline,
2094 ),
2095 };
2096 let automatic_cross_size = if cross_size_computes_to_auto &&
2097 item_with_auto_cross_size_stretches_to_line_size(align_self, &margin)
2098 {
2099 Size::Stretch
2100 } else {
2101 Size::FitContent
2102 };
2103 let automatic_cross_size_for_intrinsic_sizing = if config.container_is_single_line {
2104 automatic_cross_size
2105 } else {
2106 Size::FitContent
2107 };
2108 let containing_block_size = flex_axis.vec2_to_flex_relative(containing_block.size);
2109 let stretch_size = FlexRelativeVec2 {
2110 main: containing_block_size
2111 .main
2112 .map(|v| Au::zero().max(v - pbm_auto_is_zero.main)),
2113 cross: containing_block_size
2114 .cross
2115 .map(|v| Au::zero().max(v - pbm_auto_is_zero.cross)),
2116 };
2117
2118 let is_table = self.independent_formatting_context.is_table();
2119 let tentative_cross_content_size = if cross_axis_is_item_block_axis {
2120 self.independent_formatting_context
2121 .tentative_block_content_size(preferred_aspect_ratio)
2122 } else {
2123 None
2124 };
2125 let (preferred_cross_size, min_cross_size, max_cross_size) =
2126 if let Some(cross_content_size) = tentative_cross_content_size {
2127 let (preferred, min, max) = content_cross_sizes.resolve_each(
2128 automatic_cross_size_for_intrinsic_sizing,
2129 Au::zero,
2130 stretch_size.cross,
2131 || cross_content_size,
2132 is_table,
2133 );
2134 (Some(preferred), min, max)
2135 } else {
2136 content_cross_sizes.resolve_each_extrinsic(
2137 automatic_cross_size_for_intrinsic_sizing,
2138 Au::zero(),
2139 stretch_size.cross,
2140 )
2141 };
2142 let cross_size = SizeConstraint::new(preferred_cross_size, min_cross_size, max_cross_size);
2143
2144 let transferred_size_suggestion =
2149 LazyCell::new(|| match (preferred_aspect_ratio, cross_size) {
2150 (Some(ratio), SizeConstraint::Definite(cross_size)) => {
2151 Some(ratio.compute_dependent_size(main_axis, cross_size))
2152 },
2153 _ => None,
2154 });
2155
2156 let flex_base_size_is_definite = Cell::new(true);
2158 let main_content_sizes = LazyCell::new(|| {
2159 let flex_item = &self.independent_formatting_context;
2160 if let Some(transferred_size_suggestion) = *transferred_size_suggestion {
2166 return transferred_size_suggestion.into();
2167 }
2168
2169 flex_base_size_is_definite.set(false);
2170
2171 if cross_axis_is_item_block_axis {
2180 let constraint_space =
2183 ConstraintSpace::new(cross_size, style.writing_mode, preferred_aspect_ratio);
2184 let content_sizes = flex_item
2185 .inline_content_sizes(layout_context, &constraint_space)
2186 .sizes;
2187 if let Some(ratio) = preferred_aspect_ratio {
2188 let transferred_min = ratio.compute_dependent_size(main_axis, min_cross_size);
2189 let transferred_max =
2190 max_cross_size.map(|v| ratio.compute_dependent_size(main_axis, v));
2191 content_sizes
2192 .map(|size| size.clamp_between_extremums(transferred_min, transferred_max))
2193 } else {
2194 content_sizes
2195 }
2196 } else {
2197 self.layout_for_block_content_size(
2198 flex_context_getter(),
2199 &pbm_auto_is_zero,
2200 content_box_sizes,
2201 preferred_aspect_ratio,
2202 automatic_cross_size_for_intrinsic_sizing,
2203 IntrinsicSizingMode::Size,
2204 )
2205 .into()
2206 }
2207 });
2208
2209 let flex_base_size = self
2210 .flex_basis(
2211 containing_block_size.main,
2212 content_main_sizes.preferred,
2213 padding_border.main,
2214 )
2215 .resolve_for_preferred(Size::MaxContent, stretch_size.main, &main_content_sizes);
2216 let flex_base_size_is_definite = flex_base_size_is_definite.take();
2217
2218 let content_max_main_size = content_main_sizes
2219 .max
2220 .resolve_for_max(stretch_size.main, &main_content_sizes);
2221
2222 let get_automatic_minimum_size = || {
2223 if style.establishes_scroll_container(self.base_fragment_info().flags) {
2225 return Au::zero();
2226 }
2227
2228 let specified_size_suggestion = content_main_sizes
2232 .preferred
2233 .maybe_resolve_extrinsic(stretch_size.main);
2234
2235 let is_replaced = self.independent_formatting_context.is_replaced();
2236
2237 let content_size_suggestion = match preferred_aspect_ratio {
2242 Some(ratio) => main_content_sizes.min_content.clamp_between_extremums(
2243 ratio.compute_dependent_size(main_axis, min_cross_size),
2244 max_cross_size.map(|l| ratio.compute_dependent_size(main_axis, l)),
2245 ),
2246 None => main_content_sizes.min_content,
2247 };
2248
2249 match (specified_size_suggestion, *transferred_size_suggestion) {
2256 (Some(specified), _) => specified.min(content_size_suggestion),
2257 (_, Some(transferred)) if is_replaced => transferred.min(content_size_suggestion),
2258 _ => content_size_suggestion,
2259 }
2260 .clamp_below_max(content_max_main_size)
2261 };
2262 let content_min_main_size = content_main_sizes.min.resolve_for_min(
2263 get_automatic_minimum_size,
2264 stretch_size.main,
2265 &main_content_sizes,
2266 is_table,
2267 );
2268
2269 FlexItem {
2270 box_: self,
2271 content_cross_sizes: content_cross_sizes.clone(),
2272 padding,
2273 border,
2274 margin: config.sides_to_flex_relative(pbm.margin),
2275 pbm_auto_is_zero,
2276 flex_base_size,
2277 flex_base_size_is_definite,
2278 hypothetical_main_size: flex_base_size
2279 .clamp_between_extremums(content_min_main_size, content_max_main_size),
2280 content_min_main_size,
2281 content_max_main_size,
2282 align_self,
2283 depends_on_block_constraints: *depends_on_block_constraints,
2284 preferred_aspect_ratio,
2285 automatic_cross_size,
2286 automatic_cross_size_for_intrinsic_sizing,
2287 }
2288 }
2289
2290 fn main_content_size_info<'a>(
2291 &self,
2292 layout_context: &LayoutContext,
2293 containing_block: &IndefiniteContainingBlock,
2294 config: &FlexContainerConfig,
2295 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2296 ) -> FlexItemBoxInlineContentSizesInfo {
2297 let content_box_sizes_and_pbm = self
2298 .independent_formatting_context
2299 .layout_style()
2300 .content_box_sizes_and_padding_border_margin(containing_block);
2301
2302 let FlexItem {
2305 flex_base_size,
2306 content_min_main_size,
2307 content_max_main_size,
2308 pbm_auto_is_zero,
2309 preferred_aspect_ratio,
2310 automatic_cross_size_for_intrinsic_sizing,
2311 ..
2312 } = self.to_flex_item(
2313 layout_context,
2314 containing_block,
2315 &content_box_sizes_and_pbm,
2316 config,
2317 flex_context_getter,
2318 );
2319
2320 let (content_contribution_sizes, depends_on_block_constraints) = match config.flex_axis {
2323 FlexAxis::Row => {
2324 let auto_minimum = LogicalVec2 {
2325 inline: content_min_main_size,
2326 block: Au::zero(),
2327 };
2328 let InlineContentSizesResult {
2329 sizes,
2330 depends_on_block_constraints,
2331 } = self
2332 .independent_formatting_context
2333 .outer_inline_content_sizes(
2334 layout_context,
2335 containing_block,
2336 &auto_minimum,
2337 automatic_cross_size_for_intrinsic_sizing == Size::Stretch,
2338 );
2339 (sizes, depends_on_block_constraints)
2340 },
2341 FlexAxis::Column => {
2342 let size = self.layout_for_block_content_size(
2343 flex_context_getter(),
2344 &pbm_auto_is_zero,
2345 &content_box_sizes_and_pbm.content_box_sizes,
2346 preferred_aspect_ratio,
2347 automatic_cross_size_for_intrinsic_sizing,
2348 IntrinsicSizingMode::Contribution,
2349 );
2350 (size.into(), true)
2351 },
2352 };
2353
2354 let outer_flex_base_size = flex_base_size + pbm_auto_is_zero.main;
2355 let outer_min_main_size = content_min_main_size + pbm_auto_is_zero.main;
2356 let outer_max_main_size = content_max_main_size.map(|v| v + pbm_auto_is_zero.main);
2357 let max_flex_factors = self.desired_flex_factors_for_preferred_width(
2358 content_contribution_sizes.max_content,
2359 flex_base_size,
2360 outer_flex_base_size,
2361 );
2362
2363 let min_flex_factors = self.desired_flex_factors_for_preferred_width(
2367 content_contribution_sizes.min_content,
2368 flex_base_size,
2369 outer_flex_base_size,
2370 );
2371
2372 let mut min_content_main_size_for_multiline_container =
2379 content_contribution_sizes.min_content;
2380 let style_position = &self.style().get_position();
2381 if style_position.flex_grow.is_zero() {
2382 min_content_main_size_for_multiline_container.min_assign(outer_flex_base_size);
2383 }
2384 if style_position.flex_shrink.is_zero() {
2385 min_content_main_size_for_multiline_container.max_assign(outer_flex_base_size);
2386 }
2387 min_content_main_size_for_multiline_container =
2388 min_content_main_size_for_multiline_container
2389 .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
2390
2391 FlexItemBoxInlineContentSizesInfo {
2392 outer_flex_base_size,
2393 outer_min_main_size,
2394 outer_max_main_size,
2395 min_flex_factors,
2396 max_flex_factors,
2397 min_content_main_size_for_multiline_container,
2398 depends_on_block_constraints,
2399 }
2400 }
2401
2402 fn desired_flex_factors_for_preferred_width(
2403 &self,
2404 preferred_width: Au,
2405 flex_base_size: Au,
2406 outer_flex_base_size: Au,
2407 ) -> DesiredFlexFractionAndGrowOrShrinkFactor {
2408 let difference = (preferred_width - outer_flex_base_size).to_f32_px();
2409 let (flex_grow_or_scaled_flex_shrink_factor, desired_flex_fraction) = if difference > 0.0 {
2410 let flex_grow_factor = self.style().get_position().flex_grow.0;
2414
2415 (
2416 flex_grow_factor,
2417 if flex_grow_factor >= 1.0 {
2418 difference / flex_grow_factor
2419 } else {
2420 difference * flex_grow_factor
2421 },
2422 )
2423 } else if difference < 0.0 {
2424 let flex_shrink_factor = self.style().get_position().flex_shrink.0;
2428 let scaled_flex_shrink_factor = flex_shrink_factor * flex_base_size.to_f32_px();
2429
2430 (
2431 scaled_flex_shrink_factor,
2432 if scaled_flex_shrink_factor != 0.0 {
2433 difference / scaled_flex_shrink_factor
2434 } else {
2435 f32::NEG_INFINITY
2436 },
2437 )
2438 } else {
2439 (0.0, 0.0)
2440 };
2441
2442 DesiredFlexFractionAndGrowOrShrinkFactor {
2443 desired_flex_fraction,
2444 flex_grow_or_shrink_factor: flex_grow_or_scaled_flex_shrink_factor,
2445 }
2446 }
2447
2448 fn flex_basis(
2454 &self,
2455 container_definite_main_size: Option<Au>,
2456 main_preferred_size: Size<Au>,
2457 main_padding_border_sum: Au,
2458 ) -> Size<Au> {
2459 let style_position = &self.independent_formatting_context.style().get_position();
2460 match &style_position.flex_basis {
2461 FlexBasis::Content => Size::Initial,
2464
2465 FlexBasis::Size(size) => match Size::<LengthPercentage>::from(size.clone()) {
2466 Size::Initial => main_preferred_size,
2470
2471 size => {
2481 let apply_box_sizing = |length: Au| {
2482 match style_position.box_sizing {
2483 BoxSizing::ContentBox => length,
2484 BoxSizing::BorderBox => length - main_padding_border_sum,
2487 }
2488 };
2489 size.resolve_percentages_for_preferred(container_definite_main_size)
2490 .map(apply_box_sizing)
2491 },
2492 },
2493 }
2494 }
2495
2496 #[allow(clippy::too_many_arguments)]
2497 #[servo_tracing::instrument(name = "FlexContainer::layout_for_block_content_size", skip_all)]
2498 fn layout_for_block_content_size(
2499 &self,
2500 flex_context: &FlexContext,
2501 pbm_auto_is_zero: &FlexRelativeVec2<Au>,
2502 content_box_sizes: &LogicalVec2<Sizes>,
2503 preferred_aspect_ratio: Option<AspectRatio>,
2504 automatic_inline_size: Size<Au>,
2505 intrinsic_sizing_mode: IntrinsicSizingMode,
2506 ) -> Au {
2507 let content_block_size = || {
2508 let mut positioning_context = PositioningContext::default();
2509 let style = self.independent_formatting_context.style();
2510
2511 let tentative_block_size = SizeConstraint::default();
2515
2516 let inline_size = {
2519 let stretch_size =
2520 flex_context.containing_block.size.inline - pbm_auto_is_zero.cross;
2521 let get_content_size = || {
2522 self.inline_content_sizes(
2523 flex_context,
2524 tentative_block_size,
2525 preferred_aspect_ratio,
2526 )
2527 };
2528 content_box_sizes.inline.resolve(
2529 Direction::Inline,
2530 automatic_inline_size,
2531 Au::zero,
2532 Some(stretch_size),
2533 get_content_size,
2534 false,
2535 )
2536 };
2537 let item_as_containing_block = ContainingBlock {
2538 size: ContainingBlockSize {
2539 inline: inline_size,
2540 block: tentative_block_size,
2541 },
2542 style,
2543 };
2544 self.independent_formatting_context
2545 .layout(
2546 flex_context.layout_context,
2547 &mut positioning_context,
2548 &item_as_containing_block,
2549 flex_context.containing_block,
2550 preferred_aspect_ratio,
2551 &LazySize::intrinsic(),
2552 )
2553 .content_block_size
2554 };
2555 match intrinsic_sizing_mode {
2556 IntrinsicSizingMode::Contribution => {
2557 let stretch_size = flex_context
2558 .containing_block
2559 .size
2560 .block
2561 .to_definite()
2562 .map(|block_size| block_size - pbm_auto_is_zero.main);
2563 let inner_block_size = content_box_sizes.block.resolve(
2564 Direction::Block,
2565 Size::FitContent,
2566 Au::zero,
2567 stretch_size,
2568 || ContentSizes::from(content_block_size()),
2569 false, );
2577 inner_block_size + pbm_auto_is_zero.main
2578 },
2579 IntrinsicSizingMode::Size => content_block_size(),
2580 }
2581 }
2582
2583 fn inline_content_sizes(
2584 &self,
2585 flex_context: &FlexContext,
2586 block_size: SizeConstraint,
2587 preferred_aspect_ratio: Option<AspectRatio>,
2588 ) -> ContentSizes {
2589 let writing_mode = self.independent_formatting_context.style().writing_mode;
2590 let constraint_space =
2591 ConstraintSpace::new(block_size, writing_mode, preferred_aspect_ratio);
2592 self.independent_formatting_context
2593 .inline_content_sizes(flex_context.layout_context, &constraint_space)
2594 .sizes
2595 }
2596}