Skip to main content

layout/taffy/
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/. */
4mod layout;
5mod stylo_taffy;
6use std::fmt;
7
8use app_units::Au;
9use malloc_size_of_derive::MallocSizeOf;
10use script::layout_dom::ServoLayoutNode;
11use servo_arc::Arc;
12use style::Atom;
13use style::context::SharedStyleContext;
14use style::properties::ComputedValues;
15pub(crate) use stylo_taffy::TaffyStyloStyle;
16use taffy::GridItemStyle;
17
18use crate::cell::ArcRefCell;
19use crate::construct_modern::{ModernContainerBuilder, ModernItemKind};
20use crate::context::LayoutContext;
21use crate::dom::{LayoutBox, WeakLayoutBox};
22use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
23use crate::formatting_contexts::IndependentFormattingContext;
24use crate::fragment_tree::Fragment;
25use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize};
26use crate::layout_box_base::LayoutBoxBase;
27use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
28use crate::{DefiniteContainingBlock, PropagatedBoxTreeData};
29
30#[derive(Debug, MallocSizeOf)]
31pub(crate) struct TaffyContainer {
32    children: Vec<ArcRefCell<TaffyItemBox>>,
33    style: Arc<ComputedValues>,
34}
35
36impl TaffyContainer {
37    pub fn construct(
38        context: &LayoutContext,
39        info: &NodeAndStyleInfo,
40        contents: NonReplacedContents,
41        propagated_data: PropagatedBoxTreeData,
42    ) -> Self {
43        let mut builder = ModernContainerBuilder::new(context, info, propagated_data);
44        contents.traverse(context, info, &mut builder);
45        let items = builder.finish();
46
47        let children = items
48            .into_iter()
49            .map(|item| {
50                let taffy_item_box = match item.kind {
51                    ModernItemKind::InFlow(independent_formatting_context) => {
52                        ArcRefCell::new(TaffyItemBox::new(TaffyItemBoxInner::InFlowBox(
53                            independent_formatting_context,
54                        )))
55                    },
56                    ModernItemKind::OutOfFlow(independent_formatting_context) => {
57                        let abs_pos_box = ArcRefCell::new(AbsolutelyPositionedBox::new(
58                            independent_formatting_context,
59                        ));
60                        ArcRefCell::new(TaffyItemBox::new(
61                            TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box),
62                        ))
63                    },
64                    ModernItemKind::ReusedBox(layout_box) => match layout_box {
65                        LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box,
66                        _ => unreachable!("Undamaged taffy level element should be associated with taffy level box"),
67                    },
68                };
69
70                item.box_slot.set(LayoutBox::TaffyItemBox(taffy_item_box.clone()));
71                taffy_item_box
72            })
73            .collect();
74
75        Self {
76            children,
77            style: info.style.clone(),
78        }
79    }
80
81    pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
82        self.style = new_style.clone();
83    }
84}
85
86#[derive(MallocSizeOf)]
87pub(crate) struct TaffyItemBox {
88    pub(crate) taffy_layout: taffy::Layout,
89    pub(crate) taffy_baselines: taffy::Baselines,
90    pub(crate) child_fragments: Vec<Fragment>,
91    pub(crate) positioning_context: PositioningContext,
92    pub(crate) style: Arc<ComputedValues>,
93    pub(crate) taffy_level_box: TaffyItemBoxInner,
94}
95
96#[expect(clippy::large_enum_variant)]
97#[derive(Debug, MallocSizeOf)]
98pub(crate) enum TaffyItemBoxInner {
99    InFlowBox(IndependentFormattingContext),
100    OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
101}
102
103impl fmt::Debug for TaffyItemBox {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.debug_struct("TaffyItemBox")
106            .field("taffy_layout", &self.taffy_layout)
107            .field("taffy_baselines", &self.taffy_baselines)
108            .field("child_fragments", &self.child_fragments.len())
109            .field("style", &self.style)
110            .field("taffy_level_box", &self.taffy_level_box)
111            .finish()
112    }
113}
114
115impl TaffyItemBox {
116    fn new(inner: TaffyItemBoxInner) -> Self {
117        let style: Arc<ComputedValues> = match &inner {
118            TaffyItemBoxInner::InFlowBox(item) => item.style().clone(),
119            TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(absbox) => {
120                (*absbox).borrow().context.style().clone()
121            },
122        };
123
124        Self {
125            taffy_layout: Default::default(),
126            taffy_baselines: taffy::Baselines::NONE,
127            child_fragments: Vec::new(),
128            positioning_context: PositioningContext::default(),
129            style,
130            taffy_level_box: inner,
131        }
132    }
133
134    pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
135        match self.taffy_level_box {
136            TaffyItemBoxInner::InFlowBox(ref independent_formatting_context) => {
137                callback(&independent_formatting_context.base)
138            },
139            TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(ref positioned_box) => {
140                callback(&positioned_box.borrow().context.base)
141            },
142        }
143    }
144
145    pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
146        match &mut self.taffy_level_box {
147            TaffyItemBoxInner::InFlowBox(independent_formatting_context) => {
148                callback(&mut independent_formatting_context.base)
149            },
150            TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
151                callback(&mut positioned_box.borrow_mut().context.base)
152            },
153        }
154    }
155
156    pub(crate) fn repair_style(
157        &mut self,
158        context: &SharedStyleContext,
159        node: &ServoLayoutNode,
160        new_style: &Arc<ComputedValues>,
161    ) {
162        self.style = new_style.clone();
163        match &mut self.taffy_level_box {
164            TaffyItemBoxInner::InFlowBox(independent_formatting_context) => {
165                independent_formatting_context.repair_style(context, node, new_style)
166            },
167            TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
168                .borrow_mut()
169                .context
170                .repair_style(context, node, new_style),
171        }
172    }
173
174    fn is_in_flow_replaced(&self) -> bool {
175        match &self.taffy_level_box {
176            TaffyItemBoxInner::InFlowBox(fc) => fc.is_replaced(),
177            TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(_) => false,
178        }
179    }
180
181    pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
182        match &self.taffy_level_box {
183            TaffyItemBoxInner::InFlowBox(formatting_context) => {
184                formatting_context.attached_to_tree(layout_box)
185            },
186            TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
187                .borrow_mut()
188                .context
189                .attached_to_tree(layout_box),
190        }
191    }
192}
193
194/// Details from Taffy grid layout that will be stored
195#[derive(Clone, Debug, MallocSizeOf)]
196pub(crate) struct SpecificTaffyGridInfo {
197    pub info: taffy::DetailedGridInfo<Atom>,
198}
199
200impl SpecificTaffyGridInfo {
201    fn from_detailed_grid_layout(grid_info: taffy::DetailedGridInfo<Atom>) -> Self {
202        Self { info: grid_info }
203    }
204
205    pub(crate) fn resolve_grid_area(
206        &self,
207        item_style: &ComputedValues,
208        containing_block: &DefiniteContainingBlock,
209        containing_block_border: PhysicalSides<Au>,
210    ) -> PhysicalRect<Au> {
211        let item_style = TaffyStyloStyle::new(item_style, false);
212        let writing_mode = containing_block.style.writing_mode;
213        let physical_containing_block_size = containing_block.size.to_physical_size(writing_mode);
214
215        // Convert direction to Taffy type
216        let direction = if writing_mode.is_bidi_ltr() {
217            taffy::Direction::Ltr
218        } else {
219            taffy::Direction::Rtl
220        };
221
222        // Convert padding box to Taffy type
223        let border_left = containing_block_border.left.to_f32_px();
224        let border_top = containing_block_border.top.to_f32_px();
225
226        let padding_box = taffy::Rect {
227            left: border_left,
228            right: border_left + physical_containing_block_size.width.to_f32_px(),
229            top: border_top,
230            bottom: border_top + physical_containing_block_size.height.to_f32_px(),
231        };
232
233        // Call into Taffy to resolve grid area
234        let area = self.info.resolve_absolute_grid_area(
235            item_style.grid_row(),
236            item_style.grid_column(),
237            direction,
238            padding_box,
239        );
240
241        // Convert grid area into a PhysicalRect, and adjust it to be relative to the padding box
242        PhysicalRect::new(
243            PhysicalPoint::new(
244                Au::from_f32_px(area.left) - containing_block_border.left,
245                Au::from_f32_px(area.top) - containing_block_border.top,
246            ),
247            PhysicalSize::new(
248                Au::from_f32_px(area.right - area.left),
249                Au::from_f32_px(area.bottom - area.top),
250            ),
251        )
252    }
253}