Skip to main content

layout/taffy/stylo_taffy/
wrapper.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 std::ops::Deref;
6
7use style::properties::ComputedValues;
8use style::values::CustomIdent;
9use style::values::computed::{BorderSideWidth, GridTemplateAreas, LengthPercentage};
10use style::values::generics::grid::{TrackListValue, TrackRepeat, TrackSize};
11use style::values::specified::BorderStyle;
12use style::values::specified::position::NamedArea;
13use style::{Atom, OwnedSlice};
14use taffy::prelude::TaffyAuto;
15
16use super::{as_clamped_i16, convert, stylo};
17
18/// A wrapper struct for anything that Deref's to a [`ComputedValues`], which
19/// implements Taffy's layout traits and can used with Taffy's layout algorithms.
20pub struct TaffyStyloStyle<T: Deref<Target = ComputedValues>> {
21    pub style: T,
22    pub is_compressible_replaced: bool,
23}
24
25impl<T: Deref<Target = ComputedValues>> TaffyStyloStyle<T> {
26    pub fn new(style: T, is_compressible_replaced: bool) -> Self {
27        Self {
28            style,
29            is_compressible_replaced,
30        }
31    }
32}
33
34impl<T: Deref<Target = ComputedValues>> taffy::CoreStyle for TaffyStyloStyle<T> {
35    type CustomIdent = Atom;
36
37    #[inline]
38    fn box_generation_mode(&self) -> taffy::BoxGenerationMode {
39        convert::box_generation_mode(self.style.get_box().display)
40    }
41
42    #[inline]
43    fn direction(&self) -> taffy::Direction {
44        convert::direction(self.style.slow_clone_direction())
45    }
46
47    #[inline]
48    fn is_block(&self) -> bool {
49        convert::is_block(self.style.get_box().display)
50    }
51
52    #[inline]
53    fn is_compressible_replaced(&self) -> bool {
54        self.is_compressible_replaced
55    }
56
57    #[inline]
58    fn box_sizing(&self) -> taffy::BoxSizing {
59        convert::box_sizing(self.style.get_position().box_sizing)
60    }
61
62    #[inline]
63    fn overflow(&self) -> taffy::Point<taffy::Overflow> {
64        let box_styles = self.style.get_box();
65        taffy::Point {
66            x: convert::overflow(box_styles.overflow_x),
67            y: convert::overflow(box_styles.overflow_y),
68        }
69    }
70
71    #[inline]
72    fn scrollbar_width(&self) -> f32 {
73        0.0
74    }
75
76    #[inline]
77    fn position(&self) -> taffy::Position {
78        convert::position(self.style.get_box().position)
79    }
80
81    #[inline]
82    fn inset(&self) -> taffy::Rect<taffy::LengthPercentageAuto> {
83        // Taffy doesn't support static nor sticky positionings, they are treated
84        // as relative. As a workaround, ignore the insets.
85        if matches!(
86            self.style.get_box().position,
87            stylo::Position::Static | stylo::Position::Sticky
88        ) {
89            return taffy::Rect {
90                left: taffy::LengthPercentageAuto::AUTO,
91                right: taffy::LengthPercentageAuto::AUTO,
92                top: taffy::LengthPercentageAuto::AUTO,
93                bottom: taffy::LengthPercentageAuto::AUTO,
94            };
95        }
96        let position_styles = self.style.get_position();
97        taffy::Rect {
98            left: convert::inset(&position_styles.left),
99            right: convert::inset(&position_styles.right),
100            top: convert::inset(&position_styles.top),
101            bottom: convert::inset(&position_styles.bottom),
102        }
103    }
104
105    #[inline]
106    fn size(&self) -> taffy::Size<taffy::Dimension> {
107        let position_styles = self.style.get_position();
108        taffy::Size {
109            width: convert::dimension(&position_styles.width),
110            height: convert::dimension(&position_styles.height),
111        }
112    }
113
114    #[inline]
115    fn min_size(&self) -> taffy::Size<taffy::LengthPercentageAuto> {
116        let position_styles = self.style.get_position();
117        taffy::Size {
118            width: convert::min_size(&position_styles.min_width),
119            height: convert::min_size(&position_styles.min_height),
120        }
121    }
122
123    #[inline]
124    fn max_size(&self) -> taffy::Size<taffy::LengthPercentageAuto> {
125        let position_styles = self.style.get_position();
126        taffy::Size {
127            width: convert::max_size(&position_styles.max_width),
128            height: convert::max_size(&position_styles.max_height),
129        }
130    }
131
132    #[inline]
133    fn aspect_ratio(&self) -> Option<f32> {
134        convert::aspect_ratio(self.style.get_position().aspect_ratio)
135    }
136
137    #[inline]
138    fn margin(&self) -> taffy::Rect<taffy::LengthPercentageAuto> {
139        let margin_styles = self.style.get_margin();
140        taffy::Rect {
141            left: convert::margin(&margin_styles.margin_left),
142            right: convert::margin(&margin_styles.margin_right),
143            top: convert::margin(&margin_styles.margin_top),
144            bottom: convert::margin(&margin_styles.margin_bottom),
145        }
146    }
147
148    #[inline]
149    fn padding(&self) -> taffy::Rect<taffy::LengthPercentage> {
150        let padding_styles = self.style.get_padding();
151        taffy::Rect {
152            left: convert::length_percentage(&padding_styles.padding_left.0),
153            right: convert::length_percentage(&padding_styles.padding_right.0),
154            top: convert::length_percentage(&padding_styles.padding_top.0),
155            bottom: convert::length_percentage(&padding_styles.padding_bottom.0),
156        }
157    }
158
159    #[inline]
160    fn border(&self) -> taffy::Rect<taffy::LengthPercentage> {
161        let border = self.style.get_border();
162        let resolve = |width: &BorderSideWidth, style: BorderStyle| {
163            taffy::LengthPercentage::length(if style.none_or_hidden() {
164                0.0
165            } else {
166                width.0.to_f32_px()
167            })
168        };
169        taffy::Rect {
170            left: resolve(&border.border_left_width, border.border_left_style),
171            right: resolve(&border.border_right_width, border.border_right_style),
172            top: resolve(&border.border_top_width, border.border_top_style),
173            bottom: resolve(&border.border_bottom_width, border.border_bottom_style),
174        }
175    }
176}
177
178type SliceMapIter<'a, Input, Output> =
179    core::iter::Map<core::slice::Iter<'a, Input>, for<'c> fn(&'c Input) -> Output>;
180type SliceMapRefIter<'a, Input, Output> =
181    core::iter::Map<core::slice::Iter<'a, Input>, for<'c> fn(&'c Input) -> &'c Output>;
182
183// Line name iterator type aliases
184type LineNameSetIter<'a> = SliceMapRefIter<'a, CustomIdent, Atom>;
185type LineNameIter<'a> = core::iter::Map<
186    core::slice::Iter<'a, OwnedSlice<CustomIdent>>,
187    fn(&OwnedSlice<CustomIdent>) -> LineNameSetIter<'_>,
188>;
189
190#[derive(Clone)]
191pub struct StyloLineNameIter<'a>(LineNameIter<'a>);
192impl<'a> StyloLineNameIter<'a> {
193    fn new(names: &'a OwnedSlice<OwnedSlice<CustomIdent>>) -> Self {
194        Self(names.iter().map(|names| names.iter().map(|ident| &ident.0)))
195    }
196}
197impl<'a> Iterator for StyloLineNameIter<'a> {
198    type Item = core::iter::Map<core::slice::Iter<'a, CustomIdent>, fn(&CustomIdent) -> &Atom>;
199    fn next(&mut self) -> Option<Self::Item> {
200        self.0.next()
201    }
202    fn size_hint(&self) -> (usize, Option<usize>) {
203        self.0.size_hint()
204    }
205}
206impl ExactSizeIterator for StyloLineNameIter<'_> {}
207impl<'a> taffy::TemplateLineNames<'a, Atom> for StyloLineNameIter<'a> {
208    type LineNameSet<'b>
209        = SliceMapRefIter<'b, CustomIdent, Atom>
210    where
211        Self: 'b;
212}
213
214pub struct RepetitionWrapper<'a>(&'a TrackRepeat<LengthPercentage, i32>);
215
216impl taffy::GenericRepetition for RepetitionWrapper<'_> {
217    type CustomIdent = Atom;
218
219    type RepetitionTrackList<'a>
220        = SliceMapIter<'a, stylo::TrackSize<LengthPercentage>, taffy::TrackSizingFunction>
221    where
222        Self: 'a;
223
224    type TemplateLineNames<'a>
225        = StyloLineNameIter<'a>
226    where
227        Self: 'a;
228
229    fn count(&self) -> taffy::RepetitionCount {
230        convert::track_repeat(self.0.count)
231    }
232
233    fn tracks(&self) -> Self::RepetitionTrackList<'_> {
234        self.0.track_sizes.iter().map(convert::track_size)
235    }
236
237    fn lines_names(&self) -> Self::TemplateLineNames<'_> {
238        StyloLineNameIter::new(&self.0.line_names)
239    }
240}
241
242impl<T: Deref<Target = ComputedValues>> taffy::GridContainerStyle for TaffyStyloStyle<T> {
243    type Repetition<'a>
244        = RepetitionWrapper<'a>
245    where
246        Self: 'a;
247
248    type TemplateTrackList<'a>
249        = core::iter::Map<
250        core::slice::Iter<'a, TrackListValue<LengthPercentage, i32>>,
251        fn(
252            &'a TrackListValue<LengthPercentage, i32>,
253        ) -> taffy::GenericGridTemplateComponent<Atom, RepetitionWrapper<'a>>,
254    >
255    where
256        Self: 'a;
257
258    type AutoTrackList<'a>
259        = SliceMapIter<'a, TrackSize<LengthPercentage>, taffy::TrackSizingFunction>
260    where
261        Self: 'a;
262
263    type TemplateLineNames<'a>
264        = StyloLineNameIter<'a>
265    where
266        Self: 'a;
267    type GridTemplateAreas<'a>
268        = SliceMapIter<'a, NamedArea, taffy::GridTemplateArea<Atom>>
269    where
270        Self: 'a;
271
272    #[inline]
273    fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>> {
274        match &self.style.get_position().grid_template_rows {
275            stylo::GenericGridTemplateComponent::None => None,
276            stylo::GenericGridTemplateComponent::TrackList(list) => {
277                Some(list.values.iter().map(|track| match track {
278                    stylo::TrackListValue::TrackSize(size) => {
279                        taffy::GenericGridTemplateComponent::Single(convert::track_size(size))
280                    },
281                    stylo::TrackListValue::TrackRepeat(repeat) => {
282                        taffy::GenericGridTemplateComponent::Repeat(RepetitionWrapper(repeat))
283                    },
284                }))
285            },
286
287            // TODO: Implement subgrid
288            stylo::GenericGridTemplateComponent::Subgrid(_) => None,
289        }
290    }
291
292    #[inline]
293    fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>> {
294        match &self.style.get_position().grid_template_columns {
295            stylo::GenericGridTemplateComponent::None => None,
296            stylo::GenericGridTemplateComponent::TrackList(list) => {
297                Some(list.values.iter().map(|track| match track {
298                    stylo::TrackListValue::TrackSize(size) => {
299                        taffy::GenericGridTemplateComponent::Single(convert::track_size(size))
300                    },
301                    stylo::TrackListValue::TrackRepeat(repeat) => {
302                        taffy::GenericGridTemplateComponent::Repeat(RepetitionWrapper(repeat))
303                    },
304                }))
305            },
306
307            // TODO: Implement subgrid
308            stylo::GenericGridTemplateComponent::Subgrid(_) => None,
309        }
310    }
311
312    #[inline]
313    fn grid_auto_rows(&self) -> Self::AutoTrackList<'_> {
314        self.style
315            .get_position()
316            .grid_auto_rows
317            .0
318            .iter()
319            .map(convert::track_size)
320    }
321
322    #[inline]
323    fn grid_auto_columns(&self) -> Self::AutoTrackList<'_> {
324        self.style
325            .get_position()
326            .grid_auto_columns
327            .0
328            .iter()
329            .map(convert::track_size)
330    }
331
332    fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>> {
333        match &self.style.get_position().grid_template_areas {
334            GridTemplateAreas::Areas(areas) => {
335                Some(areas.0.areas.iter().map(|area| taffy::GridTemplateArea {
336                    name: area.name.clone(),
337                    row_start: area.rows.start as u16,
338                    row_end: area.rows.end as u16,
339                    column_start: area.columns.start as u16,
340                    column_end: area.columns.end as u16,
341                }))
342            },
343            GridTemplateAreas::None => None,
344        }
345    }
346
347    fn grid_template_area_row_count(&self) -> u16 {
348        match &self.style.get_position().grid_template_areas {
349            GridTemplateAreas::Areas(areas) => as_clamped_i16(areas.0.strings.len() as i32) as u16,
350            GridTemplateAreas::None => 0,
351        }
352    }
353
354    fn grid_template_area_column_count(&self) -> u16 {
355        match &self.style.get_position().grid_template_areas {
356            GridTemplateAreas::Areas(areas) => as_clamped_i16(areas.0.width as i32) as u16,
357            GridTemplateAreas::None => 0,
358        }
359    }
360
361    fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>> {
362        match &self.style.get_position().grid_template_columns {
363            stylo::GenericGridTemplateComponent::None => None,
364            stylo::GenericGridTemplateComponent::TrackList(list) => {
365                Some(StyloLineNameIter::new(&list.line_names))
366            },
367            // TODO: Implement subgrid
368            stylo::GenericGridTemplateComponent::Subgrid(_) => None,
369        }
370    }
371
372    fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>> {
373        match &self.style.get_position().grid_template_rows {
374            stylo::GenericGridTemplateComponent::None => None,
375            stylo::GenericGridTemplateComponent::TrackList(list) => {
376                Some(StyloLineNameIter::new(&list.line_names))
377            },
378            // TODO: Implement subgrid
379            stylo::GenericGridTemplateComponent::Subgrid(_) => None,
380        }
381    }
382
383    #[inline]
384    fn grid_auto_flow(&self) -> taffy::GridAutoFlow {
385        convert::grid_auto_flow(self.style.get_position().grid_auto_flow)
386    }
387
388    #[inline]
389    fn gap(&self) -> taffy::Size<taffy::LengthPercentage> {
390        let position_styles = self.style.get_position();
391        taffy::Size {
392            width: convert::gap(&position_styles.column_gap),
393            height: convert::gap(&position_styles.row_gap),
394        }
395    }
396
397    #[inline]
398    fn align_content(&self) -> Option<taffy::AlignContent> {
399        convert::content_alignment(self.style.get_position().align_content)
400    }
401
402    #[inline]
403    fn justify_content(&self) -> Option<taffy::JustifyContent> {
404        convert::content_alignment(self.style.get_position().justify_content)
405    }
406
407    #[inline]
408    fn align_items(&self) -> Option<taffy::AlignItems> {
409        convert::item_alignment(self.style.get_position().align_items.0)
410    }
411
412    #[inline]
413    fn justify_items(&self) -> Option<taffy::AlignItems> {
414        convert::item_alignment(self.style.get_position().justify_items.computed.0.0)
415    }
416}
417
418impl<T: Deref<Target = ComputedValues>> taffy::GridItemStyle for TaffyStyloStyle<T> {
419    #[inline]
420    fn grid_row(&self) -> taffy::Line<taffy::GridPlacement<Atom>> {
421        let position_styles = self.style.get_position();
422        taffy::Line {
423            start: convert::grid_line(&position_styles.grid_row_start),
424            end: convert::grid_line(&position_styles.grid_row_end),
425        }
426    }
427
428    #[inline]
429    fn grid_column(&self) -> taffy::Line<taffy::GridPlacement<Atom>> {
430        let position_styles = self.style.get_position();
431        taffy::Line {
432            start: convert::grid_line(&position_styles.grid_column_start),
433            end: convert::grid_line(&position_styles.grid_column_end),
434        }
435    }
436
437    #[inline]
438    fn align_self(&self) -> Option<taffy::AlignSelf> {
439        convert::item_alignment(self.style.get_position().align_self.0)
440    }
441
442    #[inline]
443    fn justify_self(&self) -> Option<taffy::AlignSelf> {
444        convert::item_alignment(self.style.get_position().justify_self.0)
445    }
446}