1use 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#[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.slow_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::WRAP_REVERSE => false,
57 _ => unreachable!("FlexWrap::BALANCE should be disabled"),
58 };
59 let flex_direction_is_reversed = match flex_direction {
60 FlexDirection::Row | FlexDirection::Column => false,
61 FlexDirection::RowReverse | FlexDirection::ColumnReverse => true,
62 };
63 let flex_wrap_reverse = match flex_wrap {
64 FlexWrap::NOWRAP | FlexWrap::WRAP => false,
65 FlexWrap::WRAP_REVERSE => true,
66 _ => unreachable!("FlexWrap::BALANCE should be disabled"),
67 };
68
69 let align_content = container_style.slow_clone_align_content();
70 let align_items = AlignItems(match container_style.get_align_items().0 {
71 AlignFlags::AUTO | AlignFlags::NORMAL => AlignFlags::STRETCH,
72 align => align,
73 });
74 let justify_content = container_style.slow_clone_justify_content();
75 let main_start_cross_start_sides_are =
76 MainStartCrossStart::from(flex_direction, flex_wrap_reverse);
77
78 FlexContainerConfig {
79 container_is_single_line,
80 writing_mode: container_style.writing_mode,
81 flex_axis,
82 flex_direction,
83 flex_direction_is_reversed,
84 flex_wrap,
85 flex_wrap_is_reversed: flex_wrap_reverse,
86 main_start_cross_start_sides_are,
87 align_content,
88 align_items,
89 justify_content,
90 }
91 }
92}
93
94#[derive(Debug, MallocSizeOf)]
95pub(crate) struct FlexContainer {
96 children: Vec<ArcRefCell<FlexLevelBox>>,
97
98 style: ServoArc<ComputedValues>,
99
100 config: FlexContainerConfig,
102}
103
104impl FlexContainer {
105 pub fn construct(
106 context: &LayoutContext,
107 info: &NodeAndStyleInfo<'_>,
108 contents: NonReplacedContents,
109 propagated_data: PropagatedBoxTreeData,
110 ) -> Self {
111 let mut builder = ModernContainerBuilder::new(context, info, propagated_data);
112 contents.traverse(context, info, &mut builder);
113 let items = builder.finish();
114
115 let children = items
116 .into_iter()
117 .map(|item| {
118 let flex_item_box = match item.kind {
119 ModernItemKind::InFlow(independent_formatting_context) => ArcRefCell::new(
120 FlexLevelBox::FlexItem(FlexItemBox::new(independent_formatting_context)),
121 ),
122 ModernItemKind::OutOfFlow(independent_formatting_context) => {
123 let abs_pos_box = ArcRefCell::new(AbsolutelyPositionedBox::new(
124 independent_formatting_context,
125 ));
126 ArcRefCell::new(FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(abs_pos_box))
127 },
128 ModernItemKind::ReusedBox(layout_box) => match layout_box {
129 LayoutBox::FlexLevel(flex_level_box) => flex_level_box,
130 _ => unreachable!(
131 "Undamaged flex level element should be associated with flex level box"
132 ),
133 },
134 };
135
136 item.box_slot
137 .set(LayoutBox::FlexLevel(flex_item_box.clone()));
138 flex_item_box
139 })
140 .collect();
141
142 Self {
143 children,
144 style: info.style.clone(),
145 config: FlexContainerConfig::new(&info.style),
146 }
147 }
148
149 pub(crate) fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
150 self.config = FlexContainerConfig::new(new_style);
151 self.style = new_style.clone();
152 }
153
154 pub(crate) fn subtree_size(&self) -> usize {
155 self.children
156 .iter()
157 .map(|child| child.borrow().with_base(|base| base.subtree_size()))
158 .sum()
159 }
160}
161
162#[expect(clippy::large_enum_variant)]
163#[derive(Debug, MallocSizeOf)]
164pub(crate) enum FlexLevelBox {
165 FlexItem(FlexItemBox),
166 OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
167}
168
169impl FlexLevelBox {
170 pub(crate) fn repair_style(
171 &mut self,
172 context: &SharedStyleContext,
173 node: &ServoLayoutNode,
174 new_style: &ServoArc<ComputedValues>,
175 ) {
176 match self {
177 FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
178 .independent_formatting_context
179 .repair_style(context, node, new_style),
180 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
181 .borrow_mut()
182 .context
183 .repair_style(context, node, new_style),
184 }
185 }
186
187 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
188 match self {
189 FlexLevelBox::FlexItem(flex_item_box) => {
190 callback(&flex_item_box.independent_formatting_context.base)
191 },
192 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
193 callback(&positioned_box.borrow().context.base)
194 },
195 }
196 }
197
198 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
199 match self {
200 FlexLevelBox::FlexItem(flex_item_box) => {
201 callback(&mut flex_item_box.independent_formatting_context.base)
202 },
203 FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
204 callback(&mut positioned_box.borrow_mut().context.base)
205 },
206 }
207 }
208
209 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
210 match self {
211 Self::FlexItem(flex_item_box) => flex_item_box
212 .independent_formatting_context
213 .attached_to_tree(layout_box),
214 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
215 .borrow_mut()
216 .context
217 .attached_to_tree(layout_box),
218 }
219 }
220}
221
222#[derive(MallocSizeOf)]
223pub(crate) struct FlexItemBox {
224 pub(crate) independent_formatting_context: IndependentFormattingContext,
225}
226
227impl std::fmt::Debug for FlexItemBox {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 f.write_str("FlexItemBox")
230 }
231}
232
233impl FlexItemBox {
234 fn new(independent_formatting_context: IndependentFormattingContext) -> Self {
235 Self {
236 independent_formatting_context,
237 }
238 }
239
240 pub(crate) fn style(&self) -> &ServoArc<ComputedValues> {
241 self.independent_formatting_context.style()
242 }
243
244 fn base_fragment_info(&self) -> BaseFragmentInfo {
245 self.independent_formatting_context.base_fragment_info()
246 }
247}