style/values/specified/
length.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
5//! [Length values][length].
6//!
7//! [length]: https://drafts.csswg.org/css-values/#lengths
8
9use super::{AllowQuirks, Number, Percentage, ToComputedValue};
10use crate::computed_value_flags::ComputedValueFlags;
11use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
12#[cfg(feature = "gecko")]
13use crate::gecko_bindings::structs::GeckoFontMetrics;
14use crate::parser::{Parse, ParserContext};
15use crate::values::computed::{self, CSSPixelLength, Context};
16use crate::values::generics::length as generics;
17use crate::values::generics::length::{
18    GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal,
19    GenericMargin, GenericMaxSize, GenericSize,
20};
21use crate::values::generics::NonNegative;
22use crate::values::specified::calc::{self, AllowAnchorPositioningFunctions, CalcNode};
23use crate::values::specified::font::QueryFontMetricsFlags;
24use crate::values::specified::NonNegativeNumber;
25use crate::values::CSSFloat;
26use crate::{Zero, ZeroNoPercent};
27use app_units::AU_PER_PX;
28use cssparser::{Parser, Token};
29use std::cmp;
30use std::fmt::{self, Write};
31use style_traits::values::specified::AllowedNumericType;
32use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
33
34pub use super::image::Image;
35pub use super::image::{EndingShape as GradientEndingShape, Gradient};
36pub use crate::values::specified::calc::CalcLengthPercentage;
37
38/// Number of pixels per inch
39pub const PX_PER_IN: CSSFloat = 96.;
40/// Number of pixels per centimeter
41pub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54;
42/// Number of pixels per millimeter
43pub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4;
44/// Number of pixels per quarter
45pub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.;
46/// Number of pixels per point
47pub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.;
48/// Number of pixels per pica
49pub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.;
50
51/// A font relative length. Note that if any new value is
52/// added here, `custom_properties::NonCustomReferences::from_unit`
53/// must also be updated. Consult the comment in that function as to why.
54#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
55#[repr(u8)]
56pub enum FontRelativeLength {
57    /// A "em" value: https://drafts.csswg.org/css-values/#em
58    #[css(dimension)]
59    Em(CSSFloat),
60    /// A "ex" value: https://drafts.csswg.org/css-values/#ex
61    #[css(dimension)]
62    Ex(CSSFloat),
63    /// A "ch" value: https://drafts.csswg.org/css-values/#ch
64    #[css(dimension)]
65    Ch(CSSFloat),
66    /// A "cap" value: https://drafts.csswg.org/css-values/#cap
67    #[css(dimension)]
68    Cap(CSSFloat),
69    /// An "ic" value: https://drafts.csswg.org/css-values/#ic
70    #[css(dimension)]
71    Ic(CSSFloat),
72    /// A "rem" value: https://drafts.csswg.org/css-values/#rem
73    #[css(dimension)]
74    Rem(CSSFloat),
75    /// A "lh" value: https://drafts.csswg.org/css-values/#lh
76    #[css(dimension)]
77    Lh(CSSFloat),
78    /// A "rlh" value: https://drafts.csswg.org/css-values/#lh
79    #[css(dimension)]
80    Rlh(CSSFloat),
81}
82
83/// A source to resolve font-relative units against
84#[derive(Clone, Copy, Debug, PartialEq)]
85pub enum FontBaseSize {
86    /// Use the font-size of the current element.
87    CurrentStyle,
88    /// Use the inherited font-size.
89    InheritedStyle,
90}
91
92/// A source to resolve font-relative line-height units against.
93#[derive(Clone, Copy, Debug, PartialEq)]
94pub enum LineHeightBase {
95    /// Use the line-height of the current element.
96    CurrentStyle,
97    /// Use the inherited line-height.
98    InheritedStyle,
99}
100
101impl FontBaseSize {
102    /// Calculate the actual size for a given context
103    pub fn resolve(&self, context: &Context) -> computed::FontSize {
104        let style = context.style();
105        match *self {
106            Self::CurrentStyle => style.get_font().clone_font_size(),
107            Self::InheritedStyle => {
108                // If we're using the size from our inherited style, we still need to apply our
109                // own zoom.
110                let zoom = style.effective_zoom_for_inheritance;
111                style.get_parent_font().clone_font_size().zoom(zoom)
112            },
113        }
114    }
115}
116
117impl FontRelativeLength {
118    /// Unit identifier for `em`.
119    pub const EM: &'static str = "em";
120    /// Unit identifier for `ex`.
121    pub const EX: &'static str = "ex";
122    /// Unit identifier for `ch`.
123    pub const CH: &'static str = "ch";
124    /// Unit identifier for `cap`.
125    pub const CAP: &'static str = "cap";
126    /// Unit identifier for `ic`.
127    pub const IC: &'static str = "ic";
128    /// Unit identifier for `rem`.
129    pub const REM: &'static str = "rem";
130    /// Unit identifier for `lh`.
131    pub const LH: &'static str = "lh";
132    /// Unit identifier for `rlh`.
133    pub const RLH: &'static str = "rlh";
134
135    /// Return the unitless, raw value.
136    fn unitless_value(&self) -> CSSFloat {
137        match *self {
138            Self::Em(v) |
139            Self::Ex(v) |
140            Self::Ch(v) |
141            Self::Cap(v) |
142            Self::Ic(v) |
143            Self::Rem(v) |
144            Self::Lh(v) |
145            Self::Rlh(v) => v,
146        }
147    }
148
149    // Return the unit, as a string.
150    fn unit(&self) -> &'static str {
151        match *self {
152            Self::Em(_) => Self::EM,
153            Self::Ex(_) => Self::EX,
154            Self::Ch(_) => Self::CH,
155            Self::Cap(_) => Self::CAP,
156            Self::Ic(_) => Self::IC,
157            Self::Rem(_) => Self::REM,
158            Self::Lh(_) => Self::LH,
159            Self::Rlh(_) => Self::RLH,
160        }
161    }
162
163    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
164    where
165        O: Fn(f32, f32) -> f32,
166    {
167        use self::FontRelativeLength::*;
168
169        if std::mem::discriminant(self) != std::mem::discriminant(other) {
170            return Err(());
171        }
172
173        Ok(match (self, other) {
174            (&Em(one), &Em(other)) => Em(op(one, other)),
175            (&Ex(one), &Ex(other)) => Ex(op(one, other)),
176            (&Ch(one), &Ch(other)) => Ch(op(one, other)),
177            (&Cap(one), &Cap(other)) => Cap(op(one, other)),
178            (&Ic(one), &Ic(other)) => Ic(op(one, other)),
179            (&Rem(one), &Rem(other)) => Rem(op(one, other)),
180            (&Lh(one), &Lh(other)) => Lh(op(one, other)),
181            (&Rlh(one), &Rlh(other)) => Rlh(op(one, other)),
182            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
183            // able to figure it own on its own so we help.
184            _ => unsafe {
185                match *self {
186                    Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) | Lh(..) | Rlh(..) => {},
187                }
188                debug_unreachable!("Forgot to handle unit in try_op()")
189            },
190        })
191    }
192
193    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
194        match self {
195            Self::Em(x) => Self::Em(op(*x)),
196            Self::Ex(x) => Self::Ex(op(*x)),
197            Self::Ch(x) => Self::Ch(op(*x)),
198            Self::Cap(x) => Self::Cap(op(*x)),
199            Self::Ic(x) => Self::Ic(op(*x)),
200            Self::Rem(x) => Self::Rem(op(*x)),
201            Self::Lh(x) => Self::Lh(op(*x)),
202            Self::Rlh(x) => Self::Rlh(op(*x)),
203        }
204    }
205
206    /// Computes the font-relative length.
207    pub fn to_computed_value(
208        &self,
209        context: &Context,
210        base_size: FontBaseSize,
211        line_height_base: LineHeightBase,
212    ) -> computed::Length {
213        let (reference_size, length) =
214            self.reference_font_size_and_length(context, base_size, line_height_base);
215        (reference_size * length).finite()
216    }
217
218    /// Computes the length, given a GeckoFontMetrics getter to resolve font-relative units.
219    #[cfg(feature = "gecko")]
220    pub fn to_computed_pixel_length_with_font_metrics(
221        &self,
222        get_font_metrics: impl Fn() -> GeckoFontMetrics,
223    ) -> Result<CSSFloat, ()> {
224        let metrics = get_font_metrics();
225        Ok(match *self {
226            Self::Em(v) => v * metrics.mComputedEmSize.px(),
227            Self::Ex(v) => v * metrics.mXSize.px(),
228            Self::Ch(v) => v * metrics.mChSize.px(),
229            Self::Cap(v) => v * metrics.mCapHeight.px(),
230            Self::Ic(v) => v * metrics.mIcWidth.px(),
231            // `lh`, `rlh` & `rem` are unsupported as we have no context for it.
232            Self::Rem(_) | Self::Lh(_) | Self::Rlh(_) => return Err(()),
233        })
234    }
235
236    /// Return reference font size.
237    ///
238    /// We use the base_size flag to pass a different size for computing
239    /// font-size and unconstrained font-size.
240    ///
241    /// This returns a pair, the first one is the reference font size, and the
242    /// second one is the unpacked relative length.
243    fn reference_font_size_and_length(
244        &self,
245        context: &Context,
246        base_size: FontBaseSize,
247        line_height_base: LineHeightBase,
248    ) -> (computed::Length, CSSFloat) {
249        fn query_font_metrics(
250            context: &Context,
251            base_size: FontBaseSize,
252            orientation: FontMetricsOrientation,
253            flags: QueryFontMetricsFlags,
254        ) -> FontMetrics {
255            context.query_font_metrics(
256                base_size,
257                orientation,
258                flags,
259            )
260        }
261
262        let reference_font_size = base_size.resolve(context);
263        match *self {
264            Self::Em(length) => {
265                if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle {
266                    context
267                        .rule_cache_conditions
268                        .borrow_mut()
269                        .set_font_size_dependency(reference_font_size.computed_size);
270                }
271
272                (reference_font_size.computed_size(), length)
273            },
274            Self::Ex(length) => {
275                // The x-height is an intrinsically horizontal metric.
276                let metrics = query_font_metrics(
277                    context,
278                    base_size,
279                    FontMetricsOrientation::Horizontal,
280                    QueryFontMetricsFlags::empty(),
281                );
282                let reference_size = metrics.x_height.unwrap_or_else(|| {
283                    // https://drafts.csswg.org/css-values/#ex
284                    //
285                    //     In the cases where it is impossible or impractical to
286                    //     determine the x-height, a value of 0.5em must be
287                    //     assumed.
288                    //
289                    // (But note we use 0.5em of the used, not computed
290                    // font-size)
291                    reference_font_size.used_size() * 0.5
292                });
293                (reference_size, length)
294            },
295            Self::Ch(length) => {
296                // https://drafts.csswg.org/css-values/#ch:
297                //
298                //     Equal to the used advance measure of the “0” (ZERO,
299                //     U+0030) glyph in the font used to render it. (The advance
300                //     measure of a glyph is its advance width or height,
301                //     whichever is in the inline axis of the element.)
302                //
303                let metrics = query_font_metrics(
304                    context,
305                    base_size,
306                    FontMetricsOrientation::MatchContextPreferHorizontal,
307                    QueryFontMetricsFlags::NEEDS_CH,
308                );
309                let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| {
310                    // https://drafts.csswg.org/css-values/#ch
311                    //
312                    //     In the cases where it is impossible or impractical to
313                    //     determine the measure of the “0” glyph, it must be
314                    //     assumed to be 0.5em wide by 1em tall. Thus, the ch
315                    //     unit falls back to 0.5em in the general case, and to
316                    //     1em when it would be typeset upright (i.e.
317                    //     writing-mode is vertical-rl or vertical-lr and
318                    //     text-orientation is upright).
319                    //
320                    // Same caveat about computed vs. used font-size applies
321                    // above.
322                    let wm = context.style().writing_mode;
323                    if wm.is_vertical() && wm.is_upright() {
324                        reference_font_size.used_size()
325                    } else {
326                        reference_font_size.used_size() * 0.5
327                    }
328                });
329                (reference_size, length)
330            },
331            Self::Cap(length) => {
332                let metrics = query_font_metrics(
333                    context,
334                    base_size,
335                    FontMetricsOrientation::Horizontal,
336                    QueryFontMetricsFlags::empty(),
337                );
338                let reference_size = metrics.cap_height.unwrap_or_else(|| {
339                    // https://drafts.csswg.org/css-values/#cap
340                    //
341                    //     In the cases where it is impossible or impractical to
342                    //     determine the cap-height, the font’s ascent must be
343                    //     used.
344                    //
345                    metrics.ascent
346                });
347                (reference_size, length)
348            },
349            Self::Ic(length) => {
350                let metrics = query_font_metrics(
351                    context,
352                    base_size,
353                    FontMetricsOrientation::MatchContextPreferVertical,
354                    QueryFontMetricsFlags::NEEDS_IC,
355                );
356                let reference_size = metrics.ic_width.unwrap_or_else(|| {
357                    // https://drafts.csswg.org/css-values/#ic
358                    //
359                    //     In the cases where it is impossible or impractical to
360                    //     determine the ideographic advance measure, it must be
361                    //     assumed to be 1em.
362                    //
363                    // Same caveat about computed vs. used as for other
364                    // metric-dependent units.
365                    reference_font_size.used_size()
366                });
367                (reference_size, length)
368            },
369            Self::Rem(length) => {
370                // https://drafts.csswg.org/css-values/#rem:
371                //
372                //     When specified on the font-size property of the root
373                //     element, the rem units refer to the property's initial
374                //     value.
375                //
376                let reference_size = if context.builder.is_root_element || context.in_media_query {
377                    reference_font_size.computed_size()
378                } else {
379                    context
380                        .device()
381                        .root_font_size()
382                        .zoom(context.builder.effective_zoom)
383                };
384                (reference_size, length)
385            },
386            Self::Lh(length) => {
387                // https://drafts.csswg.org/css-values-4/#lh
388                //
389                //     When specified in media-query, the lh units refer to the
390                //     initial values of font and line-height properties.
391                //
392                let reference_size = if context.in_media_query {
393                    context
394                        .device()
395                        .calc_line_height(
396                            &context.default_style().get_font(),
397                            context.style().writing_mode,
398                            None,
399                        )
400                        .0
401                } else {
402                    let line_height = context.builder.calc_line_height(
403                        context.device(),
404                        line_height_base,
405                        context.style().writing_mode,
406                    );
407                    if context.for_non_inherited_property &&
408                        line_height_base == LineHeightBase::CurrentStyle
409                    {
410                        context
411                            .rule_cache_conditions
412                            .borrow_mut()
413                            .set_line_height_dependency(line_height)
414                    }
415                    line_height.0
416                };
417                (reference_size, length)
418            },
419            Self::Rlh(length) => {
420                // https://drafts.csswg.org/css-values-4/#rlh
421                //
422                //     When specified on the root element, the rlh units refer
423                //     to the initial values of font and line-height properties.
424                //
425                let reference_size = if context.builder.is_root_element {
426                    context.builder
427                        .calc_line_height(
428                            context.device(),
429                            line_height_base,
430                            context.style().writing_mode,
431                        )
432                        .0
433                } else if context.in_media_query {
434                    context
435                        .device()
436                        .calc_line_height(
437                            &context.default_style().get_font(),
438                            context.style().writing_mode,
439                            None,
440                        )
441                        .0
442                } else {
443                    context.device().root_line_height()
444                };
445                let reference_size = reference_size.zoom(context.builder.effective_zoom);
446                (reference_size, length)
447            },
448        }
449    }
450}
451
452/// https://drafts.csswg.org/css-values/#viewport-variants
453pub enum ViewportVariant {
454    /// https://drafts.csswg.org/css-values/#ua-default-viewport-size
455    UADefault,
456    /// https://drafts.csswg.org/css-values/#small-viewport-percentage-units
457    Small,
458    /// https://drafts.csswg.org/css-values/#large-viewport-percentage-units
459    Large,
460    /// https://drafts.csswg.org/css-values/#dynamic-viewport-percentage-units
461    Dynamic,
462}
463
464/// https://drafts.csswg.org/css-values/#viewport-relative-units
465#[derive(PartialEq)]
466enum ViewportUnit {
467    /// *vw units.
468    Vw,
469    /// *vh units.
470    Vh,
471    /// *vmin units.
472    Vmin,
473    /// *vmax units.
474    Vmax,
475    /// *vb units.
476    Vb,
477    /// *vi units.
478    Vi,
479}
480
481/// A viewport-relative length.
482///
483/// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
484#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
485#[repr(u8)]
486pub enum ViewportPercentageLength {
487    /// <https://drafts.csswg.org/css-values/#valdef-length-vw>
488    #[css(dimension)]
489    Vw(CSSFloat),
490    /// <https://drafts.csswg.org/css-values/#valdef-length-svw>
491    #[css(dimension)]
492    Svw(CSSFloat),
493    /// <https://drafts.csswg.org/css-values/#valdef-length-lvw>
494    #[css(dimension)]
495    Lvw(CSSFloat),
496    /// <https://drafts.csswg.org/css-values/#valdef-length-dvw>
497    #[css(dimension)]
498    Dvw(CSSFloat),
499    /// <https://drafts.csswg.org/css-values/#valdef-length-vh>
500    #[css(dimension)]
501    Vh(CSSFloat),
502    /// <https://drafts.csswg.org/css-values/#valdef-length-svh>
503    #[css(dimension)]
504    Svh(CSSFloat),
505    /// <https://drafts.csswg.org/css-values/#valdef-length-lvh>
506    #[css(dimension)]
507    Lvh(CSSFloat),
508    /// <https://drafts.csswg.org/css-values/#valdef-length-dvh>
509    #[css(dimension)]
510    Dvh(CSSFloat),
511    /// <https://drafts.csswg.org/css-values/#valdef-length-vmin>
512    #[css(dimension)]
513    Vmin(CSSFloat),
514    /// <https://drafts.csswg.org/css-values/#valdef-length-svmin>
515    #[css(dimension)]
516    Svmin(CSSFloat),
517    /// <https://drafts.csswg.org/css-values/#valdef-length-lvmin>
518    #[css(dimension)]
519    Lvmin(CSSFloat),
520    /// <https://drafts.csswg.org/css-values/#valdef-length-dvmin>
521    #[css(dimension)]
522    Dvmin(CSSFloat),
523    /// <https://drafts.csswg.org/css-values/#valdef-length-vmax>
524    #[css(dimension)]
525    Vmax(CSSFloat),
526    /// <https://drafts.csswg.org/css-values/#valdef-length-svmax>
527    #[css(dimension)]
528    Svmax(CSSFloat),
529    /// <https://drafts.csswg.org/css-values/#valdef-length-lvmax>
530    #[css(dimension)]
531    Lvmax(CSSFloat),
532    /// <https://drafts.csswg.org/css-values/#valdef-length-dvmax>
533    #[css(dimension)]
534    Dvmax(CSSFloat),
535    /// <https://drafts.csswg.org/css-values/#valdef-length-vb>
536    #[css(dimension)]
537    Vb(CSSFloat),
538    /// <https://drafts.csswg.org/css-values/#valdef-length-svb>
539    #[css(dimension)]
540    Svb(CSSFloat),
541    /// <https://drafts.csswg.org/css-values/#valdef-length-lvb>
542    #[css(dimension)]
543    Lvb(CSSFloat),
544    /// <https://drafts.csswg.org/css-values/#valdef-length-dvb>
545    #[css(dimension)]
546    Dvb(CSSFloat),
547    /// <https://drafts.csswg.org/css-values/#valdef-length-vi>
548    #[css(dimension)]
549    Vi(CSSFloat),
550    /// <https://drafts.csswg.org/css-values/#valdef-length-svi>
551    #[css(dimension)]
552    Svi(CSSFloat),
553    /// <https://drafts.csswg.org/css-values/#valdef-length-lvi>
554    #[css(dimension)]
555    Lvi(CSSFloat),
556    /// <https://drafts.csswg.org/css-values/#valdef-length-dvi>
557    #[css(dimension)]
558    Dvi(CSSFloat),
559}
560
561impl ViewportPercentageLength {
562    /// Return the unitless, raw value.
563    fn unitless_value(&self) -> CSSFloat {
564        self.unpack().2
565    }
566
567    // Return the unit, as a string.
568    fn unit(&self) -> &'static str {
569        match *self {
570            Self::Vw(_) => "vw",
571            Self::Lvw(_) => "lvw",
572            Self::Svw(_) => "svw",
573            Self::Dvw(_) => "dvw",
574            Self::Vh(_) => "vh",
575            Self::Svh(_) => "svh",
576            Self::Lvh(_) => "lvh",
577            Self::Dvh(_) => "dvh",
578            Self::Vmin(_) => "vmin",
579            Self::Svmin(_) => "svmin",
580            Self::Lvmin(_) => "lvmin",
581            Self::Dvmin(_) => "dvmin",
582            Self::Vmax(_) => "vmax",
583            Self::Svmax(_) => "svmax",
584            Self::Lvmax(_) => "lvmax",
585            Self::Dvmax(_) => "dvmax",
586            Self::Vb(_) => "vb",
587            Self::Svb(_) => "svb",
588            Self::Lvb(_) => "lvb",
589            Self::Dvb(_) => "dvb",
590            Self::Vi(_) => "vi",
591            Self::Svi(_) => "svi",
592            Self::Lvi(_) => "lvi",
593            Self::Dvi(_) => "dvi",
594        }
595    }
596
597    fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) {
598        match *self {
599            Self::Vw(v) => (ViewportVariant::UADefault, ViewportUnit::Vw, v),
600            Self::Svw(v) => (ViewportVariant::Small, ViewportUnit::Vw, v),
601            Self::Lvw(v) => (ViewportVariant::Large, ViewportUnit::Vw, v),
602            Self::Dvw(v) => (ViewportVariant::Dynamic, ViewportUnit::Vw, v),
603            Self::Vh(v) => (ViewportVariant::UADefault, ViewportUnit::Vh, v),
604            Self::Svh(v) => (ViewportVariant::Small, ViewportUnit::Vh, v),
605            Self::Lvh(v) => (ViewportVariant::Large, ViewportUnit::Vh, v),
606            Self::Dvh(v) => (ViewportVariant::Dynamic, ViewportUnit::Vh, v),
607            Self::Vmin(v) => (ViewportVariant::UADefault, ViewportUnit::Vmin, v),
608            Self::Svmin(v) => (ViewportVariant::Small, ViewportUnit::Vmin, v),
609            Self::Lvmin(v) => (ViewportVariant::Large, ViewportUnit::Vmin, v),
610            Self::Dvmin(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmin, v),
611            Self::Vmax(v) => (ViewportVariant::UADefault, ViewportUnit::Vmax, v),
612            Self::Svmax(v) => (ViewportVariant::Small, ViewportUnit::Vmax, v),
613            Self::Lvmax(v) => (ViewportVariant::Large, ViewportUnit::Vmax, v),
614            Self::Dvmax(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmax, v),
615            Self::Vb(v) => (ViewportVariant::UADefault, ViewportUnit::Vb, v),
616            Self::Svb(v) => (ViewportVariant::Small, ViewportUnit::Vb, v),
617            Self::Lvb(v) => (ViewportVariant::Large, ViewportUnit::Vb, v),
618            Self::Dvb(v) => (ViewportVariant::Dynamic, ViewportUnit::Vb, v),
619            Self::Vi(v) => (ViewportVariant::UADefault, ViewportUnit::Vi, v),
620            Self::Svi(v) => (ViewportVariant::Small, ViewportUnit::Vi, v),
621            Self::Lvi(v) => (ViewportVariant::Large, ViewportUnit::Vi, v),
622            Self::Dvi(v) => (ViewportVariant::Dynamic, ViewportUnit::Vi, v),
623        }
624    }
625
626    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
627    where
628        O: Fn(f32, f32) -> f32,
629    {
630        use self::ViewportPercentageLength::*;
631
632        if std::mem::discriminant(self) != std::mem::discriminant(other) {
633            return Err(());
634        }
635
636        Ok(match (self, other) {
637            (&Vw(one), &Vw(other)) => Vw(op(one, other)),
638            (&Svw(one), &Svw(other)) => Svw(op(one, other)),
639            (&Lvw(one), &Lvw(other)) => Lvw(op(one, other)),
640            (&Dvw(one), &Dvw(other)) => Dvw(op(one, other)),
641            (&Vh(one), &Vh(other)) => Vh(op(one, other)),
642            (&Svh(one), &Svh(other)) => Svh(op(one, other)),
643            (&Lvh(one), &Lvh(other)) => Lvh(op(one, other)),
644            (&Dvh(one), &Dvh(other)) => Dvh(op(one, other)),
645            (&Vmin(one), &Vmin(other)) => Vmin(op(one, other)),
646            (&Svmin(one), &Svmin(other)) => Svmin(op(one, other)),
647            (&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)),
648            (&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)),
649            (&Vmax(one), &Vmax(other)) => Vmax(op(one, other)),
650            (&Svmax(one), &Svmax(other)) => Svmax(op(one, other)),
651            (&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)),
652            (&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)),
653            (&Vb(one), &Vb(other)) => Vb(op(one, other)),
654            (&Svb(one), &Svb(other)) => Svb(op(one, other)),
655            (&Lvb(one), &Lvb(other)) => Lvb(op(one, other)),
656            (&Dvb(one), &Dvb(other)) => Dvb(op(one, other)),
657            (&Vi(one), &Vi(other)) => Vi(op(one, other)),
658            (&Svi(one), &Svi(other)) => Svi(op(one, other)),
659            (&Lvi(one), &Lvi(other)) => Lvi(op(one, other)),
660            (&Dvi(one), &Dvi(other)) => Dvi(op(one, other)),
661            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
662            // able to figure it own on its own so we help.
663            _ => unsafe {
664                match *self {
665                    Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) |
666                    Dvh(..) | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) |
667                    Svmax(..) | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) |
668                    Vi(..) | Svi(..) | Lvi(..) | Dvi(..) => {},
669                }
670                debug_unreachable!("Forgot to handle unit in try_op()")
671            },
672        })
673    }
674
675    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
676        match self {
677            Self::Vw(x) => Self::Vw(op(*x)),
678            Self::Svw(x) => Self::Svw(op(*x)),
679            Self::Lvw(x) => Self::Lvw(op(*x)),
680            Self::Dvw(x) => Self::Dvw(op(*x)),
681            Self::Vh(x) => Self::Vh(op(*x)),
682            Self::Svh(x) => Self::Svh(op(*x)),
683            Self::Lvh(x) => Self::Lvh(op(*x)),
684            Self::Dvh(x) => Self::Dvh(op(*x)),
685            Self::Vmin(x) => Self::Vmin(op(*x)),
686            Self::Svmin(x) => Self::Svmin(op(*x)),
687            Self::Lvmin(x) => Self::Lvmin(op(*x)),
688            Self::Dvmin(x) => Self::Dvmin(op(*x)),
689            Self::Vmax(x) => Self::Vmax(op(*x)),
690            Self::Svmax(x) => Self::Svmax(op(*x)),
691            Self::Lvmax(x) => Self::Lvmax(op(*x)),
692            Self::Dvmax(x) => Self::Dvmax(op(*x)),
693            Self::Vb(x) => Self::Vb(op(*x)),
694            Self::Svb(x) => Self::Svb(op(*x)),
695            Self::Lvb(x) => Self::Lvb(op(*x)),
696            Self::Dvb(x) => Self::Dvb(op(*x)),
697            Self::Vi(x) => Self::Vi(op(*x)),
698            Self::Svi(x) => Self::Svi(op(*x)),
699            Self::Lvi(x) => Self::Lvi(op(*x)),
700            Self::Dvi(x) => Self::Dvi(op(*x)),
701        }
702    }
703
704    /// Computes the given viewport-relative length for the given viewport size.
705    pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
706        let (variant, unit, factor) = self.unpack();
707        let size = context.viewport_size_for_viewport_unit_resolution(variant);
708        let length: app_units::Au = match unit {
709            ViewportUnit::Vw => size.width,
710            ViewportUnit::Vh => size.height,
711            ViewportUnit::Vmin => cmp::min(size.width, size.height),
712            ViewportUnit::Vmax => cmp::max(size.width, size.height),
713            ViewportUnit::Vi | ViewportUnit::Vb => {
714                context
715                    .rule_cache_conditions
716                    .borrow_mut()
717                    .set_writing_mode_dependency(context.builder.writing_mode);
718                if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() {
719                    size.width
720                } else {
721                    size.height
722                }
723            },
724        };
725
726        // NOTE: This is in app units!
727        let length = context.builder.effective_zoom.zoom(length.0 as f32);
728
729        // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform.
730        // See bug 989802. We truncate so that adding multiple viewport units that add up to 100
731        // does not overflow due to rounding differences. We convert appUnits to CSS px manually
732        // here to avoid premature clamping by going through the Au type.
733        let trunc_scaled =
734            ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;
735        CSSPixelLength::new(crate::values::normalize(trunc_scaled))
736    }
737}
738
739/// HTML5 "character width", as defined in HTML5 § 14.5.4.
740#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
741#[repr(C)]
742pub struct CharacterWidth(pub i32);
743
744impl CharacterWidth {
745    /// Computes the given character width.
746    pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length {
747        // This applies the *converting a character width to pixels* algorithm
748        // as specified in HTML5 § 14.5.4.
749        //
750        // TODO(pcwalton): Find these from the font.
751        let average_advance = reference_font_size * 0.5;
752        let max_advance = reference_font_size;
753        (average_advance * (self.0 as CSSFloat - 1.0) + max_advance).finite()
754    }
755}
756
757/// Represents an absolute length with its unit
758#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
759#[repr(u8)]
760pub enum AbsoluteLength {
761    /// An absolute length in pixels (px)
762    #[css(dimension)]
763    Px(CSSFloat),
764    /// An absolute length in inches (in)
765    #[css(dimension)]
766    In(CSSFloat),
767    /// An absolute length in centimeters (cm)
768    #[css(dimension)]
769    Cm(CSSFloat),
770    /// An absolute length in millimeters (mm)
771    #[css(dimension)]
772    Mm(CSSFloat),
773    /// An absolute length in quarter-millimeters (q)
774    #[css(dimension)]
775    Q(CSSFloat),
776    /// An absolute length in points (pt)
777    #[css(dimension)]
778    Pt(CSSFloat),
779    /// An absolute length in pica (pc)
780    #[css(dimension)]
781    Pc(CSSFloat),
782}
783
784impl AbsoluteLength {
785    /// Return the unitless, raw value.
786    fn unitless_value(&self) -> CSSFloat {
787        match *self {
788            Self::Px(v) |
789            Self::In(v) |
790            Self::Cm(v) |
791            Self::Mm(v) |
792            Self::Q(v) |
793            Self::Pt(v) |
794            Self::Pc(v) => v,
795        }
796    }
797
798    // Return the unit, as a string.
799    fn unit(&self) -> &'static str {
800        match *self {
801            Self::Px(_) => "px",
802            Self::In(_) => "in",
803            Self::Cm(_) => "cm",
804            Self::Mm(_) => "mm",
805            Self::Q(_) => "q",
806            Self::Pt(_) => "pt",
807            Self::Pc(_) => "pc",
808        }
809    }
810
811    /// Convert this into a pixel value.
812    #[inline]
813    pub fn to_px(&self) -> CSSFloat {
814        match *self {
815            Self::Px(value) => value,
816            Self::In(value) => value * PX_PER_IN,
817            Self::Cm(value) => value * PX_PER_CM,
818            Self::Mm(value) => value * PX_PER_MM,
819            Self::Q(value) => value * PX_PER_Q,
820            Self::Pt(value) => value * PX_PER_PT,
821            Self::Pc(value) => value * PX_PER_PC,
822        }
823    }
824
825    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
826    where
827        O: Fn(f32, f32) -> f32,
828    {
829        Ok(Self::Px(op(self.to_px(), other.to_px())))
830    }
831
832    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
833        Self::Px(op(self.to_px()))
834    }
835}
836
837impl ToComputedValue for AbsoluteLength {
838    type ComputedValue = CSSPixelLength;
839
840    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
841        CSSPixelLength::new(self.to_px())
842            .zoom(context.builder.effective_zoom)
843            .finite()
844    }
845
846    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
847        Self::Px(computed.px())
848    }
849}
850
851impl PartialOrd for AbsoluteLength {
852    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
853        self.to_px().partial_cmp(&other.to_px())
854    }
855}
856
857/// A container query length.
858///
859/// <https://drafts.csswg.org/css-contain-3/#container-lengths>
860#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
861#[repr(u8)]
862pub enum ContainerRelativeLength {
863    /// 1% of query container's width
864    #[css(dimension)]
865    Cqw(CSSFloat),
866    /// 1% of query container's height
867    #[css(dimension)]
868    Cqh(CSSFloat),
869    /// 1% of query container's inline size
870    #[css(dimension)]
871    Cqi(CSSFloat),
872    /// 1% of query container's block size
873    #[css(dimension)]
874    Cqb(CSSFloat),
875    /// The smaller value of `cqi` or `cqb`
876    #[css(dimension)]
877    Cqmin(CSSFloat),
878    /// The larger value of `cqi` or `cqb`
879    #[css(dimension)]
880    Cqmax(CSSFloat),
881}
882
883impl ContainerRelativeLength {
884    fn unitless_value(&self) -> CSSFloat {
885        match *self {
886            Self::Cqw(v) |
887            Self::Cqh(v) |
888            Self::Cqi(v) |
889            Self::Cqb(v) |
890            Self::Cqmin(v) |
891            Self::Cqmax(v) => v,
892        }
893    }
894
895    // Return the unit, as a string.
896    fn unit(&self) -> &'static str {
897        match *self {
898            Self::Cqw(_) => "cqw",
899            Self::Cqh(_) => "cqh",
900            Self::Cqi(_) => "cqi",
901            Self::Cqb(_) => "cqb",
902            Self::Cqmin(_) => "cqmin",
903            Self::Cqmax(_) => "cqmax",
904        }
905    }
906
907    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
908    where
909        O: Fn(f32, f32) -> f32,
910    {
911        use self::ContainerRelativeLength::*;
912
913        if std::mem::discriminant(self) != std::mem::discriminant(other) {
914            return Err(());
915        }
916
917        Ok(match (self, other) {
918            (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)),
919            (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)),
920            (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)),
921            (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)),
922            (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)),
923            (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)),
924
925            // See https://github.com/rust-lang/rust/issues/68867, then
926            // https://github.com/rust-lang/rust/pull/95161. rustc isn't
927            // able to figure it own on its own so we help.
928            _ => unsafe {
929                match *self {
930                    Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
931                }
932                debug_unreachable!("Forgot to handle unit in try_op()")
933            },
934        })
935    }
936
937    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
938        match self {
939            Self::Cqw(x) => Self::Cqw(op(*x)),
940            Self::Cqh(x) => Self::Cqh(op(*x)),
941            Self::Cqi(x) => Self::Cqi(op(*x)),
942            Self::Cqb(x) => Self::Cqb(op(*x)),
943            Self::Cqmin(x) => Self::Cqmin(op(*x)),
944            Self::Cqmax(x) => Self::Cqmax(op(*x)),
945        }
946    }
947
948    /// Computes the given container-relative length.
949    pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
950        if context.for_non_inherited_property {
951            context.rule_cache_conditions.borrow_mut().set_uncacheable();
952        }
953        context
954            .builder
955            .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
956
957        // TODO(emilio, bug 1894104): Need to handle zoom here, probably something like
958        // container_zoom - effective_zoom or so. See
959        // https://github.com/w3c/csswg-drafts/issues/10268
960        let size = context.get_container_size_query();
961        let (factor, container_length) = match *self {
962            Self::Cqw(v) => (v, size.get_container_width(context)),
963            Self::Cqh(v) => (v, size.get_container_height(context)),
964            Self::Cqi(v) => (v, size.get_container_inline_size(context)),
965            Self::Cqb(v) => (v, size.get_container_block_size(context)),
966            Self::Cqmin(v) => (
967                v,
968                cmp::min(
969                    size.get_container_inline_size(context),
970                    size.get_container_block_size(context),
971                ),
972            ),
973            Self::Cqmax(v) => (
974                v,
975                cmp::max(
976                    size.get_container_inline_size(context),
977                    size.get_container_block_size(context),
978                ),
979            ),
980        };
981        CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()
982    }
983}
984
985/// A `<length>` without taking `calc` expressions into account
986///
987/// <https://drafts.csswg.org/css-values/#lengths>
988#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
989#[repr(u8)]
990pub enum NoCalcLength {
991    /// An absolute length
992    ///
993    /// <https://drafts.csswg.org/css-values/#absolute-length>
994    Absolute(AbsoluteLength),
995
996    /// A font-relative length:
997    ///
998    /// <https://drafts.csswg.org/css-values/#font-relative-lengths>
999    FontRelative(FontRelativeLength),
1000
1001    /// A viewport-relative length.
1002    ///
1003    /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
1004    ViewportPercentage(ViewportPercentageLength),
1005
1006    /// A container query length.
1007    ///
1008    /// <https://drafts.csswg.org/css-contain-3/#container-lengths>
1009    ContainerRelative(ContainerRelativeLength),
1010    /// HTML5 "character width", as defined in HTML5 § 14.5.4.
1011    ///
1012    /// This cannot be specified by the user directly and is only generated by
1013    /// `Stylist::synthesize_rules_for_legacy_attributes()`.
1014    ServoCharacterWidth(CharacterWidth),
1015}
1016
1017impl NoCalcLength {
1018    /// Return the unitless, raw value.
1019    pub fn unitless_value(&self) -> CSSFloat {
1020        match *self {
1021            Self::Absolute(v) => v.unitless_value(),
1022            Self::FontRelative(v) => v.unitless_value(),
1023            Self::ViewportPercentage(v) => v.unitless_value(),
1024            Self::ContainerRelative(v) => v.unitless_value(),
1025            Self::ServoCharacterWidth(c) => c.0 as f32,
1026        }
1027    }
1028
1029    // Return the unit, as a string.
1030    fn unit(&self) -> &'static str {
1031        match *self {
1032            Self::Absolute(v) => v.unit(),
1033            Self::FontRelative(v) => v.unit(),
1034            Self::ViewportPercentage(v) => v.unit(),
1035            Self::ContainerRelative(v) => v.unit(),
1036            Self::ServoCharacterWidth(_) => "",
1037        }
1038    }
1039
1040    /// Returns whether the value of this length without unit is less than zero.
1041    pub fn is_negative(&self) -> bool {
1042        self.unitless_value().is_sign_negative()
1043    }
1044
1045    /// Returns whether the value of this length without unit is equal to zero.
1046    pub fn is_zero(&self) -> bool {
1047        self.unitless_value() == 0.0
1048    }
1049
1050    /// Returns whether the value of this length without unit is infinite.
1051    pub fn is_infinite(&self) -> bool {
1052        self.unitless_value().is_infinite()
1053    }
1054
1055    /// Returns whether the value of this length without unit is NaN.
1056    pub fn is_nan(&self) -> bool {
1057        self.unitless_value().is_nan()
1058    }
1059
1060    /// Whether text-only zoom should be applied to this length.
1061    ///
1062    /// Generally, font-dependent/relative units don't get text-only-zoomed,
1063    /// because the font they're relative to should be zoomed already.
1064    pub fn should_zoom_text(&self) -> bool {
1065        match *self {
1066            Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true,
1067            Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false,
1068        }
1069    }
1070
1071    /// Parse a given absolute or relative dimension.
1072    pub fn parse_dimension(
1073        context: &ParserContext,
1074        value: CSSFloat,
1075        unit: &str,
1076    ) -> Result<Self, ()> {
1077        Ok(match_ignore_ascii_case! { unit,
1078            "px" => Self::Absolute(AbsoluteLength::Px(value)),
1079            "in" => Self::Absolute(AbsoluteLength::In(value)),
1080            "cm" => Self::Absolute(AbsoluteLength::Cm(value)),
1081            "mm" => Self::Absolute(AbsoluteLength::Mm(value)),
1082            "q" => Self::Absolute(AbsoluteLength::Q(value)),
1083            "pt" => Self::Absolute(AbsoluteLength::Pt(value)),
1084            "pc" => Self::Absolute(AbsoluteLength::Pc(value)),
1085            // font-relative
1086            "em" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Em(value)),
1087            "ex" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ex(value)),
1088            "ch" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ch(value)),
1089            "cap" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Cap(value)),
1090            "ic" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ic(value)),
1091            "rem" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rem(value)),
1092            "lh" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Lh(value)),
1093            "rlh" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rlh(value)),
1094            // viewport percentages
1095            "vw" if !context.in_page_rule() => {
1096                Self::ViewportPercentage(ViewportPercentageLength::Vw(value))
1097            },
1098            "svw" if !context.in_page_rule() => {
1099                Self::ViewportPercentage(ViewportPercentageLength::Svw(value))
1100            },
1101            "lvw" if !context.in_page_rule() => {
1102                Self::ViewportPercentage(ViewportPercentageLength::Lvw(value))
1103            },
1104            "dvw" if !context.in_page_rule() => {
1105                Self::ViewportPercentage(ViewportPercentageLength::Dvw(value))
1106            },
1107            "vh" if !context.in_page_rule() => {
1108                Self::ViewportPercentage(ViewportPercentageLength::Vh(value))
1109            },
1110            "svh" if !context.in_page_rule() => {
1111                Self::ViewportPercentage(ViewportPercentageLength::Svh(value))
1112            },
1113            "lvh" if !context.in_page_rule() => {
1114                Self::ViewportPercentage(ViewportPercentageLength::Lvh(value))
1115            },
1116            "dvh" if !context.in_page_rule() => {
1117                Self::ViewportPercentage(ViewportPercentageLength::Dvh(value))
1118            },
1119            "vmin" if !context.in_page_rule() => {
1120                Self::ViewportPercentage(ViewportPercentageLength::Vmin(value))
1121            },
1122            "svmin" if !context.in_page_rule() => {
1123                Self::ViewportPercentage(ViewportPercentageLength::Svmin(value))
1124            },
1125            "lvmin" if !context.in_page_rule() => {
1126                Self::ViewportPercentage(ViewportPercentageLength::Lvmin(value))
1127            },
1128            "dvmin" if !context.in_page_rule() => {
1129                Self::ViewportPercentage(ViewportPercentageLength::Dvmin(value))
1130            },
1131            "vmax" if !context.in_page_rule() => {
1132                Self::ViewportPercentage(ViewportPercentageLength::Vmax(value))
1133            },
1134            "svmax" if !context.in_page_rule() => {
1135                Self::ViewportPercentage(ViewportPercentageLength::Svmax(value))
1136            },
1137            "lvmax" if !context.in_page_rule() => {
1138                Self::ViewportPercentage(ViewportPercentageLength::Lvmax(value))
1139            },
1140            "dvmax" if !context.in_page_rule() => {
1141                Self::ViewportPercentage(ViewportPercentageLength::Dvmax(value))
1142            },
1143            "vb" if !context.in_page_rule() => {
1144                Self::ViewportPercentage(ViewportPercentageLength::Vb(value))
1145            },
1146            "svb" if !context.in_page_rule() => {
1147                Self::ViewportPercentage(ViewportPercentageLength::Svb(value))
1148            },
1149            "lvb" if !context.in_page_rule() => {
1150                Self::ViewportPercentage(ViewportPercentageLength::Lvb(value))
1151            },
1152            "dvb" if !context.in_page_rule() => {
1153                Self::ViewportPercentage(ViewportPercentageLength::Dvb(value))
1154            },
1155            "vi" if !context.in_page_rule() => {
1156                Self::ViewportPercentage(ViewportPercentageLength::Vi(value))
1157            },
1158            "svi" if !context.in_page_rule() => {
1159                Self::ViewportPercentage(ViewportPercentageLength::Svi(value))
1160            },
1161            "lvi" if !context.in_page_rule() => {
1162                Self::ViewportPercentage(ViewportPercentageLength::Lvi(value))
1163            },
1164            "dvi" if !context.in_page_rule() => {
1165                Self::ViewportPercentage(ViewportPercentageLength::Dvi(value))
1166            },
1167            // Container query lengths. Inherit the limitation from viewport units since
1168            // we may fall back to them.
1169            "cqw" if !context.in_page_rule() && cfg!(feature = "gecko") => {
1170                Self::ContainerRelative(ContainerRelativeLength::Cqw(value))
1171            },
1172            "cqh" if !context.in_page_rule() && cfg!(feature = "gecko") => {
1173                Self::ContainerRelative(ContainerRelativeLength::Cqh(value))
1174            },
1175            "cqi" if !context.in_page_rule() && cfg!(feature = "gecko") => {
1176                Self::ContainerRelative(ContainerRelativeLength::Cqi(value))
1177            },
1178            "cqb" if !context.in_page_rule() && cfg!(feature = "gecko") => {
1179                Self::ContainerRelative(ContainerRelativeLength::Cqb(value))
1180            },
1181            "cqmin" if !context.in_page_rule() && cfg!(feature = "gecko") => {
1182                Self::ContainerRelative(ContainerRelativeLength::Cqmin(value))
1183            },
1184            "cqmax" if !context.in_page_rule() && cfg!(feature = "gecko") => {
1185                Self::ContainerRelative(ContainerRelativeLength::Cqmax(value))
1186            },
1187            _ => return Err(()),
1188        })
1189    }
1190
1191    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
1192    where
1193        O: Fn(f32, f32) -> f32,
1194    {
1195        use self::NoCalcLength::*;
1196
1197        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1198            return Err(());
1199        }
1200
1201        Ok(match (self, other) {
1202            (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?),
1203            (&FontRelative(ref one), &FontRelative(ref other)) => {
1204                FontRelative(one.try_op(other, op)?)
1205            },
1206            (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
1207                ViewportPercentage(one.try_op(other, op)?)
1208            },
1209            (&ContainerRelative(ref one), &ContainerRelative(ref other)) => {
1210                ContainerRelative(one.try_op(other, op)?)
1211            },
1212            (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
1213                ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32))
1214            },
1215            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
1216            // able to figure it own on its own so we help.
1217            _ => unsafe {
1218                match *self {
1219                    Absolute(..) |
1220                    FontRelative(..) |
1221                    ViewportPercentage(..) |
1222                    ContainerRelative(..) |
1223                    ServoCharacterWidth(..) => {},
1224                }
1225                debug_unreachable!("Forgot to handle unit in try_op()")
1226            },
1227        })
1228    }
1229
1230    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
1231        use self::NoCalcLength::*;
1232
1233        match self {
1234            Absolute(ref one) => Absolute(one.map(op)),
1235            FontRelative(ref one) => FontRelative(one.map(op)),
1236            ViewportPercentage(ref one) => ViewportPercentage(one.map(op)),
1237            ContainerRelative(ref one) => ContainerRelative(one.map(op)),
1238            ServoCharacterWidth(ref one) => {
1239                ServoCharacterWidth(CharacterWidth(op(one.0 as f32) as i32))
1240            },
1241        }
1242    }
1243
1244    /// Get a px value without context (so only absolute units can be handled).
1245    #[inline]
1246    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1247        match *self {
1248            Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
1249            _ => Err(()),
1250        }
1251    }
1252
1253    /// Get a px value without a full style context; this can handle either
1254    /// absolute or (if a font metrics getter is provided) font-relative units.
1255    #[cfg(feature = "gecko")]
1256    #[inline]
1257    pub fn to_computed_pixel_length_with_font_metrics(
1258        &self,
1259        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1260    ) -> Result<CSSFloat, ()> {
1261        match *self {
1262            Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
1263            Self::FontRelative(fr) => {
1264                if let Some(getter) = get_font_metrics {
1265                    fr.to_computed_pixel_length_with_font_metrics(getter)
1266                } else {
1267                    Err(())
1268                }
1269            },
1270            _ => Err(()),
1271        }
1272    }
1273
1274    /// Get an absolute length from a px value.
1275    #[inline]
1276    pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
1277        NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
1278    }
1279}
1280
1281impl ToCss for NoCalcLength {
1282    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1283    where
1284        W: Write,
1285    {
1286        crate::values::serialize_specified_dimension(
1287            self.unitless_value(),
1288            self.unit(),
1289            false,
1290            dest,
1291        )
1292    }
1293}
1294
1295impl SpecifiedValueInfo for NoCalcLength {}
1296
1297impl PartialOrd for NoCalcLength {
1298    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1299        use self::NoCalcLength::*;
1300
1301        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1302            return None;
1303        }
1304
1305        match (self, other) {
1306            (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),
1307            (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),
1308            (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
1309                one.partial_cmp(other)
1310            },
1311            (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other),
1312            (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
1313                one.0.partial_cmp(&other.0)
1314            },
1315            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
1316            // able to figure it own on its own so we help.
1317            _ => unsafe {
1318                match *self {
1319                    Absolute(..) |
1320                    FontRelative(..) |
1321                    ViewportPercentage(..) |
1322                    ContainerRelative(..) |
1323                    ServoCharacterWidth(..) => {},
1324                }
1325                debug_unreachable!("Forgot an arm in partial_cmp?")
1326            },
1327        }
1328    }
1329}
1330
1331impl Zero for NoCalcLength {
1332    fn zero() -> Self {
1333        NoCalcLength::Absolute(AbsoluteLength::Px(0.))
1334    }
1335
1336    fn is_zero(&self) -> bool {
1337        NoCalcLength::is_zero(self)
1338    }
1339}
1340
1341/// An extension to `NoCalcLength` to parse `calc` expressions.
1342/// This is commonly used for the `<length>` values.
1343///
1344/// <https://drafts.csswg.org/css-values/#lengths>
1345#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1346pub enum Length {
1347    /// The internal length type that cannot parse `calc`
1348    NoCalc(NoCalcLength),
1349    /// A calc expression.
1350    ///
1351    /// <https://drafts.csswg.org/css-values/#calc-notation>
1352    Calc(Box<CalcLengthPercentage>),
1353}
1354
1355impl From<NoCalcLength> for Length {
1356    #[inline]
1357    fn from(len: NoCalcLength) -> Self {
1358        Length::NoCalc(len)
1359    }
1360}
1361
1362impl PartialOrd for FontRelativeLength {
1363    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1364        use self::FontRelativeLength::*;
1365
1366        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1367            return None;
1368        }
1369
1370        match (self, other) {
1371            (&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
1372            (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
1373            (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
1374            (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),
1375            (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other),
1376            (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
1377            (&Lh(ref one), &Lh(ref other)) => one.partial_cmp(other),
1378            (&Rlh(ref one), &Rlh(ref other)) => one.partial_cmp(other),
1379            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
1380            // able to figure it own on its own so we help.
1381            _ => unsafe {
1382                match *self {
1383                    Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) | Lh(..) | Rlh(..) => {},
1384                }
1385                debug_unreachable!("Forgot an arm in partial_cmp?")
1386            },
1387        }
1388    }
1389}
1390
1391impl PartialOrd for ContainerRelativeLength {
1392    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1393        use self::ContainerRelativeLength::*;
1394
1395        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1396            return None;
1397        }
1398
1399        match (self, other) {
1400            (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other),
1401            (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other),
1402            (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other),
1403            (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other),
1404            (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other),
1405            (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other),
1406
1407            // See https://github.com/rust-lang/rust/issues/68867, then
1408            // https://github.com/rust-lang/rust/pull/95161. rustc isn't
1409            // able to figure it own on its own so we help.
1410            _ => unsafe {
1411                match *self {
1412                    Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
1413                }
1414                debug_unreachable!("Forgot to handle unit in partial_cmp()")
1415            },
1416        }
1417    }
1418}
1419
1420impl PartialOrd for ViewportPercentageLength {
1421    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1422        use self::ViewportPercentageLength::*;
1423
1424        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1425            return None;
1426        }
1427
1428        match (self, other) {
1429            (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),
1430            (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other),
1431            (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other),
1432            (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other),
1433            (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),
1434            (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other),
1435            (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other),
1436            (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other),
1437            (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),
1438            (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other),
1439            (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other),
1440            (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other),
1441            (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),
1442            (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other),
1443            (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other),
1444            (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other),
1445            (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other),
1446            (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other),
1447            (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other),
1448            (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other),
1449            (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other),
1450            (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other),
1451            (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other),
1452            (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other),
1453            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
1454            // able to figure it own on its own so we help.
1455            _ => unsafe {
1456                match *self {
1457                    Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) |
1458                    Dvh(..) | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) |
1459                    Svmax(..) | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) |
1460                    Vi(..) | Svi(..) | Lvi(..) | Dvi(..) => {},
1461                }
1462                debug_unreachable!("Forgot an arm in partial_cmp?")
1463            },
1464        }
1465    }
1466}
1467
1468impl Length {
1469    #[inline]
1470    fn parse_internal<'i, 't>(
1471        context: &ParserContext,
1472        input: &mut Parser<'i, 't>,
1473        num_context: AllowedNumericType,
1474        allow_quirks: AllowQuirks,
1475    ) -> Result<Self, ParseError<'i>> {
1476        let location = input.current_source_location();
1477        let token = input.next()?;
1478        match *token {
1479            Token::Dimension {
1480                value, ref unit, ..
1481            } if num_context.is_ok(context.parsing_mode, value) => {
1482                NoCalcLength::parse_dimension(context, value, unit)
1483                    .map(Length::NoCalc)
1484                    .map_err(|()| location.new_unexpected_token_error(token.clone()))
1485            },
1486            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1487                if value != 0. &&
1488                    !context.parsing_mode.allows_unitless_lengths() &&
1489                    !allow_quirks.allowed(context.quirks_mode)
1490                {
1491                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1492                }
1493                Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
1494                    value,
1495                ))))
1496            },
1497            Token::Function(ref name) => {
1498                let function = CalcNode::math_function(context, name, location)?;
1499                let calc = CalcNode::parse_length(context, input, num_context, function)?;
1500                Ok(Length::Calc(Box::new(calc)))
1501            },
1502            ref token => return Err(location.new_unexpected_token_error(token.clone())),
1503        }
1504    }
1505
1506    /// Parse a non-negative length
1507    #[inline]
1508    pub fn parse_non_negative<'i, 't>(
1509        context: &ParserContext,
1510        input: &mut Parser<'i, 't>,
1511    ) -> Result<Self, ParseError<'i>> {
1512        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1513    }
1514
1515    /// Parse a non-negative length, allowing quirks.
1516    #[inline]
1517    pub fn parse_non_negative_quirky<'i, 't>(
1518        context: &ParserContext,
1519        input: &mut Parser<'i, 't>,
1520        allow_quirks: AllowQuirks,
1521    ) -> Result<Self, ParseError<'i>> {
1522        Self::parse_internal(
1523            context,
1524            input,
1525            AllowedNumericType::NonNegative,
1526            allow_quirks,
1527        )
1528    }
1529
1530    /// Get an absolute length from a px value.
1531    #[inline]
1532    pub fn from_px(px_value: CSSFloat) -> Length {
1533        Length::NoCalc(NoCalcLength::from_px(px_value))
1534    }
1535
1536    /// Get a px value without context.
1537    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1538        match *self {
1539            Self::NoCalc(ref l) => l.to_computed_pixel_length_without_context(),
1540            Self::Calc(ref l) => l.to_computed_pixel_length_without_context(),
1541        }
1542    }
1543
1544    /// Get a px value, with an optional GeckoFontMetrics getter to resolve font-relative units.
1545    #[cfg(feature = "gecko")]
1546    pub fn to_computed_pixel_length_with_font_metrics(
1547        &self,
1548        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1549    ) -> Result<CSSFloat, ()> {
1550        match *self {
1551            Self::NoCalc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
1552            Self::Calc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
1553        }
1554    }
1555}
1556
1557impl Parse for Length {
1558    fn parse<'i, 't>(
1559        context: &ParserContext,
1560        input: &mut Parser<'i, 't>,
1561    ) -> Result<Self, ParseError<'i>> {
1562        Self::parse_quirky(context, input, AllowQuirks::No)
1563    }
1564}
1565
1566impl Zero for Length {
1567    fn zero() -> Self {
1568        Length::NoCalc(NoCalcLength::zero())
1569    }
1570
1571    fn is_zero(&self) -> bool {
1572        // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
1573        // non-zero here?
1574        match *self {
1575            Length::NoCalc(ref l) => l.is_zero(),
1576            Length::Calc(..) => false,
1577        }
1578    }
1579}
1580
1581impl Length {
1582    /// Parses a length, with quirks.
1583    pub fn parse_quirky<'i, 't>(
1584        context: &ParserContext,
1585        input: &mut Parser<'i, 't>,
1586        allow_quirks: AllowQuirks,
1587    ) -> Result<Self, ParseError<'i>> {
1588        Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
1589    }
1590}
1591
1592/// A wrapper of Length, whose value must be >= 0.
1593pub type NonNegativeLength = NonNegative<Length>;
1594
1595impl Parse for NonNegativeLength {
1596    #[inline]
1597    fn parse<'i, 't>(
1598        context: &ParserContext,
1599        input: &mut Parser<'i, 't>,
1600    ) -> Result<Self, ParseError<'i>> {
1601        Ok(NonNegative(Length::parse_non_negative(context, input)?))
1602    }
1603}
1604
1605impl From<NoCalcLength> for NonNegativeLength {
1606    #[inline]
1607    fn from(len: NoCalcLength) -> Self {
1608        NonNegative(Length::NoCalc(len))
1609    }
1610}
1611
1612impl From<Length> for NonNegativeLength {
1613    #[inline]
1614    fn from(len: Length) -> Self {
1615        NonNegative(len)
1616    }
1617}
1618
1619impl NonNegativeLength {
1620    /// Get an absolute length from a px value.
1621    #[inline]
1622    pub fn from_px(px_value: CSSFloat) -> Self {
1623        Length::from_px(px_value.max(0.)).into()
1624    }
1625
1626    /// Parses a non-negative length, optionally with quirks.
1627    #[inline]
1628    pub fn parse_quirky<'i, 't>(
1629        context: &ParserContext,
1630        input: &mut Parser<'i, 't>,
1631        allow_quirks: AllowQuirks,
1632    ) -> Result<Self, ParseError<'i>> {
1633        Ok(NonNegative(Length::parse_non_negative_quirky(
1634            context,
1635            input,
1636            allow_quirks,
1637        )?))
1638    }
1639}
1640
1641/// A `<length-percentage>` value. This can be either a `<length>`, a
1642/// `<percentage>`, or a combination of both via `calc()`.
1643///
1644/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
1645#[allow(missing_docs)]
1646#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1647pub enum LengthPercentage {
1648    Length(NoCalcLength),
1649    Percentage(computed::Percentage),
1650    Calc(Box<CalcLengthPercentage>),
1651}
1652
1653impl From<Length> for LengthPercentage {
1654    fn from(len: Length) -> LengthPercentage {
1655        match len {
1656            Length::NoCalc(l) => LengthPercentage::Length(l),
1657            Length::Calc(l) => LengthPercentage::Calc(l),
1658        }
1659    }
1660}
1661
1662impl From<NoCalcLength> for LengthPercentage {
1663    #[inline]
1664    fn from(len: NoCalcLength) -> Self {
1665        LengthPercentage::Length(len)
1666    }
1667}
1668
1669impl From<Percentage> for LengthPercentage {
1670    #[inline]
1671    fn from(pc: Percentage) -> Self {
1672        if let Some(clamping_mode) = pc.calc_clamping_mode() {
1673            LengthPercentage::Calc(Box::new(CalcLengthPercentage {
1674                clamping_mode,
1675                node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
1676            }))
1677        } else {
1678            LengthPercentage::Percentage(computed::Percentage(pc.get()))
1679        }
1680    }
1681}
1682
1683impl From<computed::Percentage> for LengthPercentage {
1684    #[inline]
1685    fn from(pc: computed::Percentage) -> Self {
1686        LengthPercentage::Percentage(pc)
1687    }
1688}
1689
1690impl Parse for LengthPercentage {
1691    #[inline]
1692    fn parse<'i, 't>(
1693        context: &ParserContext,
1694        input: &mut Parser<'i, 't>,
1695    ) -> Result<Self, ParseError<'i>> {
1696        Self::parse_quirky(context, input, AllowQuirks::No)
1697    }
1698}
1699
1700impl LengthPercentage {
1701    #[inline]
1702    /// Returns a `0%` value.
1703    pub fn zero_percent() -> LengthPercentage {
1704        LengthPercentage::Percentage(computed::Percentage::zero())
1705    }
1706
1707    #[inline]
1708    /// Returns a `100%` value.
1709    pub fn hundred_percent() -> LengthPercentage {
1710        LengthPercentage::Percentage(computed::Percentage::hundred())
1711    }
1712
1713    fn parse_internal<'i, 't>(
1714        context: &ParserContext,
1715        input: &mut Parser<'i, 't>,
1716        num_context: AllowedNumericType,
1717        allow_quirks: AllowQuirks,
1718        allow_anchor: AllowAnchorPositioningFunctions,
1719    ) -> Result<Self, ParseError<'i>> {
1720        let location = input.current_source_location();
1721        let token = input.next()?;
1722        match *token {
1723            Token::Dimension {
1724                value, ref unit, ..
1725            } if num_context.is_ok(context.parsing_mode, value) => {
1726                return NoCalcLength::parse_dimension(context, value, unit)
1727                    .map(LengthPercentage::Length)
1728                    .map_err(|()| location.new_unexpected_token_error(token.clone()));
1729            },
1730            Token::Percentage { unit_value, .. }
1731                if num_context.is_ok(context.parsing_mode, unit_value) =>
1732            {
1733                return Ok(LengthPercentage::Percentage(computed::Percentage(
1734                    unit_value,
1735                )));
1736            },
1737            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1738                if value != 0. &&
1739                    !context.parsing_mode.allows_unitless_lengths() &&
1740                    !allow_quirks.allowed(context.quirks_mode)
1741                {
1742                    return Err(location.new_unexpected_token_error(token.clone()));
1743                } else {
1744                    return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
1745                }
1746            },
1747            Token::Function(ref name) => {
1748                let function = CalcNode::math_function(context, name, location)?;
1749                let calc = CalcNode::parse_length_or_percentage(
1750                    context,
1751                    input,
1752                    num_context,
1753                    function,
1754                    allow_anchor,
1755                )?;
1756                Ok(LengthPercentage::Calc(Box::new(calc)))
1757            },
1758            _ => return Err(location.new_unexpected_token_error(token.clone())),
1759        }
1760    }
1761
1762    /// Parses allowing the unitless length quirk.
1763    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1764    #[inline]
1765    pub fn parse_quirky<'i, 't>(
1766        context: &ParserContext,
1767        input: &mut Parser<'i, 't>,
1768        allow_quirks: AllowQuirks,
1769    ) -> Result<Self, ParseError<'i>> {
1770        Self::parse_internal(
1771            context,
1772            input,
1773            AllowedNumericType::All,
1774            allow_quirks,
1775            AllowAnchorPositioningFunctions::No,
1776        )
1777    }
1778
1779    /// Parses allowing the unitless length quirk, as well as allowing
1780    /// anchor-positioning related function, `anchor-size()`.
1781    #[inline]
1782    fn parse_quirky_with_anchor_size_function<'i, 't>(
1783        context: &ParserContext,
1784        input: &mut Parser<'i, 't>,
1785        allow_quirks: AllowQuirks,
1786    ) -> Result<Self, ParseError<'i>> {
1787        Self::parse_internal(
1788            context,
1789            input,
1790            AllowedNumericType::All,
1791            allow_quirks,
1792            AllowAnchorPositioningFunctions::AllowAnchorSize,
1793        )
1794    }
1795
1796    /// Parses allowing the unitless length quirk, as well as allowing
1797    /// anchor-positioning related functions, `anchor()` and `anchor-size()`.
1798    #[inline]
1799    pub fn parse_quirky_with_anchor_functions<'i, 't>(
1800        context: &ParserContext,
1801        input: &mut Parser<'i, 't>,
1802        allow_quirks: AllowQuirks,
1803    ) -> Result<Self, ParseError<'i>> {
1804        Self::parse_internal(
1805            context,
1806            input,
1807            AllowedNumericType::All,
1808            allow_quirks,
1809            AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,
1810        )
1811    }
1812
1813    /// Parses non-negative length, allowing the unitless length quirk,
1814    /// as well as allowing `anchor-size()`.
1815    pub fn parse_non_negative_with_anchor_size<'i, 't>(
1816        context: &ParserContext,
1817        input: &mut Parser<'i, 't>,
1818        allow_quirks: AllowQuirks,
1819    ) -> Result<Self, ParseError<'i>> {
1820        Self::parse_internal(
1821            context,
1822            input,
1823            AllowedNumericType::NonNegative,
1824            allow_quirks,
1825            AllowAnchorPositioningFunctions::AllowAnchorSize,
1826        )
1827    }
1828
1829    /// Parse a non-negative length.
1830    ///
1831    /// FIXME(emilio): This should be not public and we should use
1832    /// NonNegativeLengthPercentage instead.
1833    #[inline]
1834    pub fn parse_non_negative<'i, 't>(
1835        context: &ParserContext,
1836        input: &mut Parser<'i, 't>,
1837    ) -> Result<Self, ParseError<'i>> {
1838        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1839    }
1840
1841    /// Parse a non-negative length, with quirks.
1842    #[inline]
1843    pub fn parse_non_negative_quirky<'i, 't>(
1844        context: &ParserContext,
1845        input: &mut Parser<'i, 't>,
1846        allow_quirks: AllowQuirks,
1847    ) -> Result<Self, ParseError<'i>> {
1848        Self::parse_internal(
1849            context,
1850            input,
1851            AllowedNumericType::NonNegative,
1852            allow_quirks,
1853            AllowAnchorPositioningFunctions::No,
1854        )
1855    }
1856
1857    /// Returns self as specified::calc::CalcNode.
1858    /// Note that this expect the clamping_mode is AllowedNumericType::All for Calc. The caller
1859    /// should take care about it when using this function.
1860    fn to_calc_node(self) -> CalcNode {
1861        match self {
1862            LengthPercentage::Length(l) => CalcNode::Leaf(calc::Leaf::Length(l)),
1863            LengthPercentage::Percentage(p) => CalcNode::Leaf(calc::Leaf::Percentage(p.0)),
1864            LengthPercentage::Calc(p) => p.node,
1865        }
1866    }
1867
1868    /// Construct the value representing `calc(100% - self)`.
1869    pub fn hundred_percent_minus(self, clamping_mode: AllowedNumericType) -> Self {
1870        let mut sum = smallvec::SmallVec::<[CalcNode; 2]>::new();
1871        sum.push(CalcNode::Leaf(calc::Leaf::Percentage(1.0)));
1872
1873        let mut node = self.to_calc_node();
1874        node.negate();
1875        sum.push(node);
1876
1877        let calc = CalcNode::Sum(sum.into_boxed_slice().into());
1878        LengthPercentage::Calc(Box::new(
1879            calc.into_length_or_percentage(clamping_mode).unwrap(),
1880        ))
1881    }
1882}
1883
1884impl Zero for LengthPercentage {
1885    fn zero() -> Self {
1886        LengthPercentage::Length(NoCalcLength::zero())
1887    }
1888
1889    fn is_zero(&self) -> bool {
1890        match *self {
1891            LengthPercentage::Length(l) => l.is_zero(),
1892            LengthPercentage::Percentage(p) => p.0 == 0.0,
1893            LengthPercentage::Calc(_) => false,
1894        }
1895    }
1896}
1897
1898impl ZeroNoPercent for LengthPercentage {
1899    fn is_zero_no_percent(&self) -> bool {
1900        match *self {
1901            LengthPercentage::Percentage(_) => false,
1902            _ => self.is_zero(),
1903        }
1904    }
1905}
1906
1907/// A specified type for `<length-percentage> | auto`.
1908pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
1909
1910impl LengthPercentageOrAuto {
1911    /// Returns a value representing `0%`.
1912    #[inline]
1913    pub fn zero_percent() -> Self {
1914        generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
1915    }
1916
1917    /// Parses a length or a percentage, allowing the unitless length quirk.
1918    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1919    #[inline]
1920    pub fn parse_quirky<'i, 't>(
1921        context: &ParserContext,
1922        input: &mut Parser<'i, 't>,
1923        allow_quirks: AllowQuirks,
1924    ) -> Result<Self, ParseError<'i>> {
1925        Self::parse_with(context, input, |context, input| {
1926            LengthPercentage::parse_quirky(context, input, allow_quirks)
1927        })
1928    }
1929}
1930
1931/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
1932pub type NonNegativeLengthPercentageOrAuto =
1933    generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
1934
1935impl NonNegativeLengthPercentageOrAuto {
1936    /// Returns a value representing `0%`.
1937    #[inline]
1938    pub fn zero_percent() -> Self {
1939        generics::LengthPercentageOrAuto::LengthPercentage(
1940            NonNegativeLengthPercentage::zero_percent(),
1941        )
1942    }
1943
1944    /// Parses a non-negative length-percentage, allowing the unitless length
1945    /// quirk.
1946    #[inline]
1947    pub fn parse_quirky<'i, 't>(
1948        context: &ParserContext,
1949        input: &mut Parser<'i, 't>,
1950        allow_quirks: AllowQuirks,
1951    ) -> Result<Self, ParseError<'i>> {
1952        Self::parse_with(context, input, |context, input| {
1953            NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
1954        })
1955    }
1956}
1957
1958/// A wrapper of LengthPercentage, whose value must be >= 0.
1959pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
1960
1961/// Either a NonNegativeLengthPercentage or the `normal` keyword.
1962pub type NonNegativeLengthPercentageOrNormal =
1963    GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
1964
1965impl From<NoCalcLength> for NonNegativeLengthPercentage {
1966    #[inline]
1967    fn from(len: NoCalcLength) -> Self {
1968        NonNegative(LengthPercentage::from(len))
1969    }
1970}
1971
1972impl Parse for NonNegativeLengthPercentage {
1973    #[inline]
1974    fn parse<'i, 't>(
1975        context: &ParserContext,
1976        input: &mut Parser<'i, 't>,
1977    ) -> Result<Self, ParseError<'i>> {
1978        Self::parse_quirky(context, input, AllowQuirks::No)
1979    }
1980}
1981
1982impl NonNegativeLengthPercentage {
1983    #[inline]
1984    /// Returns a `0%` value.
1985    pub fn zero_percent() -> Self {
1986        NonNegative(LengthPercentage::zero_percent())
1987    }
1988
1989    /// Parses a length or a percentage, allowing the unitless length quirk.
1990    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1991    #[inline]
1992    pub fn parse_quirky<'i, 't>(
1993        context: &ParserContext,
1994        input: &mut Parser<'i, 't>,
1995        allow_quirks: AllowQuirks,
1996    ) -> Result<Self, ParseError<'i>> {
1997        LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
1998    }
1999
2000    /// Parses a length or a percentage, allowing the unitless length quirk,
2001    /// as well as allowing `anchor-size()`.
2002    #[inline]
2003    pub fn parse_non_negative_with_anchor_size<'i, 't>(
2004        context: &ParserContext,
2005        input: &mut Parser<'i, 't>,
2006        allow_quirks: AllowQuirks,
2007    ) -> Result<Self, ParseError<'i>> {
2008        LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)
2009            .map(NonNegative)
2010    }
2011}
2012
2013/// Either a `<length>` or the `auto` keyword.
2014///
2015/// Note that we use LengthPercentage just for convenience, since it pretty much
2016/// is everything we care about, but we could just add a similar LengthOrAuto
2017/// instead if we think getting rid of this weirdness is worth it.
2018pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
2019
2020impl LengthOrAuto {
2021    /// Parses a length, allowing the unitless length quirk.
2022    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
2023    #[inline]
2024    pub fn parse_quirky<'i, 't>(
2025        context: &ParserContext,
2026        input: &mut Parser<'i, 't>,
2027        allow_quirks: AllowQuirks,
2028    ) -> Result<Self, ParseError<'i>> {
2029        Self::parse_with(context, input, |context, input| {
2030            Length::parse_quirky(context, input, allow_quirks)
2031        })
2032    }
2033}
2034
2035/// Either a non-negative `<length>` or the `auto` keyword.
2036pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
2037
2038/// Either a `<length>` or a `<number>`.
2039pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
2040
2041/// A specified value for `min-width`, `min-height`, `width` or `height` property.
2042pub type Size = GenericSize<NonNegativeLengthPercentage>;
2043
2044impl Parse for Size {
2045    fn parse<'i, 't>(
2046        context: &ParserContext,
2047        input: &mut Parser<'i, 't>,
2048    ) -> Result<Self, ParseError<'i>> {
2049        Size::parse_quirky(context, input, AllowQuirks::No)
2050    }
2051}
2052
2053macro_rules! parse_size_non_length {
2054    ($size:ident, $input:expr, $auto_or_none:expr => $auto_or_none_ident:ident) => {{
2055        let size = $input.try_parse(|input| {
2056            Ok(try_match_ident_ignore_ascii_case! { input,
2057                "min-content" | "-moz-min-content" => $size::MinContent,
2058                "max-content" | "-moz-max-content" => $size::MaxContent,
2059                "fit-content" | "-moz-fit-content" => $size::FitContent,
2060                #[cfg(feature = "gecko")]
2061                "-moz-available" => $size::MozAvailable,
2062                #[cfg(feature = "gecko")]
2063                "-webkit-fill-available" if is_webkit_fill_available_keyword_enabled() => $size::WebkitFillAvailable,
2064                "stretch" if is_stretch_enabled() => $size::Stretch,
2065                $auto_or_none => $size::$auto_or_none_ident,
2066            })
2067        });
2068        if size.is_ok() {
2069            return size;
2070        }
2071    }};
2072}
2073
2074#[cfg(feature = "gecko")]
2075fn is_webkit_fill_available_keyword_enabled() -> bool {
2076    static_prefs::pref!("layout.css.webkit-fill-available.enabled")
2077}
2078fn is_stretch_enabled() -> bool {
2079    static_prefs::pref!("layout.css.stretch-size-keyword.enabled")
2080}
2081
2082fn is_fit_content_function_enabled() -> bool {
2083    static_prefs::pref!("layout.css.fit-content-function.enabled")
2084}
2085
2086macro_rules! parse_fit_content_function {
2087    ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
2088        if is_fit_content_function_enabled() {
2089            if let Ok(length) = $input.try_parse(|input| {
2090                input.expect_function_matching("fit-content")?;
2091                input.parse_nested_block(|i| {
2092                    NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
2093                })
2094            }) {
2095                return Ok($size::FitContentFunction(length));
2096            }
2097        }
2098    };
2099}
2100
2101impl Size {
2102    /// Parses, with quirks.
2103    pub fn parse_quirky<'i, 't>(
2104        context: &ParserContext,
2105        input: &mut Parser<'i, 't>,
2106        allow_quirks: AllowQuirks,
2107    ) -> Result<Self, ParseError<'i>> {
2108        parse_size_non_length!(Size, input, "auto" => Auto);
2109        parse_fit_content_function!(Size, input, context, allow_quirks);
2110
2111        match input
2112            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
2113        {
2114            Ok(length) => return Ok(GenericSize::LengthPercentage(length)),
2115            Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2116                return Err(e.into())
2117            },
2118            Err(_) => (),
2119        };
2120        if let Ok(length) = input.try_parse(|i| {
2121            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
2122                context,
2123                i,
2124                allow_quirks,
2125            )
2126        }) {
2127            return Ok(GenericSize::AnchorContainingCalcFunction(length));
2128        }
2129        Ok(Self::AnchorSizeFunction(Box::new(
2130            GenericAnchorSizeFunction::parse(context, input)?,
2131        )))
2132    }
2133
2134    /// Returns `0%`.
2135    #[inline]
2136    pub fn zero_percent() -> Self {
2137        GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
2138    }
2139}
2140
2141/// A specified value for `max-width` or `max-height` property.
2142pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
2143
2144impl Parse for MaxSize {
2145    fn parse<'i, 't>(
2146        context: &ParserContext,
2147        input: &mut Parser<'i, 't>,
2148    ) -> Result<Self, ParseError<'i>> {
2149        MaxSize::parse_quirky(context, input, AllowQuirks::No)
2150    }
2151}
2152
2153impl MaxSize {
2154    /// Parses, with quirks.
2155    pub fn parse_quirky<'i, 't>(
2156        context: &ParserContext,
2157        input: &mut Parser<'i, 't>,
2158        allow_quirks: AllowQuirks,
2159    ) -> Result<Self, ParseError<'i>> {
2160        parse_size_non_length!(MaxSize, input, "none" => None);
2161        parse_fit_content_function!(MaxSize, input, context, allow_quirks);
2162
2163        match input
2164            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
2165        {
2166            Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),
2167            Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2168                return Err(e.into())
2169            },
2170            Err(_) => (),
2171        };
2172        if let Ok(length) = input.try_parse(|i| {
2173            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
2174                context,
2175                i,
2176                allow_quirks,
2177            )
2178        }) {
2179            return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));
2180        }
2181        Ok(Self::AnchorSizeFunction(Box::new(
2182            GenericAnchorSizeFunction::parse(context, input)?,
2183        )))
2184    }
2185}
2186
2187/// A specified non-negative `<length>` | `<number>`.
2188pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
2189
2190/// A specified value for `anchor-size` function.
2191pub type AnchorSizeFunction = GenericAnchorSizeFunction<LengthPercentage>;
2192
2193/// A specified value for `margin` properties.
2194pub type Margin = GenericMargin<LengthPercentage>;
2195
2196impl Margin {
2197    /// Parses a margin type, allowing the unitless length quirk.
2198    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
2199    #[inline]
2200    pub fn parse_quirky<'i, 't>(
2201        context: &ParserContext,
2202        input: &mut Parser<'i, 't>,
2203        allow_quirks: AllowQuirks,
2204    ) -> Result<Self, ParseError<'i>> {
2205        if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2206        {
2207            return Ok(Self::LengthPercentage(l));
2208        }
2209        match input.try_parse(|i| i.expect_ident_matching("auto")) {
2210            Ok(_) => return Ok(Self::Auto),
2211            Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2212                return Err(e.into())
2213            },
2214            Err(_) => (),
2215        };
2216        if let Ok(l) = input.try_parse(|i| {
2217            LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)
2218        }) {
2219            return Ok(Self::AnchorContainingCalcFunction(l));
2220        }
2221        let inner = AnchorSizeFunction::parse(context, input)?;
2222        Ok(Self::AnchorSizeFunction(Box::new(inner)))
2223    }
2224}
2225
2226impl Parse for Margin {
2227    fn parse<'i, 't>(
2228        context: &ParserContext,
2229        input: &mut Parser<'i, 't>,
2230    ) -> Result<Self, ParseError<'i>> {
2231        Self::parse_quirky(context, input, AllowQuirks::No)
2232    }
2233}