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