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