Skip to main content

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