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