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 #[allow(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.clone_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.clone_column_gap(),
540 FlexAxis::Column => self.style.clone_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.clone_row_gap();
686 let column_gap = self.style.clone_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 items: Vec<_> = if flex_context
1235 .layout_context
1236 .should_parallelize_layout(items.iter().map(FlexItem::subtree_size))
1237 {
1238 items
1239 .into_par_iter()
1240 .zip(item_used_main_sizes.into_par_iter())
1241 .map(|(item, used_main_size)| {
1242 let layout_result = item.layout(used_main_size, flex_context, None);
1243 FlexLineItem {
1244 item,
1245 layout_result,
1246 used_main_size,
1247 }
1248 })
1249 .collect()
1250 } else {
1251 items
1252 .into_iter()
1253 .zip(item_used_main_sizes)
1254 .map(|(item, used_main_size)| {
1255 let layout_result = item.layout(used_main_size, flex_context, None);
1256 FlexLineItem {
1257 item,
1258 layout_result,
1259 used_main_size,
1260 }
1261 })
1262 .collect()
1263 };
1264
1265 let line_cross_size = Self::cross_size(&items, flex_context);
1267 let line_size = FlexRelativeVec2 {
1268 main: container_main_size,
1269 cross: line_cross_size,
1270 };
1271
1272 InitialFlexLineLayout {
1273 items,
1274 line_size,
1275 free_space_in_main_axis,
1276 }
1277 }
1278
1279 fn resolve_flexible_lengths<'items>(
1282 items: &'items [FlexItem<'items>],
1283 outer_hypothetical_main_sizes_sum: Au,
1284 container_main_size: Au,
1285 ) -> (Vec<Au>, Au) {
1286 struct FlexibleLengthResolutionItem<'items> {
1287 item: &'items FlexItem<'items>,
1288 frozen: Cell<bool>,
1289 target_main_size: Cell<Au>,
1290 flex_factor: f32,
1291 min_max_violation_kind: Cell<Ordering>,
1292 }
1293
1294 let grow = outer_hypothetical_main_sizes_sum < container_main_size;
1299
1300 let mut frozen_count = 0;
1301 let items: Vec<_> = items
1302 .iter()
1303 .map(|item| {
1304 let target_main_size = Cell::new(item.flex_base_size);
1307
1308 let flex_factor = if grow {
1315 item.box_.style().get_position().flex_grow.0
1316 } else {
1317 item.box_.style().get_position().flex_shrink.0
1318 };
1319
1320 let is_inflexible = flex_factor == 0. ||
1321 if grow {
1322 item.flex_base_size > item.hypothetical_main_size
1323 } else {
1324 item.flex_base_size < item.hypothetical_main_size
1325 };
1326
1327 let frozen = Cell::new(false);
1328 if is_inflexible {
1329 frozen_count += 1;
1330 frozen.set(true);
1331 target_main_size.set(item.hypothetical_main_size);
1332 }
1333
1334 FlexibleLengthResolutionItem {
1335 item,
1336 frozen,
1337 target_main_size,
1338 flex_factor,
1339 min_max_violation_kind: Cell::new(Ordering::Equal),
1341 }
1342 })
1343 .collect();
1344
1345 let unfrozen_items = || items.iter().filter(|item| !item.frozen.get());
1346 let main_sizes = |items: Vec<FlexibleLengthResolutionItem>| {
1347 items
1348 .into_iter()
1349 .map(|item| item.target_main_size.get())
1350 .collect()
1351 };
1352
1353 let free_space = |all_items_frozen| {
1358 let items_size = items
1359 .iter()
1360 .map(|item| {
1361 item.item.pbm_auto_is_zero.main +
1362 if all_items_frozen || item.frozen.get() {
1363 item.target_main_size.get()
1364 } else {
1365 item.item.flex_base_size
1366 }
1367 })
1368 .sum();
1369 container_main_size - items_size
1370 };
1371
1372 let initial_free_space = free_space(false);
1373 loop {
1374 let mut remaining_free_space = free_space(false);
1376 if frozen_count >= items.len() {
1379 return (main_sizes(items), remaining_free_space);
1380 }
1381
1382 let unfrozen_items_flex_factor_sum =
1388 unfrozen_items().map(|item| item.flex_factor).sum();
1389 if unfrozen_items_flex_factor_sum < 1. {
1390 let multiplied = initial_free_space.scale_by(unfrozen_items_flex_factor_sum);
1391 if multiplied.abs() < remaining_free_space.abs() {
1392 remaining_free_space = multiplied
1393 }
1394 }
1395
1396 if !remaining_free_space.is_zero() {
1402 if grow {
1408 for item in unfrozen_items() {
1409 let ratio = item.flex_factor / unfrozen_items_flex_factor_sum;
1410 item.target_main_size
1411 .set(item.item.flex_base_size + remaining_free_space.scale_by(ratio));
1412 }
1413 } else {
1422 let scaled_shrink_factor = |item: &FlexibleLengthResolutionItem| {
1424 item.item.flex_base_size.scale_by(item.flex_factor)
1425 };
1426 let scaled_shrink_factors_sum: Au =
1427 unfrozen_items().map(scaled_shrink_factor).sum();
1428 if scaled_shrink_factors_sum > Au::zero() {
1429 for item in unfrozen_items() {
1430 let ratio = scaled_shrink_factor(item).0 as f32 /
1431 scaled_shrink_factors_sum.0 as f32;
1432 item.target_main_size.set(
1433 item.item.flex_base_size -
1434 remaining_free_space.abs().scale_by(ratio),
1435 );
1436 }
1437 }
1438 }
1439 }
1440
1441 let mut total_violation = Au::zero();
1447 for item in unfrozen_items() {
1448 let unclamped = item.target_main_size.get();
1449 let clamped = unclamped.clamp_between_extremums(
1450 item.item.content_min_main_size,
1451 item.item.content_max_main_size,
1452 );
1453 item.target_main_size.set(clamped);
1454 item.min_max_violation_kind.set(clamped.cmp(&unclamped));
1457 total_violation += clamped - unclamped;
1458 }
1459
1460 match total_violation.cmp(&Au::zero()) {
1467 Ordering::Equal => {
1468 let remaining_free_space = free_space(true);
1471 return (main_sizes(items), remaining_free_space);
1472 },
1473 total_violation_kind => {
1474 for item in unfrozen_items() {
1475 if item.min_max_violation_kind.get() == total_violation_kind {
1476 item.frozen.set(true);
1477 frozen_count += 1;
1478 }
1479 }
1480 },
1481 }
1482 }
1483 }
1484
1485 fn cross_size<'items>(items: &'items [FlexLineItem<'items>], flex_context: &FlexContext) -> Au {
1487 if flex_context.config.container_is_single_line &&
1488 let SizeConstraint::Definite(size) =
1489 flex_context.container_inner_size_constraint.cross
1490 {
1491 return size;
1492 }
1493
1494 let mut max_ascent = Au::zero();
1495 let mut max_descent = Au::zero();
1496 let mut max_outer_hypothetical_cross_size = Au::zero();
1497 for item in items.iter() {
1498 if matches!(
1500 item.item.align_self.0.value(),
1501 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
1502 ) {
1503 let baseline = item.get_or_synthesize_baseline_with_cross_size(
1504 item.layout_result.hypothetical_cross_size,
1505 &flex_context.config,
1506 );
1507 let hypothetical_margin_box_cross_size =
1508 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross;
1509 max_ascent = max_ascent.max(baseline);
1510 max_descent = max_descent.max(hypothetical_margin_box_cross_size - baseline);
1511 } else {
1512 max_outer_hypothetical_cross_size = max_outer_hypothetical_cross_size.max(
1513 item.layout_result.hypothetical_cross_size + item.item.pbm_auto_is_zero.cross,
1514 );
1515 }
1516 }
1517
1518 let largest = max_outer_hypothetical_cross_size.max(max_ascent + max_descent);
1520 match flex_context.container_inner_size_constraint.cross {
1521 SizeConstraint::MinMax(min, max) if flex_context.config.container_is_single_line => {
1522 largest.clamp_between_extremums(min, max)
1523 },
1524 _ => largest,
1525 }
1526 }
1527
1528 fn finish_with_final_cross_size(
1529 mut self,
1530 flex_context: &mut FlexContext,
1531 main_gap: Au,
1532 final_line_cross_size: Au,
1533 ) -> FinalFlexLineLayout {
1534 let auto_margins_count = self
1542 .items
1543 .iter()
1544 .map(|item| {
1545 item.item.margin.main_start.is_auto() as u32 +
1546 item.item.margin.main_end.is_auto() as u32
1547 })
1548 .sum::<u32>();
1549 let (space_distributed_to_auto_main_margins, free_space_in_main_axis) =
1550 if self.free_space_in_main_axis > Au::zero() && auto_margins_count > 0 {
1551 (
1552 self.free_space_in_main_axis / auto_margins_count as i32,
1553 Au::zero(),
1554 )
1555 } else {
1556 (Au::zero(), self.free_space_in_main_axis)
1557 };
1558
1559 let item_count = self.items.len();
1562 let mut shared_alignment_baseline = None;
1563 let mut item_used_cross_sizes = Vec::with_capacity(item_count);
1564 let mut item_margins = Vec::with_capacity(item_count);
1565 for item in self.items.iter_mut() {
1566 let cross_axis = match flex_context.config.flex_axis {
1567 FlexAxis::Row => Direction::Block,
1568 FlexAxis::Column => Direction::Inline,
1569 };
1570 let layout = &mut item.layout_result;
1571 let get_content_size = || match cross_axis {
1572 Direction::Block => layout.content_block_size.into(),
1573 Direction::Inline => item
1574 .item
1575 .inline_content_sizes(flex_context, item.used_main_size),
1576 };
1577 let used_cross_size = item.item.content_cross_sizes.resolve(
1578 cross_axis,
1579 item.item.automatic_cross_size,
1580 Au::zero,
1581 Some(Au::zero().max(final_line_cross_size - item.item.pbm_auto_is_zero.cross)),
1582 get_content_size,
1583 item.item.box_.independent_formatting_context.is_table() &&
1588 cross_axis == Direction::Inline,
1589 );
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 #[allow(clippy::too_many_arguments)]
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::WrapReverse => true,
2096 };
2097 if flex_wrap_reverse {
2101 cross_start = self.margin.cross_start.auto_is(|| available);
2102 cross_end = self.margin.cross_end.auto_is(Au::zero);
2103 } else {
2104 cross_start = self.margin.cross_start.auto_is(Au::zero);
2105 cross_end = self.margin.cross_end.auto_is(|| available);
2106 }
2107 }
2108
2109 FlexRelativeSides {
2110 cross_start,
2111 cross_end,
2112 main_start,
2113 main_end,
2114 }
2115 }
2116
2117 fn align_along_cross_axis(
2119 &self,
2120 margin: &FlexRelativeSides<Au>,
2121 used_cross_size: &Au,
2122 line_cross_size: Au,
2123 propagated_baseline: Au,
2124 max_propagated_baseline: Au,
2125 wrap_reverse: bool,
2126 ) -> Au {
2127 let ending_alignment = line_cross_size - *used_cross_size - self.pbm_auto_is_zero.cross;
2128 let outer_cross_start =
2129 if self.margin.cross_start.is_auto() || self.margin.cross_end.is_auto() {
2130 Au::zero()
2131 } else {
2132 match self.align_self.0.value() {
2133 AlignFlags::STRETCH => Au::zero(),
2134 AlignFlags::CENTER => ending_alignment / 2,
2135 AlignFlags::BASELINE | AlignFlags::LAST_BASELINE => {
2136 max_propagated_baseline - propagated_baseline
2137 },
2138 AlignFlags::START => {
2139 if !wrap_reverse {
2140 Au::zero()
2141 } else {
2142 ending_alignment
2143 }
2144 },
2145 AlignFlags::END => {
2146 if !wrap_reverse {
2147 ending_alignment
2148 } else {
2149 Au::zero()
2150 }
2151 },
2152 _ => Au::zero(),
2153 }
2154 };
2155 outer_cross_start + margin.cross_start + self.border.cross_start + self.padding.cross_start
2156 }
2157
2158 #[inline]
2159 fn inline_content_sizes(&self, flex_context: &FlexContext, block_size: Au) -> ContentSizes {
2160 self.box_.inline_content_sizes(
2161 flex_context,
2162 SizeConstraint::Definite(block_size),
2163 self.preferred_aspect_ratio,
2164 )
2165 }
2166}
2167
2168impl FlexItemBox {
2169 fn to_flex_item<'a>(
2170 &self,
2171 layout_context: &LayoutContext,
2172 containing_block: &IndefiniteContainingBlock,
2173 content_box_sizes_and_pbm: &ContentBoxSizesAndPBM,
2174 config: &FlexContainerConfig,
2175 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2176 ) -> FlexItem<'_> {
2177 let flex_axis = config.flex_axis;
2178 let style = self.style();
2179 let cross_axis_is_item_block_axis = cross_axis_is_item_block_axis(
2180 containing_block.style.writing_mode.is_horizontal(),
2181 style.writing_mode.is_horizontal(),
2182 flex_axis,
2183 );
2184 let main_axis = if cross_axis_is_item_block_axis {
2185 Direction::Inline
2186 } else {
2187 Direction::Block
2188 };
2189 let align_self = AlignItems(config.resolve_align_self_for_child(style));
2190
2191 let ContentBoxSizesAndPBM {
2192 content_box_sizes,
2193 pbm,
2194 depends_on_block_constraints,
2195 preferred_size_computes_to_auto,
2196 } = content_box_sizes_and_pbm;
2197
2198 let preferred_aspect_ratio = self
2199 .independent_formatting_context
2200 .preferred_aspect_ratio(&pbm.padding_border_sums);
2201 let padding = config.sides_to_flex_relative(pbm.padding);
2202 let border = config.sides_to_flex_relative(pbm.border);
2203 let margin = config.sides_to_flex_relative(pbm.margin);
2204 let padding_border = padding.sum_by_axis() + border.sum_by_axis();
2205 let margin_auto_is_zero = config.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
2206 let pbm_auto_is_zero = FlexRelativeVec2 {
2207 main: padding_border.main,
2208 cross: padding_border.cross,
2209 } + margin_auto_is_zero.sum_by_axis();
2210 let (content_main_sizes, content_cross_sizes, cross_size_computes_to_auto) = match flex_axis
2211 {
2212 FlexAxis::Row => (
2213 &content_box_sizes.inline,
2214 &content_box_sizes.block,
2215 preferred_size_computes_to_auto.block,
2216 ),
2217 FlexAxis::Column => (
2218 &content_box_sizes.block,
2219 &content_box_sizes.inline,
2220 preferred_size_computes_to_auto.inline,
2221 ),
2222 };
2223 let automatic_cross_size = if cross_size_computes_to_auto &&
2224 item_with_auto_cross_size_stretches_to_line_size(align_self, &margin)
2225 {
2226 Size::Stretch
2227 } else {
2228 Size::FitContent
2229 };
2230 let automatic_cross_size_for_intrinsic_sizing = if config.container_is_single_line {
2231 automatic_cross_size
2232 } else {
2233 Size::FitContent
2234 };
2235 let containing_block_size = flex_axis.vec2_to_flex_relative(containing_block.size);
2236 let stretch_size = FlexRelativeVec2 {
2237 main: containing_block_size
2238 .main
2239 .map(|v| Au::zero().max(v - pbm_auto_is_zero.main)),
2240 cross: containing_block_size
2241 .cross
2242 .map(|v| Au::zero().max(v - pbm_auto_is_zero.cross)),
2243 };
2244
2245 let is_table = self.independent_formatting_context.is_table();
2246 let tentative_cross_content_size = if cross_axis_is_item_block_axis {
2247 self.independent_formatting_context
2248 .tentative_block_content_size(
2249 preferred_aspect_ratio,
2250 stretch_size.main.unwrap_or_default(),
2251 )
2252 } else {
2253 None
2254 };
2255 let (preferred_cross_size, min_cross_size, max_cross_size) =
2256 if let Some(cross_content_size) = tentative_cross_content_size {
2257 let (preferred, min, max) = content_cross_sizes.resolve_each(
2258 automatic_cross_size_for_intrinsic_sizing,
2259 Au::zero,
2260 stretch_size.cross,
2261 || cross_content_size,
2262 is_table,
2263 );
2264 (Some(preferred), min, max)
2265 } else {
2266 content_cross_sizes.resolve_each_extrinsic(
2267 automatic_cross_size_for_intrinsic_sizing,
2268 Au::zero(),
2269 stretch_size.cross,
2270 )
2271 };
2272 let cross_size = SizeConstraint::new(preferred_cross_size, min_cross_size, max_cross_size);
2273
2274 let transferred_size_suggestion =
2279 LazyCell::new(|| match (preferred_aspect_ratio, cross_size) {
2280 (Some(ratio), SizeConstraint::Definite(cross_size)) => {
2281 Some(ratio.compute_dependent_size(main_axis, cross_size))
2282 },
2283 _ => None,
2284 });
2285
2286 let flex_base_size_is_definite = Cell::new(true);
2288 let main_content_sizes = LazyCell::new(|| {
2289 let flex_item = &self.independent_formatting_context;
2290 if let Some(transferred_size_suggestion) = *transferred_size_suggestion {
2296 return transferred_size_suggestion.into();
2297 }
2298
2299 flex_base_size_is_definite.set(false);
2300
2301 if cross_axis_is_item_block_axis {
2310 let constraint_space =
2313 ConstraintSpace::new(cross_size, style, preferred_aspect_ratio);
2314 let content_sizes = flex_item
2315 .inline_content_sizes(layout_context, &constraint_space)
2316 .sizes;
2317 if let Some(ratio) = preferred_aspect_ratio {
2318 let transferred_min = ratio.compute_dependent_size(main_axis, min_cross_size);
2319 let transferred_max =
2320 max_cross_size.map(|v| ratio.compute_dependent_size(main_axis, v));
2321 content_sizes
2322 .map(|size| size.clamp_between_extremums(transferred_min, transferred_max))
2323 } else {
2324 content_sizes
2325 }
2326 } else {
2327 self.layout_for_block_content_size(
2328 flex_context_getter(),
2329 &pbm_auto_is_zero,
2330 content_box_sizes,
2331 preferred_aspect_ratio,
2332 automatic_cross_size_for_intrinsic_sizing,
2333 IntrinsicSizingMode::Size,
2334 )
2335 .into()
2336 }
2337 });
2338
2339 let flex_base_size = self
2340 .flex_basis(
2341 containing_block_size.main,
2342 content_main_sizes.preferred,
2343 padding_border.main,
2344 )
2345 .resolve_for_preferred(Size::MaxContent, stretch_size.main, &main_content_sizes);
2346 let flex_base_size_is_definite = flex_base_size_is_definite.take();
2347
2348 let content_max_main_size = content_main_sizes
2349 .max
2350 .resolve_for_max(stretch_size.main, &main_content_sizes);
2351
2352 let get_automatic_minimum_size = || {
2353 if style.establishes_scroll_container(self.base_fragment_info().flags) {
2355 return Au::zero();
2356 }
2357
2358 let specified_size_suggestion = content_main_sizes
2362 .preferred
2363 .maybe_resolve_extrinsic(stretch_size.main);
2364
2365 let is_replaced = self.independent_formatting_context.is_replaced();
2366
2367 let content_size_suggestion = match preferred_aspect_ratio {
2372 Some(ratio) => main_content_sizes.min_content.clamp_between_extremums(
2373 ratio.compute_dependent_size(main_axis, min_cross_size),
2374 max_cross_size.map(|l| ratio.compute_dependent_size(main_axis, l)),
2375 ),
2376 None => main_content_sizes.min_content,
2377 };
2378
2379 match (specified_size_suggestion, *transferred_size_suggestion) {
2386 (Some(specified), _) => specified.min(content_size_suggestion),
2387 (_, Some(transferred)) if is_replaced => transferred.min(content_size_suggestion),
2388 _ => content_size_suggestion,
2389 }
2390 .clamp_below_max(content_max_main_size)
2391 };
2392 let content_min_main_size = content_main_sizes.min.resolve_for_min(
2393 get_automatic_minimum_size,
2394 stretch_size.main,
2395 &main_content_sizes,
2396 is_table,
2397 );
2398
2399 FlexItem {
2400 box_: self,
2401 content_cross_sizes: content_cross_sizes.clone(),
2402 padding,
2403 border,
2404 margin: config.sides_to_flex_relative(pbm.margin),
2405 pbm_auto_is_zero,
2406 flex_base_size,
2407 flex_base_size_is_definite,
2408 hypothetical_main_size: flex_base_size
2409 .clamp_between_extremums(content_min_main_size, content_max_main_size),
2410 content_min_main_size,
2411 content_max_main_size,
2412 align_self,
2413 depends_on_block_constraints: *depends_on_block_constraints,
2414 preferred_aspect_ratio,
2415 automatic_cross_size,
2416 automatic_cross_size_for_intrinsic_sizing,
2417 }
2418 }
2419
2420 fn main_content_size_info<'a>(
2421 &self,
2422 layout_context: &LayoutContext,
2423 containing_block: &IndefiniteContainingBlock,
2424 config: &FlexContainerConfig,
2425 flex_context_getter: &impl Fn() -> &'a FlexContext<'a>,
2426 ) -> FlexItemBoxInlineContentSizesInfo {
2427 let content_box_sizes_and_pbm = self
2428 .independent_formatting_context
2429 .layout_style()
2430 .content_box_sizes_and_padding_border_margin(containing_block);
2431
2432 let FlexItem {
2435 flex_base_size,
2436 content_min_main_size,
2437 content_max_main_size,
2438 pbm_auto_is_zero,
2439 preferred_aspect_ratio,
2440 automatic_cross_size_for_intrinsic_sizing,
2441 ..
2442 } = self.to_flex_item(
2443 layout_context,
2444 containing_block,
2445 &content_box_sizes_and_pbm,
2446 config,
2447 flex_context_getter,
2448 );
2449
2450 let (content_contribution_sizes, depends_on_block_constraints) = match config.flex_axis {
2453 FlexAxis::Row => {
2454 let auto_minimum = LogicalVec2 {
2455 inline: content_min_main_size,
2456 block: Au::zero(),
2457 };
2458 let InlineContentSizesResult {
2459 sizes,
2460 depends_on_block_constraints,
2461 } = self
2462 .independent_formatting_context
2463 .outer_inline_content_sizes(
2464 layout_context,
2465 containing_block,
2466 &auto_minimum,
2467 automatic_cross_size_for_intrinsic_sizing == Size::Stretch,
2468 );
2469 (sizes, depends_on_block_constraints)
2470 },
2471 FlexAxis::Column => {
2472 let size = self.layout_for_block_content_size(
2473 flex_context_getter(),
2474 &pbm_auto_is_zero,
2475 &content_box_sizes_and_pbm.content_box_sizes,
2476 preferred_aspect_ratio,
2477 automatic_cross_size_for_intrinsic_sizing,
2478 IntrinsicSizingMode::Contribution,
2479 );
2480 (size.into(), true)
2481 },
2482 };
2483
2484 let outer_flex_base_size = flex_base_size + pbm_auto_is_zero.main;
2485 let outer_min_main_size = content_min_main_size + pbm_auto_is_zero.main;
2486 let outer_max_main_size = content_max_main_size.map(|v| v + pbm_auto_is_zero.main);
2487 let max_flex_factors = self.desired_flex_factors_for_preferred_width(
2488 content_contribution_sizes.max_content,
2489 flex_base_size,
2490 outer_flex_base_size,
2491 );
2492
2493 let min_flex_factors = self.desired_flex_factors_for_preferred_width(
2497 content_contribution_sizes.min_content,
2498 flex_base_size,
2499 outer_flex_base_size,
2500 );
2501
2502 let mut min_content_main_size_for_multiline_container =
2509 content_contribution_sizes.min_content;
2510 let style_position = &self.style().get_position();
2511 if style_position.flex_grow.is_zero() {
2512 min_content_main_size_for_multiline_container.min_assign(outer_flex_base_size);
2513 }
2514 if style_position.flex_shrink.is_zero() {
2515 min_content_main_size_for_multiline_container.max_assign(outer_flex_base_size);
2516 }
2517 min_content_main_size_for_multiline_container =
2518 min_content_main_size_for_multiline_container
2519 .clamp_between_extremums(outer_min_main_size, outer_max_main_size);
2520
2521 FlexItemBoxInlineContentSizesInfo {
2522 outer_flex_base_size,
2523 outer_min_main_size,
2524 outer_max_main_size,
2525 min_flex_factors,
2526 max_flex_factors,
2527 min_content_main_size_for_multiline_container,
2528 depends_on_block_constraints,
2529 }
2530 }
2531
2532 fn desired_flex_factors_for_preferred_width(
2533 &self,
2534 preferred_width: Au,
2535 flex_base_size: Au,
2536 outer_flex_base_size: Au,
2537 ) -> DesiredFlexFractionAndGrowOrShrinkFactor {
2538 let difference = (preferred_width - outer_flex_base_size).to_f32_px();
2539 let (flex_grow_or_scaled_flex_shrink_factor, desired_flex_fraction) = if difference > 0.0 {
2540 let flex_grow_factor = self.style().get_position().flex_grow.0;
2544
2545 (
2546 flex_grow_factor,
2547 if flex_grow_factor >= 1.0 {
2548 difference / flex_grow_factor
2549 } else {
2550 difference * flex_grow_factor
2551 },
2552 )
2553 } else if difference < 0.0 {
2554 let flex_shrink_factor = self.style().get_position().flex_shrink.0;
2558 let scaled_flex_shrink_factor = flex_shrink_factor * flex_base_size.to_f32_px();
2559
2560 (
2561 scaled_flex_shrink_factor,
2562 if scaled_flex_shrink_factor != 0.0 {
2563 difference / scaled_flex_shrink_factor
2564 } else {
2565 f32::NEG_INFINITY
2566 },
2567 )
2568 } else {
2569 (0.0, 0.0)
2570 };
2571
2572 DesiredFlexFractionAndGrowOrShrinkFactor {
2573 desired_flex_fraction,
2574 flex_grow_or_shrink_factor: flex_grow_or_scaled_flex_shrink_factor,
2575 }
2576 }
2577
2578 fn flex_basis(
2584 &self,
2585 container_definite_main_size: Option<Au>,
2586 main_preferred_size: Size<Au>,
2587 main_padding_border_sum: Au,
2588 ) -> Size<Au> {
2589 let style_position = &self.independent_formatting_context.style().get_position();
2590 match &style_position.flex_basis {
2591 FlexBasis::Content => Size::Initial,
2594
2595 FlexBasis::Size(size) => match Size::<LengthPercentage>::from(size.clone()) {
2596 Size::Initial => main_preferred_size,
2600
2601 size => {
2611 let apply_box_sizing = |length: Au| {
2612 match style_position.box_sizing {
2613 BoxSizing::ContentBox => length,
2614 BoxSizing::BorderBox => length - main_padding_border_sum,
2617 }
2618 };
2619 size.resolve_percentages_for_preferred(container_definite_main_size)
2620 .map(apply_box_sizing)
2621 },
2622 },
2623 }
2624 }
2625
2626 #[allow(clippy::too_many_arguments)]
2627 #[servo_tracing::instrument(name = "FlexContainer::layout_for_block_content_size", skip_all)]
2628 fn layout_for_block_content_size(
2629 &self,
2630 flex_context: &FlexContext,
2631 pbm_auto_is_zero: &FlexRelativeVec2<Au>,
2632 content_box_sizes: &LogicalVec2<Sizes>,
2633 preferred_aspect_ratio: Option<AspectRatio>,
2634 automatic_inline_size: Size<Au>,
2635 intrinsic_sizing_mode: IntrinsicSizingMode,
2636 ) -> Au {
2637 let content_block_size = || {
2638 let mut positioning_context = PositioningContext::default();
2639 let style = self.independent_formatting_context.style();
2640
2641 let tentative_block_size = SizeConstraint::default();
2645
2646 let inline_size = {
2649 let stretch_size =
2650 flex_context.containing_block.size.inline - pbm_auto_is_zero.cross;
2651 let get_content_size = || {
2652 self.inline_content_sizes(
2653 flex_context,
2654 tentative_block_size,
2655 preferred_aspect_ratio,
2656 )
2657 };
2658 content_box_sizes.inline.resolve(
2659 Direction::Inline,
2660 automatic_inline_size,
2661 Au::zero,
2662 Some(stretch_size),
2663 get_content_size,
2664 false,
2665 )
2666 };
2667 let item_as_containing_block = ContainingBlock {
2668 size: ContainingBlockSize {
2669 inline: inline_size,
2670 block: tentative_block_size,
2671 },
2672 style,
2673 };
2674 self.independent_formatting_context
2675 .layout(
2676 flex_context.layout_context,
2677 &mut positioning_context,
2678 &item_as_containing_block,
2679 flex_context.containing_block,
2680 preferred_aspect_ratio,
2681 &LazySize::intrinsic(),
2682 )
2683 .content_block_size
2684 };
2685 match intrinsic_sizing_mode {
2686 IntrinsicSizingMode::Contribution => {
2687 let stretch_size = flex_context
2688 .containing_block
2689 .size
2690 .block
2691 .to_definite()
2692 .map(|block_size| block_size - pbm_auto_is_zero.main);
2693 let inner_block_size = content_box_sizes.block.resolve(
2694 Direction::Block,
2695 Size::FitContent,
2696 Au::zero,
2697 stretch_size,
2698 || ContentSizes::from(content_block_size()),
2699 false, );
2707 inner_block_size + pbm_auto_is_zero.main
2708 },
2709 IntrinsicSizingMode::Size => content_block_size(),
2710 }
2711 }
2712
2713 fn inline_content_sizes(
2714 &self,
2715 flex_context: &FlexContext,
2716 block_size: SizeConstraint,
2717 preferred_aspect_ratio: Option<AspectRatio>,
2718 ) -> ContentSizes {
2719 let style = self.independent_formatting_context.style();
2720 let constraint_space = ConstraintSpace::new(block_size, style, preferred_aspect_ratio);
2721 self.independent_formatting_context
2722 .inline_content_sizes(flex_context.layout_context, &constraint_space)
2723 .sizes
2724 }
2725}