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