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