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