Skip to main content

layout/flexbox/
mod.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use geom::{FlexAxis, MainStartCrossStart};
6use malloc_size_of_derive::MallocSizeOf;
7use script::layout_dom::ServoLayoutNode;
8use servo_arc::Arc as ServoArc;
9use style::context::SharedStyleContext;
10use style::logical_geometry::WritingMode;
11use style::properties::ComputedValues;
12use style::properties::longhands::align_items::computed_value::T as AlignItems;
13use style::properties::longhands::flex_direction::computed_value::T as FlexDirection;
14use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
15use style::values::computed::ContentDistribution;
16use style::values::specified::align::AlignFlags;
17
18use crate::PropagatedBoxTreeData;
19use crate::cell::ArcRefCell;
20use crate::construct_modern::{ModernContainerBuilder, ModernItemKind};
21use crate::context::LayoutContext;
22use crate::dom::{LayoutBox, WeakLayoutBox};
23use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
24use crate::formatting_contexts::IndependentFormattingContext;
25use crate::fragment_tree::BaseFragmentInfo;
26use crate::layout_box_base::LayoutBoxBase;
27use crate::positioned::AbsolutelyPositionedBox;
28
29mod geom;
30mod layout;
31
32/// A structure to hold the configuration of a flex container for use during layout
33/// and preferred width calculation.
34#[derive(Clone, Debug, MallocSizeOf)]
35pub(crate) struct FlexContainerConfig {
36    container_is_single_line: bool,
37    writing_mode: WritingMode,
38    flex_axis: FlexAxis,
39    flex_direction: FlexDirection,
40    flex_direction_is_reversed: bool,
41    flex_wrap: FlexWrap,
42    flex_wrap_is_reversed: bool,
43    main_start_cross_start_sides_are: MainStartCrossStart,
44    align_content: ContentDistribution,
45    align_items: AlignItems,
46    justify_content: ContentDistribution,
47}
48
49impl FlexContainerConfig {
50    fn new(container_style: &ComputedValues) -> FlexContainerConfig {
51        let flex_direction = container_style.clone_flex_direction();
52        let flex_axis = FlexAxis::from(flex_direction);
53        let flex_wrap = container_style.get_position().flex_wrap;
54        let container_is_single_line = match flex_wrap {
55            FlexWrap::Nowrap => true,
56            FlexWrap::Wrap | FlexWrap::WrapReverse => false,
57        };
58        let flex_direction_is_reversed = match flex_direction {
59            FlexDirection::Row | FlexDirection::Column => false,
60            FlexDirection::RowReverse | FlexDirection::ColumnReverse => true,
61        };
62        let flex_wrap_reverse = match flex_wrap {
63            FlexWrap::Nowrap | FlexWrap::Wrap => false,
64            FlexWrap::WrapReverse => true,
65        };
66
67        let align_content = container_style.clone_align_content();
68        let align_items = AlignItems(match container_style.clone_align_items().0 {
69            AlignFlags::AUTO | AlignFlags::NORMAL => AlignFlags::STRETCH,
70            align => align,
71        });
72        let justify_content = container_style.clone_justify_content();
73        let main_start_cross_start_sides_are =
74            MainStartCrossStart::from(flex_direction, flex_wrap_reverse);
75
76        FlexContainerConfig {
77            container_is_single_line,
78            writing_mode: container_style.writing_mode,
79            flex_axis,
80            flex_direction,
81            flex_direction_is_reversed,
82            flex_wrap,
83            flex_wrap_is_reversed: flex_wrap_reverse,
84            main_start_cross_start_sides_are,
85            align_content,
86            align_items,
87            justify_content,
88        }
89    }
90}
91
92#[derive(Debug, MallocSizeOf)]
93pub(crate) struct FlexContainer {
94    children: Vec<ArcRefCell<FlexLevelBox>>,
95
96    style: ServoArc<ComputedValues>,
97
98    /// The configuration of this [`FlexContainer`].
99    config: FlexContainerConfig,
100}
101
102impl FlexContainer {
103    pub fn construct(
104        context: &LayoutContext,
105        info: &NodeAndStyleInfo<'_>,
106        contents: NonReplacedContents,
107        propagated_data: PropagatedBoxTreeData,
108    ) -> Self {
109        let mut builder = ModernContainerBuilder::new(context, info, propagated_data);
110        contents.traverse(context, info, &mut builder);
111        let items = builder.finish();
112
113        let children = items
114            .into_iter()
115            .map(|item| {
116                let flex_item_box = match item.kind {
117                    ModernItemKind::InFlow(independent_formatting_context) => ArcRefCell::new(
118                        FlexLevelBox::FlexItem(FlexItemBox::new(independent_formatting_context)),
119                    ),
120                    ModernItemKind::OutOfFlow(independent_formatting_context) => {
121                        let abs_pos_box = ArcRefCell::new(AbsolutelyPositionedBox::new(
122                            independent_formatting_context,
123                        ));
124                        ArcRefCell::new(FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(abs_pos_box))
125                    },
126                    ModernItemKind::ReusedBox(layout_box) => match layout_box {
127                        LayoutBox::FlexLevel(flex_level_box) => flex_level_box,
128                        _ => unreachable!(
129                            "Undamaged flex level element should be associated with flex level box"
130                        ),
131                    },
132                };
133
134                item.box_slot
135                    .set(LayoutBox::FlexLevel(flex_item_box.clone()));
136                flex_item_box
137            })
138            .collect();
139
140        Self {
141            children,
142            style: info.style.clone(),
143            config: FlexContainerConfig::new(&info.style),
144        }
145    }
146
147    pub(crate) fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
148        self.config = FlexContainerConfig::new(new_style);
149        self.style = new_style.clone();
150    }
151
152    pub(crate) fn subtree_size(&self) -> usize {
153        self.children
154            .iter()
155            .map(|child| child.borrow().with_base(|base| base.subtree_size()))
156            .sum()
157    }
158}
159
160#[expect(clippy::large_enum_variant)]
161#[derive(Debug, MallocSizeOf)]
162pub(crate) enum FlexLevelBox {
163    FlexItem(FlexItemBox),
164    OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
165}
166
167impl FlexLevelBox {
168    pub(crate) fn repair_style(
169        &mut self,
170        context: &SharedStyleContext,
171        node: &ServoLayoutNode,
172        new_style: &ServoArc<ComputedValues>,
173    ) {
174        match self {
175            FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
176                .independent_formatting_context
177                .repair_style(context, node, new_style),
178            FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
179                .borrow_mut()
180                .context
181                .repair_style(context, node, new_style),
182        }
183    }
184
185    pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
186        match self {
187            FlexLevelBox::FlexItem(flex_item_box) => {
188                callback(&flex_item_box.independent_formatting_context.base)
189            },
190            FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
191                callback(&positioned_box.borrow().context.base)
192            },
193        }
194    }
195
196    pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
197        match self {
198            FlexLevelBox::FlexItem(flex_item_box) => {
199                callback(&mut flex_item_box.independent_formatting_context.base)
200            },
201            FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
202                callback(&mut positioned_box.borrow_mut().context.base)
203            },
204        }
205    }
206
207    pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
208        match self {
209            Self::FlexItem(flex_item_box) => flex_item_box
210                .independent_formatting_context
211                .attached_to_tree(layout_box),
212            Self::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
213                .borrow_mut()
214                .context
215                .attached_to_tree(layout_box),
216        }
217    }
218}
219
220#[derive(MallocSizeOf)]
221pub(crate) struct FlexItemBox {
222    pub(crate) independent_formatting_context: IndependentFormattingContext,
223}
224
225impl std::fmt::Debug for FlexItemBox {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        f.write_str("FlexItemBox")
228    }
229}
230
231impl FlexItemBox {
232    fn new(independent_formatting_context: IndependentFormattingContext) -> Self {
233        Self {
234            independent_formatting_context,
235        }
236    }
237
238    pub(crate) fn style(&self) -> &ServoArc<ComputedValues> {
239        self.independent_formatting_context.style()
240    }
241
242    fn base_fragment_info(&self) -> BaseFragmentInfo {
243        self.independent_formatting_context.base_fragment_info()
244    }
245}