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, IntoParallelIterator, ParallelDrainRange, ParallelIterator,
14};
15use style::Zero;
16use style::computed_values::position::T as Position;
17use style::logical_geometry::{Direction, WritingMode};
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(
157 &self,
158 cross_size: Au,
159 flex_container_config: &FlexContainerConfig,
160 ) -> Au {
161 self.layout_result
162 .flex_alignment_baseline_relative_to_margin_box
163 .unwrap_or_else(|| {
164 self.item
165 .synthesized_baseline_relative_to_margin_box(cross_size, flex_container_config)
166 })
167 }
168
169 #[expect(clippy::too_many_arguments)]
170 fn collect_fragment(
171 mut self,
172 initial_flex_layout: &InitialFlexLineLayout,
173 item_used_size: FlexRelativeVec2<Au>,
174 item_margin: FlexRelativeSides<Au>,
175 item_main_interval: Au,
176 final_line_cross_size: Au,
177 shared_alignment_baseline: &Option<Au>,
178 flex_context: &mut FlexContext,
179 all_baselines: &mut Baselines,
180 main_position_cursor: &mut Au,
181 ) -> (Arc<BoxFragment>, PositioningContext) {
182 *main_position_cursor +=
185 item_margin.main_start + self.item.border.main_start + self.item.padding.main_start;
186 let item_content_main_start_position = *main_position_cursor;
187
188 *main_position_cursor += item_used_size.main +
189 self.item.padding.main_end +
190 self.item.border.main_end +
191 item_margin.main_end +
192 item_main_interval;
193
194 let item_content_cross_start_position = self.item.align_along_cross_axis(
196 &item_margin,
197 &item_used_size.cross,
198 final_line_cross_size,
199 self.layout_result
200 .flex_alignment_baseline_relative_to_margin_box
201 .unwrap_or_default(),
202 shared_alignment_baseline.unwrap_or_default(),
203 flex_context.config.flex_wrap_is_reversed,
204 );
205
206 let start_corner = FlexRelativeVec2 {
207 main: item_content_main_start_position,
208 cross: item_content_cross_start_position,
209 };
210
211 let final_line_size = FlexRelativeVec2 {
213 main: initial_flex_layout.line_size.main,
214 cross: final_line_cross_size,
215 };
216 let content_rect = flex_context.rect_to_flow_relative(
217 final_line_size,
218 FlexRelativeRect {
219 start_corner,
220 size: item_used_size,
221 },
222 );
223
224 let adjust_baseline = |baseline: Au| {
225 baseline + item_content_cross_start_position -
226 self.item.border.cross_start -
227 self.item.padding.cross_start -
228 item_margin.cross_start
229 };
230
231 let baselines = self
232 .layout_result
233 .content_baselines_for_parent_relative_to_margin_box;
234 if flex_context.config.flex_direction_is_reversed {
235 if let Some(last_baseline) = baselines.last {
236 all_baselines
237 .last
238 .get_or_insert_with(|| adjust_baseline(last_baseline));
239 }
240 if let Some(first_baseline) = baselines.first {
241 all_baselines.first = Some(adjust_baseline(first_baseline));
242 }
243 } else {
244 if let Some(first_baseline) = baselines.first {
245 all_baselines
246 .first
247 .get_or_insert_with(|| adjust_baseline(first_baseline));
248 }
249 if let Some(last_baseline) = baselines.last {
250 all_baselines.last = Some(adjust_baseline(last_baseline));
251 }
252 }
253
254 let mut fragment_info = self.item.box_.base_fragment_info();
255 fragment_info
256 .flags
257 .insert(FragmentFlags::IS_FLEX_OR_GRID_ITEM);
258 if self.item.depends_on_block_constraints {
259 fragment_info.flags.insert(
260 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
261 );
262 }
263 let flags = fragment_info.flags;
264
265 let containing_block = flex_context.containing_block;
266 let container_writing_mode = containing_block.style.writing_mode;
267 let style = self.item.box_.style();
268
269 let mut fragment = BoxFragment::new(
270 fragment_info,
271 style.clone(),
272 self.layout_result.fragments,
273 content_rect.as_physical(Some(flex_context.containing_block)),
274 flex_context
275 .sides_to_flow_relative(self.item.padding)
276 .to_physical(container_writing_mode),
277 flex_context
278 .sides_to_flow_relative(self.item.border)
279 .to_physical(container_writing_mode),
280 flex_context
281 .sides_to_flow_relative(item_margin)
282 .to_physical(container_writing_mode),
283 self.layout_result.specific_layout_info,
284 );
285
286 if style.establishes_containing_block_for_absolute_descendants(flags) {
290 self.layout_result
291 .positioning_context
292 .layout_collected_children(flex_context.layout_context, &mut fragment);
293 }
294
295 if style.get_box().position == Position::Relative {
296 fragment.base.translate_rect(
297 relative_adjustement(style, containing_block)
298 .to_physical_size(containing_block.style.writing_mode),
299 );
300 }
301
302 let fragment = Arc::new(fragment);
303 self.item
304 .box_
305 .independent_formatting_context
306 .base
307 .set_fragment(Fragment::Box(fragment.clone()));
308 (fragment, self.layout_result.positioning_context)
309 }
310}
311
312struct FinalFlexLineLayout {
315 cross_size: Au,
317 item_fragments: Vec<(Arc<BoxFragment>, PositioningContext)>,
320 shared_alignment_baseline: Option<Au>,
323 all_baselines: Baselines,
328}
329
330impl FlexContainerConfig {
331 fn resolve_reversable_flex_alignment(
332 &self,
333 align_flags: AlignFlags,
334 reversed: bool,
335 ) -> AlignFlags {
336 match (align_flags.value(), reversed) {
337 (AlignFlags::FLEX_START, false) => AlignFlags::START | align_flags.flags(),
338 (AlignFlags::FLEX_START, true) => AlignFlags::END | align_flags.flags(),
339 (AlignFlags::FLEX_END, false) => AlignFlags::END | align_flags.flags(),
340 (AlignFlags::FLEX_END, true) => AlignFlags::START | align_flags.flags(),
341 (_, _) => align_flags,
342 }
343 }
344
345 fn resolve_align_self_for_child(&self, child_style: &ComputedValues) -> AlignFlags {
346 self.resolve_reversable_flex_alignment(
347 child_style
348 .resolve_align_self(self.align_items, AlignFlags::STRETCH)
349 .0,
350 self.flex_wrap_is_reversed,
351 )
352 }
353
354 fn resolve_justify_content_for_child(&self) -> AlignFlags {
355 self.resolve_reversable_flex_alignment(
356 self.justify_content.primary(),
357 self.flex_direction_is_reversed,
358 )
359 }
360
361 fn sides_to_flex_relative<T>(&self, sides: LogicalSides<T>) -> FlexRelativeSides<T> {
362 self.main_start_cross_start_sides_are
363 .sides_to_flex_relative(sides)
364 }
365
366 fn sides_to_flow_relative<T>(&self, sides: FlexRelativeSides<T>) -> LogicalSides<T> {
367 self.main_start_cross_start_sides_are
368 .sides_to_flow_relative(sides)
369 }
370}
371
372impl FlexContext<'_> {
373 #[inline]
374 fn sides_to_flow_relative<T>(&self, x: FlexRelativeSides<T>) -> LogicalSides<T> {
375 self.config.sides_to_flow_relative(x)
376 }
377
378 #[inline]
379 fn rect_to_flow_relative(
380 &self,
381 base_rect_size: FlexRelativeVec2<Au>,
382 rect: FlexRelativeRect<Au>,
383 ) -> LogicalRect<Au> {
384 super::geom::rect_to_flow_relative(
385 self.config.flex_axis,
386 self.config.main_start_cross_start_sides_are,
387 base_rect_size,
388 rect,
389 )
390 }
391}
392
393#[derive(Debug, Default)]
394struct DesiredFlexFractionAndGrowOrShrinkFactor {
395 desired_flex_fraction: f32,
396 flex_grow_or_shrink_factor: f32,
397}
398
399#[derive(Default)]
400struct FlexItemBoxInlineContentSizesInfo {
401 outer_flex_base_size: Au,
402 outer_min_main_size: Au,
403 outer_max_main_size: Option<Au>,
404 min_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
405 max_flex_factors: DesiredFlexFractionAndGrowOrShrinkFactor,
406 min_content_main_size_for_multiline_container: Au,
407 depends_on_block_constraints: bool,
408}
409
410impl ComputeInlineContentSizes for FlexContainer {
411 #[servo_tracing::instrument(name = "FlexContainer::compute_inline_content_sizes", skip_all)]
412 fn compute_inline_content_sizes(
413 &self,
414 layout_context: &LayoutContext,
415 constraint_space: &ConstraintSpace,
416 ) -> InlineContentSizesResult {
417 match self.config.flex_axis {
418 FlexAxis::Row => {
419 self.main_content_sizes(layout_context, &constraint_space.into(), || {
420 unreachable!(
421 "Unexpected FlexContext query during row flex intrinsic size calculation."
422 )
423 })
424 },
425 FlexAxis::Column => self.cross_content_sizes(layout_context, &constraint_space.into()),
426 }
427 }
428}
429
430impl FlexContainer {
431 fn cross_content_sizes(
432 &self,
433 layout_context: &LayoutContext,
434 containing_block_for_children: &IndefiniteContainingBlock,
435 ) -> InlineContentSizesResult {
436 assert_eq!(
438 self.config.flex_axis,
439 FlexAxis::Column,
440 "The cross axis should be the inline one"
441 );
442 let mut sizes = ContentSizes::zero();
443 let mut depends_on_block_constraints = false;
444 for kid in self.children.iter() {
445 let kid = &*kid.borrow();
446 match kid {
447 FlexLevelBox::FlexItem(item) => {
448 let ifc = &item.independent_formatting_context;
452 let result = ifc.outer_inline_content_sizes(
453 layout_context,
454 containing_block_for_children,
455 &LogicalVec2::zero(),
456 false, );
458 sizes.max_assign(result.sizes);
459 depends_on_block_constraints |= result.depends_on_block_constraints;
460 },
461 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
462 }
463 }
464 InlineContentSizesResult {
465 sizes,
466 depends_on_block_constraints,
467 }
468 }
469
470 fn main_content_sizes<'a>(
471 &self,
472 layout_context: &LayoutContext,
473 containing_block_for_children: &IndefiniteContainingBlock,
474 flex_context_getter: impl Fn() -> &'a FlexContext<'a>,
475 ) -> InlineContentSizesResult {
476 let mut chosen_max_flex_fraction = f32::NEG_INFINITY;
485 let mut chosen_min_flex_fraction = f32::NEG_INFINITY;
486 let mut sum_of_flex_grow_factors = 0.0;
487 let mut sum_of_flex_shrink_factors = 0.0;
488 let mut item_infos = vec![];
489
490 for kid in self.children.iter() {
491 let kid = &*kid.borrow();
492 match kid {
493 FlexLevelBox::FlexItem(item) => {
494 sum_of_flex_grow_factors += item.style().get_position().flex_grow.0;
495 sum_of_flex_shrink_factors += item.style().get_position().flex_shrink.0;
496
497 let info = item.main_content_size_info(
498 layout_context,
499 containing_block_for_children,
500 &self.config,
501 &flex_context_getter,
502 );
503
504 chosen_max_flex_fraction =
509 chosen_max_flex_fraction.max(info.max_flex_factors.desired_flex_fraction);
510 chosen_min_flex_fraction =
511 chosen_min_flex_fraction.max(info.min_flex_factors.desired_flex_fraction);
512
513 item_infos.push(info)
514 },
515 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
516 }
517 }
518
519 let normalize_flex_fraction = |chosen_flex_fraction| {
520 if chosen_flex_fraction > 0.0 && sum_of_flex_grow_factors < 1.0 {
521 chosen_flex_fraction / sum_of_flex_grow_factors
525 } else if chosen_flex_fraction < 0.0 && sum_of_flex_shrink_factors < 1.0 {
526 chosen_flex_fraction * sum_of_flex_shrink_factors
530 } else {
531 chosen_flex_fraction
532 }
533 };
534
535 let chosen_min_flex_fraction = normalize_flex_fraction(chosen_min_flex_fraction);
536 let chosen_max_flex_fraction = normalize_flex_fraction(chosen_max_flex_fraction);
537
538 let main_gap = match self.config.flex_axis {
539 FlexAxis::Row => self.style.get_column_gap(),
540 FlexAxis::Column => self.style.get_row_gap(),
541 };
542 let main_gap = match main_gap {
543 LengthPercentageOrNormal::LengthPercentage(length_percentage) => {
544 length_percentage.to_used_value(Au::zero())
545 },
546 LengthPercentageOrNormal::Normal => Au::zero(),
547 };
548 let extra_space_from_main_gap = main_gap * (item_infos.len() as i32 - 1);
549 let mut container_max_content_size = extra_space_from_main_gap;
550 let mut container_min_content_size = if self.config.flex_wrap == FlexWrap::NOWRAP {
551 extra_space_from_main_gap
552 } else {
553 Au::zero()
554 };
555 let mut container_depends_on_block_constraints = false;
556
557 for FlexItemBoxInlineContentSizesInfo {
558 outer_flex_base_size,
559 outer_min_main_size,
560 outer_max_main_size,
561 min_flex_factors,
562 max_flex_factors,
563 min_content_main_size_for_multiline_container,
564 depends_on_block_constraints,
565 } in item_infos.iter()
566 {
567 container_max_content_size += (*outer_flex_base_size +
573 Au::from_f32_px(
574 max_flex_factors.flex_grow_or_shrink_factor * chosen_max_flex_fraction,
575 ))
576 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
577
578 if self.config.flex_wrap == FlexWrap::NOWRAP {
589 container_min_content_size += (*outer_flex_base_size +
590 Au::from_f32_px(
591 min_flex_factors.flex_grow_or_shrink_factor * chosen_min_flex_fraction,
592 ))
593 .clamp_between_extremums(*outer_min_main_size, *outer_max_main_size);
594 } else {
595 container_min_content_size
596 .max_assign(*min_content_main_size_for_multiline_container);
597 }
598
599 container_depends_on_block_constraints |= depends_on_block_constraints;
600 }
601
602 InlineContentSizesResult {
603 sizes: ContentSizes {
604 min_content: container_min_content_size,
605 max_content: container_max_content_size,
606 },
607 depends_on_block_constraints: container_depends_on_block_constraints,
608 }
609 }
610
611 #[servo_tracing::instrument(
613 name = "FlexContainer::layout",
614 skip_all,
615 fields(self_address = self as *const _ as usize)
616 )]
617 pub(crate) fn layout(
618 &self,
619 layout_context: &LayoutContext,
620 positioning_context: &mut PositioningContext,
621 containing_block: &ContainingBlock,
622 lazy_block_size: &LazySize,
623 ) -> IndependentFormattingContextLayoutResult {
624 let mut flex_context = FlexContext {
625 config: self.config.clone(),
626 layout_context,
627 containing_block,
628 container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative(
630 LogicalVec2 {
631 inline: SizeConstraint::Definite(containing_block.size.inline),
632 block: containing_block.size.block,
633 },
634 ),
635 };
636
637 let container_main_size = match self.config.flex_axis {
640 FlexAxis::Row => containing_block.size.inline,
641 FlexAxis::Column => lazy_block_size.resolve(|| {
642 let mut containing_block = IndefiniteContainingBlock::from(containing_block);
643 containing_block.size.block = None;
644 self.main_content_sizes(layout_context, &containing_block, || &flex_context)
645 .sizes
646 .max_content
647 }),
648 };
649
650 let mut flex_items = Vec::with_capacity(self.children.len());
652
653 let absolutely_positioned_items_with_original_order = self
661 .children
662 .iter()
663 .map(|arcrefcell| {
664 let borrowed = arcrefcell.borrow();
665 match &*borrowed {
666 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned) => {
667 FlexContent::AbsolutelyPositionedBox(absolutely_positioned.clone())
668 },
669 FlexLevelBox::FlexItem(_) => {
670 let item = AtomicRef::map(borrowed, |child| match child {
671 FlexLevelBox::FlexItem(item) => item,
672 _ => unreachable!(),
673 });
674 flex_items.push(item);
675 FlexContent::FlexItemPlaceholder
676 },
677 }
678 })
679 .collect::<Vec<_>>();
680 let flex_item_boxes = flex_items.iter().map(|child| &**child);
681 let flex_items = flex_item_boxes
682 .map(|flex_item_box| FlexItem::new(&flex_context, flex_item_box))
683 .collect::<Vec<_>>();
684
685 let row_gap = self.style.get_row_gap();
686 let column_gap = self.style.get_column_gap();
687 let (cross_gap, main_gap) = match flex_context.config.flex_axis {
688 FlexAxis::Row => (row_gap, column_gap),
689 FlexAxis::Column => (column_gap, row_gap),
690 };
691 let cross_gap = match cross_gap {
692 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
693 .maybe_to_used_value(
694 flex_context
695 .container_inner_size_constraint
696 .cross
697 .to_definite(),
698 )
699 .unwrap_or_default(),
700 LengthPercentageOrNormal::Normal => Au::zero(),
701 };
702 let main_gap = match main_gap {
703 LengthPercentageOrNormal::LengthPercentage(length_percent) => length_percent
704 .maybe_to_used_value(
705 flex_context
706 .container_inner_size_constraint
707 .main
708 .to_definite(),
709 )
710 .unwrap_or_default(),
711 LengthPercentageOrNormal::Normal => Au::zero(),
712 };
713
714 let initial_line_layouts = do_initial_flex_line_layout(
717 &mut flex_context,
718 container_main_size,
719 flex_items,
720 main_gap,
721 );
722
723 let line_count = initial_line_layouts.len();
724 let content_cross_size = initial_line_layouts
725 .iter()
726 .map(|layout| layout.line_size.cross)
727 .sum::<Au>() +
728 cross_gap * (line_count as i32 - 1);
729 let content_block_size = match self.config.flex_axis {
730 FlexAxis::Row => content_cross_size,
731 FlexAxis::Column => container_main_size,
732 };
733
734 let container_cross_size = match self.config.flex_axis {
736 FlexAxis::Row => lazy_block_size.resolve(|| content_cross_size),
737 FlexAxis::Column => containing_block.size.inline,
738 };
739
740 let container_size = FlexRelativeVec2 {
741 main: container_main_size,
742 cross: container_cross_size,
743 };
744
745 let mut remaining_free_cross_space = container_cross_size - content_cross_size;
746
747 let num_lines = initial_line_layouts.len();
752 let resolved_align_content: AlignFlags = {
753 let align_content_style = flex_context.config.align_content.primary();
755 let mut is_safe = align_content_style.flags() == AlignFlags::SAFE;
756
757 let mut resolved_align_content = match align_content_style.value() {
760 AlignFlags::NORMAL => AlignFlags::STRETCH,
761 align_content => align_content,
762 };
763
764 let fallback_is_needed = match resolved_align_content {
772 _ if remaining_free_cross_space <= Au::zero() => true,
773 AlignFlags::STRETCH => num_lines < 1,
774 AlignFlags::SPACE_BETWEEN | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
775 num_lines < 2
776 },
777 _ => false,
778 };
779
780 if fallback_is_needed {
781 (resolved_align_content, is_safe) = match resolved_align_content {
782 AlignFlags::STRETCH => (AlignFlags::FLEX_START, false),
783 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
784 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
785 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
786 _ => (resolved_align_content, is_safe),
787 }
788 };
789
790 if remaining_free_cross_space <= Au::zero() && is_safe {
792 resolved_align_content = AlignFlags::START;
793 }
794
795 resolved_align_content
796 };
797
798 let flex_wrap_is_reversed = flex_context.config.flex_wrap_is_reversed;
800 let resolved_align_content = self
801 .config
802 .resolve_reversable_flex_alignment(resolved_align_content, flex_wrap_is_reversed);
803 let mut cross_start_position_cursor = match resolved_align_content {
804 AlignFlags::START if flex_wrap_is_reversed => remaining_free_cross_space,
805 AlignFlags::START => Au::zero(),
806 AlignFlags::END if flex_wrap_is_reversed => Au::zero(),
807 AlignFlags::END => remaining_free_cross_space,
808 AlignFlags::CENTER => remaining_free_cross_space / 2,
809 AlignFlags::STRETCH => Au::zero(),
810 AlignFlags::SPACE_BETWEEN => Au::zero(),
811 AlignFlags::SPACE_AROUND => remaining_free_cross_space / num_lines as i32 / 2,
812 AlignFlags::SPACE_EVENLY => remaining_free_cross_space / (num_lines as i32 + 1),
813
814 _ => Au::zero(),
816 };
817
818 let inline_axis_is_main_axis = self.config.flex_axis == FlexAxis::Row;
819 let mut baseline_alignment_participating_baselines = Baselines::default();
820 let mut all_baselines = Baselines::default();
821 let mut flex_item_fragments = initial_line_layouts.into_iter().enumerate().flat_map(
822 |(index, initial_line_layout)| {
823 let (space_to_add_to_line, space_to_add_after_line) =
827 allocate_free_cross_space_for_flex_line(
828 resolved_align_content,
829 remaining_free_cross_space,
830 (num_lines - index) as i32,
831 );
832 remaining_free_cross_space -= space_to_add_to_line + space_to_add_after_line;
833
834 let final_line_cross_size =
835 initial_line_layout.line_size.cross + space_to_add_to_line;
836 let mut final_line_layout = initial_line_layout.finish_with_final_cross_size(
837 &mut flex_context,
838 main_gap,
839 final_line_cross_size,
840 );
841
842 let line_cross_start_position = cross_start_position_cursor;
843 cross_start_position_cursor = line_cross_start_position +
844 final_line_cross_size +
845 space_to_add_after_line +
846 cross_gap;
847
848 let flow_relative_line_position =
849 match (self.config.flex_axis, flex_wrap_is_reversed) {
850 (FlexAxis::Row, false) => LogicalVec2 {
851 block: line_cross_start_position,
852 inline: Au::zero(),
853 },
854 (FlexAxis::Row, true) => LogicalVec2 {
855 block: container_cross_size -
856 line_cross_start_position -
857 final_line_layout.cross_size,
858 inline: Au::zero(),
859 },
860 (FlexAxis::Column, false) => LogicalVec2 {
861 block: Au::zero(),
862 inline: line_cross_start_position,
863 },
864 (FlexAxis::Column, true) => LogicalVec2 {
865 block: Au::zero(),
866 inline: container_cross_size -
867 line_cross_start_position -
868 final_line_cross_size,
869 },
870 };
871
872 if inline_axis_is_main_axis {
873 let line_shared_alignment_baseline = final_line_layout
874 .shared_alignment_baseline
875 .map(|baseline| baseline + flow_relative_line_position.block);
876 if index == 0 {
877 baseline_alignment_participating_baselines.first =
878 line_shared_alignment_baseline;
879 }
880 if index == num_lines - 1 {
881 baseline_alignment_participating_baselines.last =
882 line_shared_alignment_baseline;
883 }
884 }
885
886 let line_all_baselines = final_line_layout
887 .all_baselines
888 .offset(flow_relative_line_position.block);
889 if index == 0 {
890 all_baselines.first = line_all_baselines.first;
891 }
892 if index == num_lines - 1 {
893 all_baselines.last = line_all_baselines.last;
894 }
895
896 let physical_line_position =
897 flow_relative_line_position.to_physical_size(self.style.writing_mode);
898 for (fragment, _) in &mut final_line_layout.item_fragments {
899 fragment.base.translate_rect(physical_line_position);
900 }
901 final_line_layout.item_fragments
902 },
903 );
904
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
996 },
997 (AlignFlags::STRETCH, false) => AlignFlags::START,
998 (AlignFlags::SPACE_BETWEEN, false) => AlignFlags::START,
999 (AlignFlags::SPACE_BETWEEN, true) => AlignFlags::END,
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 line_cross_size_before_layout = Self::cross_size_before_layout(flex_context);
1239 let layout_item = |(item, used_main_size): (FlexItem<'items>, Au)| {
1240 let used_cross_size = line_cross_size_before_layout.and_then(|line_cross_size| {
1241 item.used_cross_size_before_layout(flex_context, used_main_size, line_cross_size)
1242 });
1243 let layout_result = item.layout(used_main_size, flex_context, used_cross_size);
1244 FlexLineItem {
1245 item,
1246 layout_result,
1247 used_main_size,
1248 }
1249 };
1250 let items: Vec<_> = if flex_context
1251 .layout_context
1252 .should_parallelize_layout(items.iter().map(FlexItem::subtree_size))
1253 {
1254 items
1255 .into_par_iter()
1256 .zip(item_used_main_sizes.into_par_iter())
1257 .map(layout_item)
1258 .collect()
1259 } else {
1260 items
1261 .into_iter()
1262 .zip(item_used_main_sizes)
1263 .map(layout_item)
1264 .collect()
1265 };
1266
1267 let line_cross_size = Self::cross_size(&items, flex_context);
1269 let line_size = FlexRelativeVec2 {
1270 main: container_main_size,
1271 cross: line_cross_size,
1272 };
1273
1274 InitialFlexLineLayout {
1275 items,
1276 line_size,
1277 free_space_in_main_axis,
1278 }
1279 }
1280
1281 fn resolve_flexible_lengths<'items>(
1284 items: &'items [FlexItem<'items>],
1285 outer_hypothetical_main_sizes_sum: Au,
1286 container_main_size: Au,
1287 ) -> (Vec<Au>, Au) {
1288 struct FlexibleLengthResolutionItem<'items> {
1289 item: &'items FlexItem<'items>,
1290 frozen: Cell<bool>,
1291 target_main_size: Cell<Au>,
1292 flex_factor: f32,
1293 min_max_violation_kind: Cell<Ordering>,
1294 }
1295
1296 let grow = outer_hypothetical_main_sizes_sum < container_main_size;
1301
1302 let mut frozen_count = 0;
1303 let items: Vec<_> = items
1304 .iter()
1305 .map(|item| {
1306 let target_main_size = Cell::new(item.flex_base_size);
1309
1310 let flex_factor = if grow {
1317 item.box_.style().get_position().flex_grow.0
1318 } else {
1319 item.box_.style().get_position().flex_shrink.0
1320 };
1321
1322 let is_inflexible = flex_factor == 0. ||
1323 if grow {
1324 item.flex_base_size > item.hypothetical_main_size
1325 } else {
1326 item.flex_base_size < item.hypothetical_main_size
1327 };
1328
1329 let frozen = Cell::new(false);
1330 if is_inflexible {
1331 frozen_count += 1;
1332 frozen.set(true);
1333 target_main_size.set(item.hypothetical_main_size);
1334 }
1335
1336 FlexibleLengthResolutionItem {
1337 item,
1338 frozen,
1339 target_main_size,
1340 flex_factor,
1341 min_max_violation_kind: Cell::new(Ordering::Equal),
1343 }
1344 })
1345 .collect();
1346
1347 let unfrozen_items = || items.iter().filter(|item| !item.frozen.get());
1348 let main_sizes = |items: Vec<FlexibleLengthResolutionItem>| {
1349 items
1350 .into_iter()
1351 .map(|item| item.target_main_size.get())
1352 .collect()
1353 };
1354
1355 let free_space = |all_items_frozen| {
1360 let items_size = items
1361 .iter()
1362 .map(|item| {
1363 item.item.pbm_auto_is_zero.main +
1364 if all_items_frozen || item.frozen.get() {
1365 item.target_main_size.get()
1366 } else {
1367 item.item.flex_base_size
1368 }
1369 })
1370 .sum();
1371 container_main_size - items_size
1372 };
1373
1374 let initial_free_space = free_space(false);
1375 loop {
1376 let mut remaining_free_space = free_space(false);
1378 if frozen_count >= items.len() {
1381 return (main_sizes(items), remaining_free_space);
1382 }
1383
1384 let unfrozen_items_flex_factor_sum =
1390 unfrozen_items().map(|item| item.flex_factor).sum();
1391 if unfrozen_items_flex_factor_sum < 1. {
1392 let multiplied = initial_free_space.scale_by(unfrozen_items_flex_factor_sum);
1393 if multiplied.abs() < remaining_free_space.abs() {
1394 remaining_free_space = multiplied
1395 }
1396 }
1397
1398 if !remaining_free_space.is_zero() {
1404 if grow {
1410 for item in unfrozen_items() {
1411 let ratio = item.flex_factor / unfrozen_items_flex_factor_sum;
1412 item.target_main_size
1413 .set(item.item.flex_base_size + remaining_free_space.scale_by(ratio));
1414 }
1415 } else {
1424 let scaled_shrink_factor = |item: &FlexibleLengthResolutionItem| {
1426 item.item.flex_base_size.scale_by(item.flex_factor)
1427 };
1428 let scaled_shrink_factors_sum: Au =
1429 unfrozen_items().map(scaled_shrink_factor).sum();
1430 if scaled_shrink_factors_sum > Au::zero() {
1431 for item in unfrozen_items() {
1432 let ratio = scaled_shrink_factor(item).0 as f32 /
1433 scaled_shrink_factors_sum.0 as f32;
1434 item.target_main_size.set(
1435 item.item.flex_base_size -
1436 remaining_free_space.abs().scale_by(ratio),
1437 );
1438 }
1439 }
1440 }
1441 }
1442
1443 let mut total_violation = Au::zero();
1449 for item in unfrozen_items() {
1450 let unclamped = item.target_main_size.get();
1451 let clamped = unclamped.clamp_between_extremums(
1452 item.item.content_min_main_size,
1453 item.item.content_max_main_size,
1454 );
1455 item.target_main_size.set(clamped);
1456 item.min_max_violation_kind.set(clamped.cmp(&unclamped));
1459 total_violation += clamped - unclamped;
1460 }
1461
1462 match total_violation.cmp(&Au::zero()) {
1469 Ordering::Equal => {
1470 let remaining_free_space = free_space(true);
1473 return (main_sizes(items), remaining_free_space);
1474 },
1475 total_violation_kind => {
1476 for item in unfrozen_items() {
1477 if item.min_max_violation_kind.get() == total_violation_kind {
1478 item.frozen.set(true);
1479 frozen_count += 1;
1480 }
1481 }
1482 },
1483 }
1484 }
1485 }
1486
1487 fn cross_size_before_layout(flex_context: &FlexContext) -> Option<Au> {
1491 flex_context
1492 .container_inner_size_constraint
1493 .cross
1494 .to_definite()
1495 .filter(|_| flex_context.config.container_is_single_line)
1496 }
1497
1498 fn cross_size<'items>(items: &'items [FlexLineItem<'items>], flex_context: &FlexContext) -> Au {
1500 if let Some(size) = Self::cross_size_before_layout(flex_context) {
1501 return size;
1502 }
1503
1504 let mut max_ascent = Au::zero();
1505 let mut max_descent = Au::zero();
1506 let mut max_outer_hypothetical_cross_size = Au::zero();
1507 for item in items.iter() {
1508 if matches!(
1510 item.item.align_self.0.value(),
1511 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1512 ) {
1513 let baseline = item.get_or_synthesize_baseline_with_cross_size(
1514 item.layout_result.hypothetical_cross_size,
1515 &flex_context.config,
1516 );
1517 let hypothetical_margin_box_cross_size =
1518 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross;
1519 max_ascent = max_ascent.max(baseline);
1520 max_descent = max_descent.max(hypothetical_margin_box_cross_size - baseline);
1521 } else {
1522 max_outer_hypothetical_cross_size = max_outer_hypothetical_cross_size.max(
1523 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross,
1524 );
1525 }
1526 }
1527
1528 let largest = max_outer_hypothetical_cross_size.max(max_ascent + max_descent);
1530 match flex_context.container_inner_size_constraint.cross {
1531 SizeConstraint::MinMax(min, max) if flex_context.config.container_is_single_line => {
1532 largest.clamp_between_extremums(min, max)
1533 },
1534 _ => largest,
1535 }
1536 }
1537
1538 fn finish_with_final_cross_size(
1539 mut self,
1540 flex_context: &mut FlexContext,
1541 main_gap: Au,
1542 final_line_cross_size: Au,
1543 ) -> FinalFlexLineLayout {
1544 let auto_margins_count = self
1552 .items
1553 .iter()
1554 .map(|item| {
1555 item.item.margin.main_start.is_auto() as u32 +
1556 item.item.margin.main_end.is_auto() as u32
1557 })
1558 .sum::<u32>();
1559 let (space_distributed_to_auto_main_margins, free_space_in_main_axis) =
1560 if self.free_space_in_main_axis > Au::zero() && auto_margins_count > 0 {
1561 (
1562 self.free_space_in_main_axis / auto_margins_count as i32,
1563 Au::zero(),
1564 )
1565 } else {
1566 (Au::zero(), self.free_space_in_main_axis)
1567 };
1568
1569 let item_count = self.items.len();
1572 let mut shared_alignment_baseline = None;
1573 let mut item_used_cross_sizes = Vec::with_capacity(item_count);
1574 let mut item_margins = Vec::with_capacity(item_count);
1575 for item in self.items.iter_mut() {
1576 let cross_axis = match flex_context.config.flex_axis {
1577 FlexAxis::Row => Direction::Block,
1578 FlexAxis::Column => Direction::Inline,
1579 };
1580 let layout = &mut item.layout_result;
1581 let get_content_size = || match cross_axis {
1582 Direction::Block => layout.content_block_size.into(),
1583 Direction::Inline => item
1584 .item
1585 .inline_content_sizes(flex_context, item.used_main_size),
1586 };
1587 let used_cross_size =
1588 item.item
1589 .used_cross_size(cross_axis, final_line_cross_size, get_content_size);
1590 item_used_cross_sizes.push(used_cross_size);
1591
1592 let needs_new_layout = match cross_axis {
1599 Direction::Block => {
1600 (match item.item.content_cross_sizes.preferred {
1601 Size::Initial => item.item.automatic_cross_size == Size::Stretch,
1602 Size::Stretch => true,
1603 _ => false,
1604 }) && SizeConstraint::Definite(used_cross_size) !=
1605 layout.containing_block_size.block &&
1606 layout.depends_on_block_constraints
1607 },
1608 Direction::Inline => used_cross_size != layout.containing_block_size.inline,
1609 };
1610 if needs_new_layout {
1611 #[cfg(feature = "tracing")]
1612 tracing::warn!(
1613 name: "Flex item stretch cache miss",
1614 cached_inline = ?layout.containing_block_size.inline,
1615 cached_block = ?layout.containing_block_size.block,
1616 required_cross_size = ?used_cross_size,
1617 cross_axis = ?cross_axis,
1618 depends_on_block_constraints = layout.depends_on_block_constraints,
1619 );
1620 *layout =
1621 item.item
1622 .layout(item.used_main_size, flex_context, Some(used_cross_size));
1623 }
1624
1625 let baseline = item
1626 .get_or_synthesize_baseline_with_cross_size(used_cross_size, &flex_context.config);
1627 if matches!(
1628 item.item.align_self.0.value(),
1629 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1630 ) {
1631 shared_alignment_baseline =
1632 Some(shared_alignment_baseline.unwrap_or(baseline).max(baseline));
1633 }
1634 item.layout_result
1635 .flex_alignment_baseline_relative_to_margin_box = Some(baseline);
1636
1637 item_margins.push(item.item.resolve_auto_margins(
1638 flex_context,
1639 final_line_cross_size,
1640 used_cross_size,
1641 space_distributed_to_auto_main_margins,
1642 ));
1643 }
1644
1645 let resolved_justify_content: AlignFlags = {
1651 let justify_content_style = flex_context.config.justify_content.primary();
1652
1653 let mut resolved_justify_content = justify_content_style.value();
1655 let mut is_safe = justify_content_style.flags() == AlignFlags::SAFE;
1656
1657 if item_count <= 1 || free_space_in_main_axis <= Au::zero() {
1662 (resolved_justify_content, is_safe) = match resolved_justify_content {
1663 AlignFlags::STRETCH => (AlignFlags::FLEX_START, true),
1664 AlignFlags::SPACE_BETWEEN => (AlignFlags::FLEX_START, true),
1665 AlignFlags::SPACE_AROUND => (AlignFlags::CENTER, true),
1666 AlignFlags::SPACE_EVENLY => (AlignFlags::CENTER, true),
1667 _ => (resolved_justify_content, is_safe),
1668 }
1669 };
1670
1671 if free_space_in_main_axis <= Au::zero() && is_safe {
1673 resolved_justify_content = AlignFlags::START;
1674 }
1675
1676 resolved_justify_content
1677 };
1678
1679 let main_start_position = match resolved_justify_content {
1681 AlignFlags::START => Au::zero(),
1682 AlignFlags::FLEX_START => {
1683 if flex_context.config.flex_direction_is_reversed {
1684 free_space_in_main_axis
1685 } else {
1686 Au::zero()
1687 }
1688 },
1689 AlignFlags::END => free_space_in_main_axis,
1690 AlignFlags::FLEX_END => {
1691 if flex_context.config.flex_direction_is_reversed {
1692 Au::zero()
1693 } else {
1694 free_space_in_main_axis
1695 }
1696 },
1697 AlignFlags::CENTER => free_space_in_main_axis / 2,
1698 AlignFlags::STRETCH => Au::zero(),
1699 AlignFlags::SPACE_BETWEEN => Au::zero(),
1700 AlignFlags::SPACE_AROUND => (free_space_in_main_axis / item_count as i32) / 2,
1701 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1702
1703 _ => Au::zero(),
1705 };
1706
1707 let item_main_interval = match resolved_justify_content {
1708 AlignFlags::START => Au::zero(),
1709 AlignFlags::FLEX_START => Au::zero(),
1710 AlignFlags::END => Au::zero(),
1711 AlignFlags::FLEX_END => Au::zero(),
1712 AlignFlags::CENTER => Au::zero(),
1713 AlignFlags::STRETCH => Au::zero(),
1714 AlignFlags::SPACE_BETWEEN => free_space_in_main_axis / (item_count - 1) as i32,
1715 AlignFlags::SPACE_AROUND => free_space_in_main_axis / item_count as i32,
1716 AlignFlags::SPACE_EVENLY => free_space_in_main_axis / (item_count + 1) as i32,
1717
1718 _ => Au::zero(),
1720 };
1721 let item_main_interval = item_main_interval + main_gap;
1722
1723 let mut all_baselines = Baselines::default();
1724 let mut main_position_cursor = main_start_position;
1725
1726 let items = std::mem::take(&mut self.items);
1727 let item_fragments = izip!(items, item_margins, item_used_cross_sizes.iter())
1728 .map(|(item, item_margin, item_used_cross_size)| {
1729 let item_used_size = FlexRelativeVec2 {
1730 main: item.used_main_size,
1731 cross: *item_used_cross_size,
1732 };
1733 item.collect_fragment(
1734 &self,
1735 item_used_size,
1736 item_margin,
1737 item_main_interval,
1738 final_line_cross_size,
1739 &shared_alignment_baseline,
1740 flex_context,
1741 &mut all_baselines,
1742 &mut main_position_cursor,
1743 )
1744 })
1745 .collect();
1746
1747 FinalFlexLineLayout {
1748 cross_size: final_line_cross_size,
1749 item_fragments,
1750 all_baselines,
1751 shared_alignment_baseline,
1752 }
1753 }
1754}
1755
1756impl FlexItem<'_> {
1757 #[servo_tracing::instrument(
1762 name = "FlexItem::layout",
1763 skip_all,
1764 fields(
1765 self_address = self as *const _ as usize,
1766 box_address = self.box_ as *const _ as usize,
1767 )
1768 )]
1769
1770 fn layout(
1771 &self,
1772 used_main_size: Au,
1773 flex_context: &FlexContext,
1774 used_cross_size_override: Option<Au>,
1775 ) -> FlexItemLayoutResult {
1776 let containing_block = flex_context.containing_block;
1777 let independent_formatting_context = &self.box_.independent_formatting_context;
1778 let is_table = independent_formatting_context.is_table();
1779 let mut positioning_context = PositioningContext::default();
1780 let item_writing_mode = independent_formatting_context.style().writing_mode;
1781 let item_is_horizontal = item_writing_mode.is_horizontal();
1782 let flex_axis = flex_context.config.flex_axis;
1783 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
1784 containing_block.style.writing_mode.is_horizontal(),
1785 item_is_horizontal,
1786 flex_axis,
1787 );
1788
1789 let (inline_size, block_size) = if cross_axis_is_item_block_axis {
1790 let cross_size = match used_cross_size_override {
1791 Some(s) => SizeConstraint::Definite(s),
1792 None => {
1793 let inline_stretch_size =
1794 Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.main);
1795 let block_stretch_size = containing_block
1796 .size
1797 .block
1798 .to_definite()
1799 .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
1800 let tentative_block_content_size = independent_formatting_context
1801 .tentative_block_content_size(
1802 self.preferred_aspect_ratio,
1803 inline_stretch_size,
1804 );
1805 if let Some(block_content_size) = tentative_block_content_size {
1806 SizeConstraint::Definite(self.content_cross_sizes.resolve(
1807 Direction::Block,
1808 Size::FitContent,
1809 Au::zero,
1810 block_stretch_size,
1811 || block_content_size,
1812 is_table,
1813 ))
1814 } else {
1815 self.content_cross_sizes.resolve_extrinsic(
1816 Size::FitContent,
1817 Au::zero(),
1818 block_stretch_size,
1819 )
1820 }
1821 },
1822 };
1823 (used_main_size, cross_size)
1824 } else {
1825 let cross_size = used_cross_size_override.unwrap_or_else(|| {
1826 let stretch_size =
1827 Au::zero().max(containing_block.size.inline - self.pbm_auto_is_zero.cross);
1828 self.content_cross_sizes.resolve(
1829 Direction::Inline,
1830 Size::FitContent,
1831 Au::zero,
1832 Some(stretch_size),
1833 || self.inline_content_sizes(flex_context, used_main_size),
1834 is_table,
1835 )
1836 });
1837 let is_grid = self.box_.independent_formatting_context.is_grid();
1846 let main_size = if is_grid ||
1847 self.flex_base_size_is_definite ||
1848 flex_context
1849 .container_inner_size_constraint
1850 .main
1851 .is_definite()
1852 {
1853 SizeConstraint::Definite(used_main_size)
1854 } else {
1855 SizeConstraint::default()
1856 };
1857 (cross_size, main_size)
1858 };
1859
1860 let item_style = independent_formatting_context.style();
1861 let item_as_containing_block = ContainingBlock {
1862 size: ContainingBlockSize {
1863 inline: inline_size,
1864 block: block_size,
1865 },
1866 style: item_style,
1867 };
1868
1869 let lazy_block_size = if !cross_axis_is_item_block_axis {
1870 used_main_size.into()
1871 } else if let Some(cross_size) = used_cross_size_override {
1872 cross_size.into()
1873 } else {
1874 let stretch_size = containing_block
1875 .size
1876 .block
1877 .to_definite()
1878 .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
1879 LazySize::new(
1880 &self.content_cross_sizes,
1881 Direction::Block,
1882 Size::FitContent,
1883 Au::zero,
1884 stretch_size,
1885 is_table,
1886 )
1887 };
1888
1889 let layout = independent_formatting_context.layout(
1890 flex_context.layout_context,
1891 &mut positioning_context,
1892 &item_as_containing_block,
1893 containing_block,
1894 self.preferred_aspect_ratio,
1895 &lazy_block_size,
1896 );
1897 let IndependentFormattingContextLayoutResult {
1898 fragments,
1899 content_block_size,
1900 baselines: content_box_baselines,
1901 depends_on_block_constraints,
1902 specific_layout_info,
1903 ..
1904 } = layout;
1905
1906 let hypothetical_cross_size = if cross_axis_is_item_block_axis {
1907 lazy_block_size.resolve(|| content_block_size)
1908 } else {
1909 inline_size
1910 };
1911
1912 let item_inline_axis_is_horizontal = item_style.writing_mode.is_horizontal();
1913 let container_inline_axis_is_horizontal = flex_context.config.writing_mode.is_horizontal();
1914 let container_main_axis_is_horizontal = match flex_axis {
1915 FlexAxis::Row => container_inline_axis_is_horizontal,
1916 FlexAxis::Column => !container_inline_axis_is_horizontal,
1917 };
1918 let item_inline_axis_parallel_to_container_inline_axis =
1919 item_inline_axis_is_horizontal == container_inline_axis_is_horizontal;
1920 let item_inline_axis_parallel_to_container_main_axis =
1921 item_inline_axis_is_horizontal == container_main_axis_is_horizontal;
1922
1923 let content_baselines_relative_to_margin_box = content_box_baselines.offset(
1924 self.margin.cross_start.auto_is(Au::zero) +
1925 self.padding.cross_start +
1926 self.border.cross_start,
1927 );
1928
1929 let content_baselines_for_parent_relative_to_margin_box =
1930 if item_inline_axis_parallel_to_container_inline_axis {
1931 content_baselines_relative_to_margin_box
1932 } else {
1933 Baselines::default()
1934 };
1935
1936 let flex_alignment_baseline_relative_to_margin_box =
1937 if item_inline_axis_parallel_to_container_main_axis {
1938 match self.align_self.0.value() {
1939 AlignFlags::BASELINE => content_baselines_relative_to_margin_box.first,
1941 AlignFlags::LAST_BASELINE => content_baselines_relative_to_margin_box.last,
1942 _ => None,
1943 }
1944 } else {
1945 None
1946 };
1947
1948 FlexItemLayoutResult {
1949 hypothetical_cross_size,
1950 fragments,
1951 positioning_context,
1952 content_baselines_for_parent_relative_to_margin_box,
1953 flex_alignment_baseline_relative_to_margin_box,
1954 content_block_size,
1955 containing_block_size: item_as_containing_block.size,
1956 depends_on_block_constraints,
1957 specific_layout_info,
1958 }
1959 }
1960
1961 fn synthesized_baseline_relative_to_margin_box(
1964 &self,
1965 content_size: Au,
1966 config: &FlexContainerConfig,
1967 ) -> Au {
1968 let own_writing_mode = self.box_.style().writing_mode;
1969 let distance_from_cross_start_to_line_under = |writing_mode: WritingMode| -> Au {
1978 let line_under_edge_is_on_same_side_as_cross_start = (writing_mode.is_horizontal() &&
1983 config.flex_wrap_is_reversed) ||
1984 (writing_mode.is_vertical_lr() != config.flex_wrap_is_reversed);
1985 if line_under_edge_is_on_same_side_as_cross_start {
1986 self.margin.cross_start.auto_is(Au::zero)
1992 } else {
1993 content_size +
1999 self.margin.cross_start.auto_is(Au::zero) +
2000 self.padding.cross_sum() +
2001 self.border.cross_sum()
2002 }
2003 };
2004
2005 let block_flow_direction_is_orthogonal_to_main_axis = |writing_mode: WritingMode| {
2016 writing_mode.is_vertical() != (config.flex_axis == FlexAxis::Row)
2017 };
2018 if block_flow_direction_is_orthogonal_to_main_axis(own_writing_mode) {
2019 distance_from_cross_start_to_line_under(own_writing_mode)
2020 } else if block_flow_direction_is_orthogonal_to_main_axis(config.writing_mode) {
2021 distance_from_cross_start_to_line_under(config.writing_mode)
2022 } else if own_writing_mode.is_vertical() {
2023 distance_from_cross_start_to_line_under(WritingMode::WRITING_MODE_HORIZONTAL_TB)
2024 } else {
2025 let used_writing_mode = match own_writing_mode.is_bidi_ltr() {
2026 true => WritingMode::WRITING_MODE_VERTICAL_LR,
2027 false => WritingMode::WRITING_MODE_VERTICAL_RL,
2028 };
2029
2030 distance_from_cross_start_to_line_under(used_writing_mode)
2031 }
2032 }
2033
2034 fn subtree_size(&self) -> usize {
2035 self.box_.independent_formatting_context.subtree_size()
2036 }
2037
2038 fn resolve_auto_margins(
2044 &self,
2045 flex_context: &FlexContext,
2046 line_cross_size: Au,
2047 item_cross_content_size: Au,
2048 space_distributed_to_auto_main_margins: Au,
2049 ) -> FlexRelativeSides<Au> {
2050 let main_start = self
2051 .margin
2052 .main_start
2053 .auto_is(|| space_distributed_to_auto_main_margins);
2054 let main_end = self
2055 .margin
2056 .main_end
2057 .auto_is(|| space_distributed_to_auto_main_margins);
2058
2059 let auto_count = match (self.margin.cross_start, self.margin.cross_end) {
2060 (AuOrAuto::LengthPercentage(cross_start), AuOrAuto::LengthPercentage(cross_end)) => {
2061 return FlexRelativeSides {
2062 cross_start,
2063 cross_end,
2064 main_start,
2065 main_end,
2066 };
2067 },
2068 (AuOrAuto::Auto, AuOrAuto::Auto) => 2,
2069 _ => 1,
2070 };
2071 let outer_cross_size = self.pbm_auto_is_zero.cross + item_cross_content_size;
2072 let available = line_cross_size - outer_cross_size;
2073 let cross_start;
2074 let cross_end;
2075 if available > Au::zero() {
2076 let each_auto_margin = available / auto_count;
2077 cross_start = self.margin.cross_start.auto_is(|| each_auto_margin);
2078 cross_end = self.margin.cross_end.auto_is(|| each_auto_margin);
2079 } else {
2080 let flex_wrap = flex_context.containing_block.style.get_position().flex_wrap;
2093 let flex_wrap_reverse = match flex_wrap {
2094 FlexWrap::NOWRAP | FlexWrap::WRAP => false,
2095 FlexWrap::WRAP_REVERSE => true,
2096 _ => unreachable!("FlexWrap::BALANCE should be disabled"),
2097 };
2098 if flex_wrap_reverse {
2102 cross_start = self.margin.cross_start.auto_is(|| available);
2103 cross_end = self.margin.cross_end.auto_is(Au::zero);
2104 } else {
2105 cross_start = self.margin.cross_start.auto_is(Au::zero);
2106 cross_end = self.margin.cross_end.auto_is(|| available);
2107 }
2108 }
2109
2110 FlexRelativeSides {
2111 cross_start,
2112 cross_end,
2113 main_start,
2114 main_end,
2115 }
2116 }
2117
2118 fn align_along_cross_axis(
2120 &self,
2121 margin: &FlexRelativeSides<Au>,
2122 used_cross_size: &Au,
2123 line_cross_size: Au,
2124 propagated_baseline: Au,
2125 max_propagated_baseline: Au,
2126 wrap_reverse: bool,
2127 ) -> Au {
2128 let ending_alignment = line_cross_size - *used_cross_size - self.pbm_auto_is_zero.cross;
2129 let outer_cross_start =
2130 if self.margin.cross_start.is_auto() || self.margin.cross_end.is_auto() {
2131 Au::zero()
2132 } else {
2133 match self.align_self.0.value() {
2134 AlignFlags::STRETCH => Au::zero(),
2135 AlignFlags::CENTER => ending_alignment / 2,
2136 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE => {
2137 max_propagated_baseline - propagated_baseline
2138 },
2139 AlignFlags::START => {
2140 if !wrap_reverse {
2141 Au::zero()
2142 } else {
2143 ending_alignment
2144 }
2145 },
2146 AlignFlags::END => {
2147 if !wrap_reverse {
2148 ending_alignment
2149 } else {
2150 Au::zero()
2151 }
2152 },
2153 _ => Au::zero(),
2154 }
2155 };
2156 outer_cross_start + margin.cross_start + self.border.cross_start + self.padding.cross_start
2157 }
2158
2159 #[inline]
2160 fn inline_content_sizes(&self, flex_context: &FlexContext, block_size: Au) -> ContentSizes {
2161 self.box_.inline_content_sizes(
2162 flex_context,
2163 SizeConstraint::Definite(block_size),
2164 self.preferred_aspect_ratio,
2165 )
2166 }
2167
2168 fn used_cross_size(
2171 &self,
2172 cross_axis: Direction,
2173 line_cross_size: Au,
2174 get_content_size: impl FnOnce() -> ContentSizes,
2175 ) -> Au {
2176 self.content_cross_sizes.resolve(
2177 cross_axis,
2178 self.automatic_cross_size,
2179 Au::zero,
2180 Some(Au::zero().max(line_cross_size - self.pbm_auto_is_zero.cross)),
2181 get_content_size,
2182 self.box_.independent_formatting_context.is_table() && cross_axis == Direction::Inline,
2187 )
2188 }
2189
2190 fn used_cross_size_before_layout(
2193 &self,
2194 flex_context: &FlexContext,
2195 used_main_size: Au,
2196 line_cross_size: Au,
2197 ) -> Option<Au> {
2198 match flex_context.config.flex_axis {
2199 FlexAxis::Column => Some(self.used_cross_size(
2200 Direction::Inline,
2201 line_cross_size,
2202 || self.inline_content_sizes(flex_context, used_main_size),
2203 )),
2204 FlexAxis::Row => {
2208 let mut needs_content_size = false;
2209 let used_cross_size =
2210 self.used_cross_size(Direction::Block, line_cross_size, || {
2211 needs_content_size = true;
2212 ContentSizes::default()
2213 });
2214 (!needs_content_size).then_some(used_cross_size)
2215 },
2216 }
2217 }
2218}
2219
2220impl FlexItemBox {
2221 fn to_flex_item<'a>(
2222 &self,
2223 layout_context: &LayoutContext,
2224 containing_block: &IndefiniteContainingBlock,
2225 content_box_sizes_and_pbm: &ContentBoxSizesAndPBM,
2226 config: &FlexContainerConfig,
2227 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2228 ) -> FlexItem<'_> {
2229 let flex_axis = config.flex_axis;
2230 let style = self.style();
2231 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
2232 containing_block.style.writing_mode.is_horizontal(),
2233 style.writing_mode.is_horizontal(),
2234 flex_axis,
2235 );
2236 let main_axis = if cross_axis_is_item_block_axis {
2237 Direction::Inline
2238 } else {
2239 Direction::Block
2240 };
2241 let align_self = AlignItems(config.resolve_align_self_for_child(style));
2242
2243 let ContentBoxSizesAndPBM {
2244 content_box_sizes,
2245 pbm,
2246 depends_on_block_constraints,
2247 preferred_size_computes_to_auto,
2248 } = content_box_sizes_and_pbm;
2249
2250 let preferred_aspect_ratio = self
2251 .independent_formatting_context
2252 .preferred_aspect_ratio(&pbm.padding_border_sums);
2253 let padding = config.sides_to_flex_relative(pbm.padding);
2254 let border = config.sides_to_flex_relative(pbm.border);
2255 let margin = config.sides_to_flex_relative(pbm.margin);
2256 let padding_border = padding.sum_by_axis() + border.sum_by_axis();
2257 let margin_auto_is_zero = config.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
2258 let pbm_auto_is_zero = FlexRelativeVec2 {
2259 main: padding_border.main,
2260 cross: padding_border.cross,
2261 } + margin_auto_is_zero.sum_by_axis();
2262 let (content_main_sizes, content_cross_sizes, cross_size_computes_to_auto) = match flex_axis
2263 {
2264 FlexAxis::Row => (
2265 &content_box_sizes.inline,
2266 &content_box_sizes.block,
2267 preferred_size_computes_to_auto.block,
2268 ),
2269 FlexAxis::Column => (
2270 &content_box_sizes.block,
2271 &content_box_sizes.inline,
2272 preferred_size_computes_to_auto.inline,
2273 ),
2274 };
2275 let automatic_cross_size = if cross_size_computes_to_auto &&
2276 item_with_auto_cross_size_stretches_to_line_size(align_self, &margin)
2277 {
2278 Size::Stretch
2279 } else {
2280 Size::FitContent
2281 };
2282 let automatic_cross_size_for_intrinsic_sizing = if config.container_is_single_line {
2283 automatic_cross_size
2284 } else {
2285 Size::FitContent
2286 };
2287 let containing_block_size = flex_axis.vec2_to_flex_relative(containing_block.size);
2288 let stretch_size = FlexRelativeVec2 {
2289 main: containing_block_size
2290 .main
2291 .map(|v| Au::zero().max(v - pbm_auto_is_zero.main)),
2292 cross: containing_block_size
2293 .cross
2294 .map(|v| Au::zero().max(v - pbm_auto_is_zero.cross)),
2295 };
2296
2297 let is_table = self.independent_formatting_context.is_table();
2298 let tentative_cross_content_size = if cross_axis_is_item_block_axis {
2299 self.independent_formatting_context
2300 .tentative_block_content_size(
2301 preferred_aspect_ratio,
2302 stretch_size.main.unwrap_or_default(),
2303 )
2304 } else {
2305 None
2306 };
2307 let (preferred_cross_size, min_cross_size, max_cross_size) =
2308 if let Some(cross_content_size) = tentative_cross_content_size {
2309 let (preferred, min, max) = content_cross_sizes.resolve_each(
2310 automatic_cross_size_for_intrinsic_sizing,
2311 Au::zero,
2312 stretch_size.cross,
2313 || cross_content_size,
2314 is_table,
2315 );
2316 (Some(preferred), min, max)
2317 } else {
2318 content_cross_sizes.resolve_each_extrinsic(
2319 automatic_cross_size_for_intrinsic_sizing,
2320 Au::zero(),
2321 stretch_size.cross,
2322 )
2323 };
2324 let cross_size = SizeConstraint::new(preferred_cross_size, min_cross_size, max_cross_size);
2325
2326 let transferred_size_suggestion =
2331 LazyCell::new(|| match (preferred_aspect_ratio, cross_size) {
2332 (Some(ratio), SizeConstraint::Definite(cross_size)) => {
2333 Some(ratio.compute_dependent_size(main_axis, cross_size))
2334 },
2335 _ => None,
2336 });
2337
2338 let flex_base_size_is_definite = Cell::new(true);
2340 let main_content_sizes = LazyCell::new(|| {
2341 let flex_item = &self.independent_formatting_context;
2342 if let Some(transferred_size_suggestion) = *transferred_size_suggestion {
2348 return transferred_size_suggestion.into();
2349 }
2350
2351 flex_base_size_is_definite.set(false);
2352
2353 if cross_axis_is_item_block_axis {
2362 let constraint_space =
2365 ConstraintSpace::new(cross_size, style, preferred_aspect_ratio);
2366 let content_sizes = flex_item
2367 .inline_content_sizes(layout_context, &constraint_space)
2368 .sizes;
2369 if let Some(ratio) = preferred_aspect_ratio {
2370 let transferred_min = ratio.compute_dependent_size(main_axis, min_cross_size);
2371 let transferred_max =
2372 max_cross_size.map(|v| ratio.compute_dependent_size(main_axis, v));
2373 content_sizes
2374 .map(|size| size.clamp_between_extremums(transferred_min, transferred_max))
2375 } else {
2376 content_sizes
2377 }
2378 } else {
2379 self.layout_for_block_content_size(
2380 flex_context_getter(),
2381 &pbm_auto_is_zero,
2382 content_box_sizes,
2383 preferred_aspect_ratio,
2384 automatic_cross_size_for_intrinsic_sizing,
2385 IntrinsicSizingMode::Size,
2386 )
2387 .into()
2388 }
2389 });
2390
2391 let flex_base_size = self
2392 .flex_basis(
2393 containing_block_size.main,
2394 content_main_sizes.preferred,
2395 padding_border.main,
2396 )
2397 .resolve_for_preferred(Size::MaxContent, stretch_size.main, &main_content_sizes);
2398 let flex_base_size_is_definite = flex_base_size_is_definite.take();
2399
2400 let content_max_main_size = content_main_sizes
2401 .max
2402 .resolve_for_max(stretch_size.main, &main_content_sizes);
2403
2404 let get_automatic_minimum_size = || {
2405 if style.establishes_scroll_container(self.base_fragment_info().flags) {
2407 return Au::zero();
2408 }
2409
2410 let specified_size_suggestion = content_main_sizes
2414 .preferred
2415 .maybe_resolve_extrinsic(stretch_size.main);
2416
2417 let is_replaced = self.independent_formatting_context.is_replaced();
2418
2419 let content_size_suggestion = match preferred_aspect_ratio {
2424 Some(ratio) => main_content_sizes.min_content.clamp_between_extremums(
2425 ratio.compute_dependent_size(main_axis, min_cross_size),
2426 max_cross_size.map(|l| ratio.compute_dependent_size(main_axis, l)),
2427 ),
2428 None => main_content_sizes.min_content,
2429 };
2430
2431 match (specified_size_suggestion, *transferred_size_suggestion) {
2438 (Some(specified), _) => specified.min(content_size_suggestion),
2439 (_, Some(transferred)) if is_replaced => transferred.min(content_size_suggestion),
2440 _ => content_size_suggestion,
2441 }
2442 .clamp_below_max(content_max_main_size)
2443 };
2444 let content_min_main_size = content_main_sizes.min.resolve_for_min(
2445 get_automatic_minimum_size,
2446 stretch_size.main,
2447 &main_content_sizes,
2448 is_table,
2449 );
2450
2451 FlexItem {
2452 box_: self,
2453 content_cross_sizes: content_cross_sizes.clone(),
2454 padding,
2455 border,
2456 margin: config.sides_to_flex_relative(pbm.margin),
2457 pbm_auto_is_zero,
2458 flex_base_size,
2459 flex_base_size_is_definite,
2460 hypothetical_main_size: flex_base_size
2461 .clamp_between_extremums(content_min_main_size, content_max_main_size),
2462 content_min_main_size,
2463 content_max_main_size,
2464 align_self,
2465 depends_on_block_constraints: *depends_on_block_constraints,
2466 preferred_aspect_ratio,
2467 automatic_cross_size,
2468 automatic_cross_size_for_intrinsic_sizing,
2469 }
2470 }
2471
2472 fn main_content_size_info<'a>(
2473 &self,
2474 layout_context: &LayoutContext,
2475 containing_block: &IndefiniteContainingBlock,
2476 config: &FlexContainerConfig,
2477 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2478 ) -> FlexItemBoxInlineContentSizesInfo {
2479 let content_box_sizes_and_pbm = self
2480 .independent_formatting_context
2481 .layout_style()
2482 .content_box_sizes_and_padding_border_margin(containing_block);
2483
2484 let FlexItem {
2487 flex_base_size,
2488 content_min_main_size,
2489 content_max_main_size,
2490 pbm_auto_is_zero,
2491 preferred_aspect_ratio,
2492 automatic_cross_size_for_intrinsic_sizing,
2493 ..
2494 } = self.to_flex_item(
2495 layout_context,
2496 containing_block,
2497 &content_box_sizes_and_pbm,
2498 config,
2499 flex_context_getter,
2500 );
2501
2502 let (content_contribution_sizes, depends_on_block_constraints) = match config.flex_axis {
2505 FlexAxis::Row => {
2506 let auto_minimum = LogicalVec2 {
2507 inline: content_min_main_size,
2508 block: Au::zero(),
2509 };
2510 let InlineContentSizesResult {
2511 sizes,
2512 depends_on_block_constraints,
2513 } = self
2514 .independent_formatting_context
2515 .outer_inline_content_sizes(
2516 layout_context,
2517 containing_block,
2518 &auto_minimum,
2519 automatic_cross_size_for_intrinsic_sizing == Size::Stretch,
2520 );
2521 (sizes, depends_on_block_constraints)
2522 },
2523 FlexAxis::Column => {
2524 let size = self.layout_for_block_content_size(
2525 flex_context_getter(),
2526 &pbm_auto_is_zero,
2527 &content_box_sizes_and_pbm.content_box_sizes,
2528 preferred_aspect_ratio,
2529 automatic_cross_size_for_intrinsic_sizing,
2530 IntrinsicSizingMode::Contribution,
2531 );
2532 (size.into(), true)
2533 },
2534 };
2535
2536 let outer_flex_base_size = flex_base_size + pbm_auto_is_zero.main;
2537 let outer_min_main_size = content_min_main_size + pbm_auto_is_zero.main;
2538 let outer_max_main_size = content_max_main_size.map(|v| v + pbm_auto_is_zero.main);
2539 let max_flex_factors = self.desired_flex_factors_for_preferred_width(
2540 content_contribution_sizes.max_content,
2541 flex_base_size,
2542 outer_flex_base_size,
2543 );
2544
2545 let min_flex_factors = self.desired_flex_factors_for_preferred_width(
2549 content_contribution_sizes.min_content,
2550 flex_base_size,
2551 outer_flex_base_size,
2552 );
2553
2554 let mut min_content_main_size_for_multiline_container =
2561 content_contribution_sizes.min_content;
2562 let style_position = &self.style().get_position();
2563 if style_position.flex_grow.is_zero() {
2564 min_content_main_size_for_multiline_container.min_assign(outer_flex_base_size);
2565 }
2566 if style_position.flex_shrink.is_zero() {
2567 min_content_main_size_for_multiline_container.max_assign(outer_flex_base_size);
2568 }
2569 min_content_main_size_for_multiline_container =
2570 min_content_main_size_for_multiline_container
2571 .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
2572
2573 FlexItemBoxInlineContentSizesInfo {
2574 outer_flex_base_size,
2575 outer_min_main_size,
2576 outer_max_main_size,
2577 min_flex_factors,
2578 max_flex_factors,
2579 min_content_main_size_for_multiline_container,
2580 depends_on_block_constraints,
2581 }
2582 }
2583
2584 fn desired_flex_factors_for_preferred_width(
2585 &self,
2586 preferred_width: Au,
2587 flex_base_size: Au,
2588 outer_flex_base_size: Au,
2589 ) -> DesiredFlexFractionAndGrowOrShrinkFactor {
2590 let difference = (preferred_width - outer_flex_base_size).to_f32_px();
2591 let (flex_grow_or_scaled_flex_shrink_factor, desired_flex_fraction) = if difference > 0.0 {
2592 let flex_grow_factor = self.style().get_position().flex_grow.0;
2596
2597 (
2598 flex_grow_factor,
2599 if flex_grow_factor >= 1.0 {
2600 difference / flex_grow_factor
2601 } else {
2602 difference * flex_grow_factor
2603 },
2604 )
2605 } else if difference < 0.0 {
2606 let flex_shrink_factor = self.style().get_position().flex_shrink.0;
2610 let scaled_flex_shrink_factor = flex_shrink_factor * flex_base_size.to_f32_px();
2611
2612 (
2613 scaled_flex_shrink_factor,
2614 if scaled_flex_shrink_factor != 0.0 {
2615 difference / scaled_flex_shrink_factor
2616 } else {
2617 f32::NEG_INFINITY
2618 },
2619 )
2620 } else {
2621 (0.0, 0.0)
2622 };
2623
2624 DesiredFlexFractionAndGrowOrShrinkFactor {
2625 desired_flex_fraction,
2626 flex_grow_or_shrink_factor: flex_grow_or_scaled_flex_shrink_factor,
2627 }
2628 }
2629
2630 fn flex_basis(
2636 &self,
2637 container_definite_main_size: Option<Au>,
2638 main_preferred_size: Size<Au>,
2639 main_padding_border_sum: Au,
2640 ) -> Size<Au> {
2641 let style_position = &self.independent_formatting_context.style().get_position();
2642 match &style_position.flex_basis {
2643 FlexBasis::Content => Size::Initial,
2646
2647 FlexBasis::Size(size) => match Size::<LengthPercentage>::from(size) {
2648 Size::Initial => main_preferred_size,
2652
2653 size => {
2663 let apply_box_sizing = |length: Au| {
2664 match style_position.box_sizing {
2665 BoxSizing::ContentBox => length,
2666 BoxSizing::BorderBox => length - main_padding_border_sum,
2669 }
2670 };
2671 size.resolve_percentages_for_preferred(container_definite_main_size)
2672 .map(apply_box_sizing)
2673 },
2674 },
2675 }
2676 }
2677
2678 #[servo_tracing::instrument(name = "FlexContainer::layout_for_block_content_size", skip_all)]
2679 fn layout_for_block_content_size(
2680 &self,
2681 flex_context: &FlexContext,
2682 pbm_auto_is_zero: &FlexRelativeVec2<Au>,
2683 content_box_sizes: &LogicalVec2<Sizes>,
2684 preferred_aspect_ratio: Option<AspectRatio>,
2685 automatic_inline_size: Size<Au>,
2686 intrinsic_sizing_mode: IntrinsicSizingMode,
2687 ) -> Au {
2688 let content_block_size = || {
2689 let mut positioning_context = PositioningContext::default();
2690 let style = self.independent_formatting_context.style();
2691
2692 let tentative_block_size = SizeConstraint::default();
2696
2697 let inline_size = {
2700 let stretch_size =
2701 flex_context.containing_block.size.inline - pbm_auto_is_zero.cross;
2702 let get_content_size = || {
2703 self.inline_content_sizes(
2704 flex_context,
2705 tentative_block_size,
2706 preferred_aspect_ratio,
2707 )
2708 };
2709 content_box_sizes.inline.resolve(
2710 Direction::Inline,
2711 automatic_inline_size,
2712 Au::zero,
2713 Some(stretch_size),
2714 get_content_size,
2715 false,
2716 )
2717 };
2718 let item_as_containing_block = ContainingBlock {
2719 size: ContainingBlockSize {
2720 inline: inline_size,
2721 block: tentative_block_size,
2722 },
2723 style,
2724 };
2725 self.independent_formatting_context
2726 .layout(
2727 flex_context.layout_context,
2728 &mut positioning_context,
2729 &item_as_containing_block,
2730 flex_context.containing_block,
2731 preferred_aspect_ratio,
2732 &LazySize::intrinsic(),
2733 )
2734 .content_block_size
2735 };
2736 match intrinsic_sizing_mode {
2737 IntrinsicSizingMode::Contribution => {
2738 let stretch_size = flex_context
2739 .containing_block
2740 .size
2741 .block
2742 .to_definite()
2743 .map(|block_size| block_size - pbm_auto_is_zero.main);
2744 let inner_block_size = content_box_sizes.block.resolve(
2745 Direction::Block,
2746 Size::FitContent,
2747 Au::zero,
2748 stretch_size,
2749 || ContentSizes::from(content_block_size()),
2750 false, );
2758 inner_block_size + pbm_auto_is_zero.main
2759 },
2760 IntrinsicSizingMode::Size => content_block_size(),
2761 }
2762 }
2763
2764 fn inline_content_sizes(
2765 &self,
2766 flex_context: &FlexContext,
2767 block_size: SizeConstraint,
2768 preferred_aspect_ratio: Option<AspectRatio>,
2769 ) -> ContentSizes {
2770 let style = self.independent_formatting_context.style();
2771 let constraint_space = ConstraintSpace::new(block_size, style, preferred_aspect_ratio);
2772 self.independent_formatting_context
2773 .inline_content_sizes(flex_context.layout_context, &constraint_space)
2774 .sizes
2775 }
2776}