From 2db9032e72bda6108b4eb5988eaf164141a61d64 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Fri, 23 Aug 2024 11:11:22 -0700 Subject: [PATCH] layout: Add support for flex items with `position: relative` (#33151) Signed-off-by: Martin Robinson --- components/layout_2020/flexbox/layout.rs | 213 +++++++++++------- tests/wpt/meta/MANIFEST.json | 17 ++ .../flex-item-position-relative-001-ref.html | 16 ++ .../flex-item-position-relative-001.html | 42 ++++ 4 files changed, 207 insertions(+), 81 deletions(-) create mode 100644 tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001-ref.html create mode 100644 tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001.html diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index bddc5dab596..7708c64fc97 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -8,6 +8,7 @@ use std::cmp::Ordering; use app_units::Au; use atomic_refcell::AtomicRefMut; use itertools::izip; +use style::computed_values::position::T as Position; use style::logical_geometry::WritingMode; use style::properties::longhands::align_items::computed_value::T as AlignItems; use style::properties::longhands::align_self::computed_value::T as AlignSelf; @@ -29,7 +30,9 @@ use crate::context::LayoutContext; use crate::formatting_contexts::{Baselines, IndependentFormattingContext, IndependentLayout}; use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags}; use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2}; -use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; +use crate::positioned::{ + relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, +}; use crate::sizing::{ContentSizes, IntrinsicSizingMode}; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; use crate::ContainingBlock; @@ -101,6 +104,109 @@ impl FlexItemLayoutResult { self.baseline_relative_to_margin_box .unwrap_or_else(|| item.synthesized_baseline_relative_to_margin_box(block_size)) } + + fn collect_fragment( + mut self, + initial_flex_layout: &InitialFlexLineLayout, + item: &FlexItem, + item_used_size: FlexRelativeVec2, + item_margin: FlexRelativeSides, + item_main_interval: Au, + final_line_cross_size: Au, + shared_alignment_baseline: &Option, + flex_context: &mut FlexContext, + all_baselines: &mut Baselines, + main_position_cursor: &mut Au, + ) -> (BoxFragment, PositioningContext) { + // https://drafts.csswg.org/css-flexbox/#algo-main-align + // “Align the items along the main-axis” + *main_position_cursor += + item_margin.main_start + item.border.main_start + item.padding.main_start; + let item_content_main_start_position = *main_position_cursor; + + *main_position_cursor += item_used_size.main + + item.padding.main_end + + item.border.main_end + + item_margin.main_end + + item_main_interval; + + // https://drafts.csswg.org/css-flexbox/#algo-cross-align + let item_content_cross_start_position = item.align_along_cross_axis( + &item_margin, + &item_used_size.cross, + final_line_cross_size, + self.baseline_relative_to_margin_box.unwrap_or_default(), + shared_alignment_baseline.unwrap_or_default(), + flex_context.config.flex_wrap_reverse, + ); + + let start_corner = FlexRelativeVec2 { + main: item_content_main_start_position, + cross: item_content_cross_start_position, + }; + + // Need to collect both baselines from baseline participation and other baselines. + let final_line_size = FlexRelativeVec2 { + main: initial_flex_layout.line_size.main, + cross: final_line_cross_size, + }; + let content_rect = flex_context.rect_to_flow_relative( + final_line_size, + FlexRelativeRect { + start_corner, + size: item_used_size, + }, + ); + let margin = flex_context.sides_to_flow_relative(item_margin); + let collapsed_margin = CollapsedBlockMargins::from_margin(&margin); + + if let Some(item_baseline) = self.baseline_relative_to_margin_box.as_ref() { + let item_baseline = *item_baseline + item_content_cross_start_position - + item.border.cross_start - + item.padding.cross_start - + item_margin.cross_start; + all_baselines.first.get_or_insert(item_baseline); + all_baselines.last = Some(item_baseline); + } + + let mut fragment_info = item.box_.base_fragment_info(); + fragment_info.flags.insert(FragmentFlags::IS_FLEX_ITEM); + let flags = fragment_info.flags; + + let containing_block = flex_context.containing_block; + let container_writing_mode = containing_block.effective_writing_mode(); + let style = item.box_.style(); + let mut fragment = BoxFragment::new( + fragment_info, + style.clone(), + self.fragments, + content_rect.to_physical(container_writing_mode), + flex_context + .sides_to_flow_relative(item.padding) + .to_physical(container_writing_mode), + flex_context + .sides_to_flow_relative(item.border) + .to_physical(container_writing_mode), + margin.to_physical(container_writing_mode), + None, /* clearance */ + collapsed_margin, + ); + + // If this flex item establishes a containing block for absolutely-positioned + // descendants, then lay out any relevant absolutely-positioned children. This + // will remove those children from `self.positioning_context`. + if style.establishes_containing_block_for_absolute_descendants(flags) { + self.positioning_context + .layout_collected_children(flex_context.layout_context, &mut fragment); + } + + if style.clone_position() == Position::Relative { + fragment.content_rect.origin += relative_adjustement(style, containing_block) + .to_physical_size(containing_block.effective_writing_mode()) + } + + (fragment, self.positioning_context) + } } struct InitialFlexLineLayout<'a> { @@ -1408,91 +1514,33 @@ impl InitialFlexLineLayout<'_> { let mut all_baselines = Baselines::default(); let mut main_position_cursor = main_start_position; + + let items = std::mem::take(&mut self.items); + let item_layout_results = std::mem::take(&mut self.item_layout_results); let item_fragments = izip!( - self.items.iter(), + items, item_margins, self.item_used_main_sizes.iter(), item_used_cross_sizes.iter(), - self.item_layout_results.into_iter() + item_layout_results.into_iter(), ) .map( |(item, item_margin, item_used_main_size, item_used_cross_size, item_layout_result)| { - // https://drafts.csswg.org/css-flexbox/#algo-main-align - // “Align the items along the main-axis” - main_position_cursor += - item_margin.main_start + item.border.main_start + item.padding.main_start; - let item_content_main_start_position = main_position_cursor; - main_position_cursor += *item_used_main_size + - item.padding.main_end + - item.border.main_end + - item_margin.main_end + - item_main_interval; - - // https://drafts.csswg.org/css-flexbox/#algo-cross-align - let item_content_cross_start_position = item.align_along_cross_axis( - &item_margin, - item_used_cross_size, - final_line_cross_size, - item_layout_result - .baseline_relative_to_margin_box - .unwrap_or_default(), - shared_alignment_baseline.unwrap_or_default(), - flex_context.config.flex_wrap_reverse, - ); - - let start_corner = FlexRelativeVec2 { - main: item_content_main_start_position, - cross: item_content_cross_start_position, - }; - let size = FlexRelativeVec2 { + let item_used_size = FlexRelativeVec2 { main: *item_used_main_size, cross: *item_used_cross_size, }; - - // Need to collect both baselines from baseline participation and other baselines. - let final_line_size = FlexRelativeVec2 { - main: self.line_size.main, - cross: final_line_cross_size, - }; - let content_rect = flex_context.rect_to_flow_relative( - final_line_size, - FlexRelativeRect { start_corner, size }, - ); - let margin = flex_context.sides_to_flow_relative(item_margin); - let collapsed_margin = CollapsedBlockMargins::from_margin(&margin); - - if let Some(item_baseline) = - item_layout_result.baseline_relative_to_margin_box.as_ref() - { - let item_baseline = *item_baseline + item_content_cross_start_position - - item.border.cross_start - - item.padding.cross_start - - item_margin.cross_start; - all_baselines.first.get_or_insert(item_baseline); - all_baselines.last = Some(item_baseline); - } - - let mut fragment_info = item.box_.base_fragment_info(); - fragment_info.flags.insert(FragmentFlags::IS_FLEX_ITEM); - - let container_writing_mode = flex_context.containing_block.effective_writing_mode(); - ( - BoxFragment::new( - fragment_info, - item.box_.style().clone(), - item_layout_result.fragments, - content_rect.to_physical(container_writing_mode), - flex_context - .sides_to_flow_relative(item.padding) - .to_physical(container_writing_mode), - flex_context - .sides_to_flow_relative(item.border) - .to_physical(container_writing_mode), - margin.to_physical(container_writing_mode), - None, /* clearance */ - collapsed_margin, - ), - item_layout_result.positioning_context, + item_layout_result.collect_fragment( + &self, + item, + item_used_size, + item_margin, + item_main_interval, + final_line_cross_size, + &shared_alignment_baseline, + flex_context, + &mut all_baselines, + &mut main_position_cursor, ) }, ) @@ -1519,11 +1567,14 @@ impl FlexItem<'_> { used_cross_size_override: Option, ) -> FlexItemLayoutResult { let containing_block = &flex_context.containing_block; - let mut positioning_context = PositioningContext::new_for_subtree( - flex_context - .positioning_context - .collects_for_nearest_positioned_ancestor(), - ); + let mut positioning_context = PositioningContext::new_for_style(self.box_.style()) + .unwrap_or_else(|| { + PositioningContext::new_for_subtree( + flex_context + .positioning_context + .collects_for_nearest_positioned_ancestor(), + ) + }); // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows let container_writing_mode = containing_block.effective_writing_mode(); diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index d0a44bfa839..3f1a6990028 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -167867,6 +167867,19 @@ {} ] ], + "flex-item-position-relative-001.html": [ + "7e6907e0e8d4801776d581474a29d5f5b5743b53", + [ + null, + [ + [ + "/css/css-flexbox/flex-item-position-relative-001-ref.html", + "==" + ] + ], + {} + ] + ], "flex-item-transferred-sizes-padding-border-sizing.html": [ "fc1a163e01ee1eaca8186619aee3e8d0e25c3155", [ @@ -400930,6 +400943,10 @@ "2b0f294c3441f1cf25a6678b9ce0748258582ea1", [] ], + "flex-item-position-relative-001-ref.html": [ + "01e078d983d1319199a9356e2da54278009a7418", + [] + ], "flex-lines": { "multi-line-wrap-reverse-column-reverse-ref.html": [ "38366a62f7e6b7df30b2da814cf178ebba5a83fa", diff --git a/tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001-ref.html b/tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001-ref.html new file mode 100644 index 00000000000..01e078d983d --- /dev/null +++ b/tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001-ref.html @@ -0,0 +1,16 @@ + +Flex items and `position: relative` + + + + + + +
+ +
diff --git a/tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001.html b/tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001.html new file mode 100644 index 00000000000..7e6907e0e8d --- /dev/null +++ b/tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001.html @@ -0,0 +1,42 @@ + +Flex items and `position: relative` + + + + + + + +
+ +
+
+
+