Skip to main content

layout/taffy/stylo_taffy/
convert.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 style::Atom;
6use stylo_atoms::atom;
7use taffy::MaxTrackSizingFunction;
8use taffy::style_helpers::*;
9
10use super::{as_clamped_i16, stylo};
11
12#[inline]
13pub fn length_percentage(val: &stylo::LengthPercentage) -> taffy::LengthPercentage {
14    match val.unpack() {
15        stylo::UnpackedLengthPercentage::Length(len) => length(len.px()),
16        stylo::UnpackedLengthPercentage::Percentage(percentage) => percent(percentage.0),
17        stylo::UnpackedLengthPercentage::Calc(calc_ref) => {
18            let calc_ptr = calc_ref as *const stylo::CalcLengthPercentage as *const ();
19            taffy::LengthPercentage::calc(calc_ptr)
20        },
21    }
22}
23
24#[inline]
25fn fit_content_function(limit: &stylo::LengthPercentage) -> taffy::Dimension {
26    match limit.unpack() {
27        stylo::UnpackedLengthPercentage::Length(length) => {
28            taffy::Dimension::fit_content_px(length.px())
29        },
30        stylo::UnpackedLengthPercentage::Percentage(percentage) => {
31            taffy::Dimension::fit_content_percent(percentage.0)
32        },
33        // TODO: fit-content() with a calc() limit
34        stylo::UnpackedLengthPercentage::Calc(_) => taffy::Dimension::fit_content(),
35    }
36}
37
38#[inline]
39pub fn dimension(value: &stylo::Size) -> taffy::Dimension {
40    match value {
41        stylo::Size::LengthPercentage(value) => length_percentage(&value.0).into(),
42        stylo::Size::Auto => taffy::Dimension::AUTO,
43        stylo::Size::MaxContent => taffy::Dimension::max_content(),
44        stylo::Size::MinContent => taffy::Dimension::min_content(),
45        stylo::Size::FitContent => taffy::Dimension::fit_content(),
46        stylo::Size::FitContentFunction(limit) => fit_content_function(&limit.0),
47        stylo::Size::Stretch | stylo::Size::WebkitFillAvailable => taffy::Dimension::stretch(),
48
49        // Anchor positioning will be flagged off for time being
50        stylo::Size::AnchorSizeFunction(_) => unreachable!(),
51        stylo::Size::AnchorContainingCalcFunction(_) => unreachable!(),
52    }
53}
54
55#[inline]
56pub fn min_size(value: &stylo::Size) -> taffy::LengthPercentageAuto {
57    match value {
58        stylo::Size::LengthPercentage(value) => length_percentage(&value.0).into(),
59        stylo::Size::Auto => taffy::LengthPercentageAuto::AUTO,
60
61        // TODO: implement other values in Taffy
62        stylo::Size::MaxContent => taffy::LengthPercentageAuto::AUTO,
63        stylo::Size::MinContent => taffy::LengthPercentageAuto::AUTO,
64        stylo::Size::FitContent => taffy::LengthPercentageAuto::AUTO,
65        stylo::Size::FitContentFunction(_) => taffy::LengthPercentageAuto::AUTO,
66        stylo::Size::Stretch | stylo::Size::WebkitFillAvailable => {
67            taffy::LengthPercentageAuto::AUTO
68        },
69
70        // Anchor positioning will be flagged off for time being
71        stylo::Size::AnchorSizeFunction(_) | stylo::Size::AnchorContainingCalcFunction(_) => {
72            unreachable!("Anchor positioning is disabled in stylo")
73        },
74    }
75}
76
77#[inline]
78pub fn max_size(value: &stylo::MaxSize) -> taffy::LengthPercentageAuto {
79    match value {
80        stylo::MaxSize::LengthPercentage(value) => length_percentage(&value.0).into(),
81        stylo::MaxSize::None => taffy::LengthPercentageAuto::AUTO,
82
83        // TODO: implement other values in Taffy
84        stylo::MaxSize::MaxContent => taffy::LengthPercentageAuto::AUTO,
85        stylo::MaxSize::MinContent => taffy::LengthPercentageAuto::AUTO,
86        stylo::MaxSize::FitContent => taffy::LengthPercentageAuto::AUTO,
87        stylo::MaxSize::FitContentFunction(_) => taffy::LengthPercentageAuto::AUTO,
88        stylo::MaxSize::Stretch | stylo::MaxSize::WebkitFillAvailable => {
89            taffy::LengthPercentageAuto::AUTO
90        },
91
92        // Anchor positioning will be flagged off for time being
93        stylo::MaxSize::AnchorSizeFunction(_) | stylo::MaxSize::AnchorContainingCalcFunction(_) => {
94            unreachable!("Anchor positioning is disabled in stylo")
95        },
96    }
97}
98
99#[inline]
100pub fn margin(val: &stylo::MarginVal) -> taffy::LengthPercentageAuto {
101    match val {
102        stylo::MarginVal::Auto => taffy::LengthPercentageAuto::AUTO,
103        stylo::MarginVal::LengthPercentage(val) => length_percentage(val).into(),
104
105        // Anchor positioning will be flagged off for time being
106        stylo::MarginVal::AnchorSizeFunction(_) |
107        stylo::MarginVal::AnchorContainingCalcFunction(_) => {
108            unreachable!("Anchor positioning is disabled in stylo")
109        },
110    }
111}
112
113#[inline]
114pub fn inset(val: &stylo::InsetVal) -> taffy::LengthPercentageAuto {
115    match val {
116        stylo::InsetVal::Auto => taffy::LengthPercentageAuto::AUTO,
117        stylo::InsetVal::LengthPercentage(val) => length_percentage(val).into(),
118
119        // Anchor positioning will be flagged off for time being
120        stylo::InsetVal::AnchorSizeFunction(_) |
121        stylo::InsetVal::AnchorFunction(_) |
122        stylo::InsetVal::AnchorContainingCalcFunction(_) => {
123            unreachable!("Anchor positioning is disabled in stylo")
124        },
125    }
126}
127
128#[inline]
129pub fn is_block(input: stylo::Display) -> bool {
130    matches!(input.outside(), stylo::DisplayOutside::Block) &&
131        matches!(
132            input.inside(),
133            stylo::DisplayInside::Flow | stylo::DisplayInside::FlowRoot
134        )
135}
136
137#[inline]
138pub fn box_generation_mode(input: stylo::Display) -> taffy::BoxGenerationMode {
139    match input.inside() {
140        stylo::DisplayInside::None => taffy::BoxGenerationMode::None,
141        _ => taffy::BoxGenerationMode::Normal,
142    }
143}
144
145#[inline]
146pub fn box_sizing(input: stylo::BoxSizing) -> taffy::BoxSizing {
147    match input {
148        stylo::BoxSizing::BorderBox => taffy::BoxSizing::BorderBox,
149        stylo::BoxSizing::ContentBox => taffy::BoxSizing::ContentBox,
150    }
151}
152
153#[inline]
154pub fn direction(input: stylo::Direction) -> taffy::Direction {
155    match input {
156        stylo::Direction::Ltr => taffy::Direction::Ltr,
157        stylo::Direction::Rtl => taffy::Direction::Rtl,
158    }
159}
160
161#[inline]
162pub fn position(input: stylo::Position) -> taffy::Position {
163    match input {
164        // TODO: support position:static
165        stylo::Position::Relative => taffy::Position::Relative,
166        stylo::Position::Static => taffy::Position::Relative,
167
168        // TODO: support position:fixed and sticky
169        stylo::Position::Absolute => taffy::Position::Absolute,
170        stylo::Position::Fixed => taffy::Position::Absolute,
171        stylo::Position::Sticky => taffy::Position::Relative,
172    }
173}
174
175#[inline]
176pub fn overflow(input: stylo::Overflow) -> taffy::Overflow {
177    // TODO: Enable Overflow::Clip in servo configuration of stylo
178    match input {
179        stylo::Overflow::Visible => taffy::Overflow::Visible,
180        stylo::Overflow::Hidden => taffy::Overflow::Hidden,
181        stylo::Overflow::Scroll => taffy::Overflow::Scroll,
182        stylo::Overflow::Clip => taffy::Overflow::Clip,
183        // TODO: Support Overflow::Auto in Taffy
184        stylo::Overflow::Auto => taffy::Overflow::Scroll,
185    }
186}
187
188#[inline]
189pub fn aspect_ratio(input: stylo::AspectRatio) -> Option<f32> {
190    match input.ratio {
191        stylo::PreferredRatio::None => None,
192        stylo::PreferredRatio::Ratio(val) => Some(val.0.0 / val.1.0),
193    }
194}
195
196#[inline]
197pub fn content_alignment(input: stylo::ContentDistribution) -> Option<taffy::AlignContent> {
198    let keyword = match input.primary().value() {
199        stylo::AlignFlags::NORMAL => return None,
200        stylo::AlignFlags::AUTO => return None,
201        stylo::AlignFlags::START => taffy::AlignContentKeyword::Start,
202        stylo::AlignFlags::END => taffy::AlignContentKeyword::End,
203        stylo::AlignFlags::LEFT => taffy::AlignContentKeyword::Start,
204        stylo::AlignFlags::RIGHT => taffy::AlignContentKeyword::End,
205        stylo::AlignFlags::FLEX_START => taffy::AlignContentKeyword::FlexStart,
206        stylo::AlignFlags::STRETCH => taffy::AlignContentKeyword::Stretch,
207        stylo::AlignFlags::FLEX_END => taffy::AlignContentKeyword::FlexEnd,
208        stylo::AlignFlags::CENTER => taffy::AlignContentKeyword::Center,
209        stylo::AlignFlags::SPACE_BETWEEN => taffy::AlignContentKeyword::SpaceBetween,
210        stylo::AlignFlags::SPACE_AROUND => taffy::AlignContentKeyword::SpaceAround,
211        stylo::AlignFlags::SPACE_EVENLY => taffy::AlignContentKeyword::SpaceEvenly,
212        // Should never be hit. But no real reason to panic here.
213        _ => return None,
214    };
215
216    let safety = match input.primary().flags() {
217        stylo::AlignFlags::SAFE => taffy::AlignmentSafety::Safe,
218        _ => taffy::AlignmentSafety::Unsafe,
219    };
220
221    Some(taffy::AlignContent { keyword, safety })
222}
223
224#[inline]
225pub fn item_alignment(input: stylo::AlignFlags) -> Option<taffy::AlignItems> {
226    let keyword = match input.value() {
227        stylo::AlignFlags::AUTO => return None,
228        stylo::AlignFlags::NORMAL => taffy::AlignItemsKeyword::Stretch,
229        stylo::AlignFlags::STRETCH => taffy::AlignItemsKeyword::Stretch,
230        stylo::AlignFlags::FLEX_START => taffy::AlignItemsKeyword::FlexStart,
231        stylo::AlignFlags::FLEX_END => taffy::AlignItemsKeyword::FlexEnd,
232        stylo::AlignFlags::SELF_START => taffy::AlignItemsKeyword::Start,
233        stylo::AlignFlags::SELF_END => taffy::AlignItemsKeyword::End,
234        stylo::AlignFlags::START => taffy::AlignItemsKeyword::Start,
235        stylo::AlignFlags::END => taffy::AlignItemsKeyword::End,
236        stylo::AlignFlags::LEFT => taffy::AlignItemsKeyword::Start,
237        stylo::AlignFlags::RIGHT => taffy::AlignItemsKeyword::End,
238        stylo::AlignFlags::CENTER => taffy::AlignItemsKeyword::Center,
239        stylo::AlignFlags::BASELINE => taffy::AlignItemsKeyword::Baseline,
240        // Should never be hit. But no real reason to panic here.
241        _ => return None,
242    };
243
244    let safety = match input.flags() {
245        stylo::AlignFlags::SAFE => taffy::AlignmentSafety::Safe,
246        _ => taffy::AlignmentSafety::Unsafe,
247    };
248
249    Some(taffy::AlignItems { keyword, safety })
250}
251
252#[inline]
253pub fn gap(input: &stylo::Gap) -> taffy::LengthPercentage {
254    match input {
255        // For Flexbox and CSS Grid the "normal" value is 0px. This may need to be updated
256        // if we ever implement multi-column layout.
257        stylo::Gap::Normal => taffy::LengthPercentage::ZERO,
258        stylo::Gap::LengthPercentage(val) => length_percentage(&val.0),
259    }
260}
261
262// CSS Grid styles
263// ===============
264
265#[inline]
266pub fn grid_auto_flow(input: stylo::GridAutoFlow) -> taffy::GridAutoFlow {
267    let is_row = input.contains(stylo::GridAutoFlow::ROW);
268    let is_dense = input.contains(stylo::GridAutoFlow::DENSE);
269
270    match (is_row, is_dense) {
271        (true, false) => taffy::GridAutoFlow::Row,
272        (true, true) => taffy::GridAutoFlow::RowDense,
273        (false, false) => taffy::GridAutoFlow::Column,
274        (false, true) => taffy::GridAutoFlow::ColumnDense,
275    }
276}
277
278#[inline]
279pub fn grid_line(input: &stylo::GridLine) -> taffy::GridPlacement<Atom> {
280    if input.is_auto() {
281        taffy::GridPlacement::Auto
282    } else if input.is_span {
283        if input.ident.0 != atom!("") {
284            taffy::GridPlacement::NamedSpan(
285                input.ident.0.clone(),
286                as_clamped_i16(input.line_num) as u16,
287            )
288        } else {
289            taffy::GridPlacement::Span(input.line_num as u16)
290        }
291    } else if input.ident.0 != atom!("") {
292        taffy::GridPlacement::NamedLine(input.ident.0.clone(), as_clamped_i16(input.line_num))
293    } else if input.line_num != 0 {
294        taffy::style_helpers::line(as_clamped_i16(input.line_num))
295    } else {
296        taffy::GridPlacement::Auto
297    }
298}
299
300#[inline]
301pub fn track_repeat(input: stylo::RepeatCount<i32>) -> taffy::RepetitionCount {
302    match input {
303        stylo::RepeatCount::Number(count) => {
304            taffy::RepetitionCount::Count(as_clamped_i16(count) as u16)
305        },
306        stylo::RepeatCount::AutoFill => taffy::RepetitionCount::AutoFill,
307        stylo::RepeatCount::AutoFit => taffy::RepetitionCount::AutoFit,
308    }
309}
310
311#[inline]
312pub fn track_size(input: &stylo::TrackSize<stylo::LengthPercentage>) -> taffy::TrackSizingFunction {
313    match input {
314        stylo::TrackSize::Breadth(breadth) => taffy::MinMax {
315            min: min_track(breadth),
316            max: max_track(breadth),
317        },
318        stylo::TrackSize::Minmax(min, max) => taffy::MinMax {
319            min: min_track(min),
320            max: max_track(max),
321        },
322        stylo::TrackSize::FitContent(limit) => taffy::MinMax {
323            min: taffy::MinTrackSizingFunction::AUTO,
324            max: match limit {
325                stylo::TrackBreadth::Breadth(lp) => {
326                    MaxTrackSizingFunction::fit_content(length_percentage(lp))
327                },
328
329                // Are these valid? Taffy doesn't support this in any case
330                stylo::TrackBreadth::Flex(_) => unreachable!(),
331                stylo::TrackBreadth::Auto => unreachable!(),
332                stylo::TrackBreadth::MinContent => unreachable!(),
333                stylo::TrackBreadth::MaxContent => unreachable!(),
334            },
335        },
336    }
337}
338
339#[inline]
340pub fn min_track(
341    input: &stylo::TrackBreadth<stylo::LengthPercentage>,
342) -> taffy::MinTrackSizingFunction {
343    match input {
344        stylo::TrackBreadth::Breadth(lp) => length_percentage(lp).into(),
345        stylo::TrackBreadth::Flex(_) => taffy::MinTrackSizingFunction::AUTO,
346        stylo::TrackBreadth::Auto => taffy::MinTrackSizingFunction::AUTO,
347        stylo::TrackBreadth::MinContent => taffy::MinTrackSizingFunction::MIN_CONTENT,
348        stylo::TrackBreadth::MaxContent => taffy::MinTrackSizingFunction::MAX_CONTENT,
349    }
350}
351
352#[inline]
353pub fn max_track(
354    input: &stylo::TrackBreadth<stylo::LengthPercentage>,
355) -> taffy::MaxTrackSizingFunction {
356    match input {
357        stylo::TrackBreadth::Breadth(lp) => length_percentage(lp).into(),
358        stylo::TrackBreadth::Flex(val) => fr(val.0),
359        stylo::TrackBreadth::Auto => taffy::MaxTrackSizingFunction::AUTO,
360        stylo::TrackBreadth::MinContent => taffy::MaxTrackSizingFunction::MIN_CONTENT,
361        stylo::TrackBreadth::MaxContent => taffy::MaxTrackSizingFunction::MAX_CONTENT,
362    }
363}