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