Skip to main content

style/values/specified/
length.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! [Length values][length].
6//!
7//! [length]: https://drafts.csswg.org/css-values/#lengths
8
9use super::{AllowQuirks, Number, Percentage, ToComputedValue};
10use crate::computed_value_flags::ComputedValueFlags;
11use crate::derives::*;
12use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
13#[cfg(feature = "gecko")]
14use crate::gecko_bindings::structs::GeckoFontMetrics;
15use crate::parser::{Parse, ParserContext};
16use crate::values::computed::{self, CSSPixelLength, Context, FontSize};
17use crate::values::generics::length as generics;
18use crate::values::generics::length::{
19    GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal,
20    GenericMargin, GenericMaxSize, GenericSize,
21};
22use crate::values::generics::NonNegative;
23use crate::values::specified::calc::{self, AllowAnchorPositioningFunctions, CalcNode};
24use crate::values::specified::font::QueryFontMetricsFlags;
25use crate::values::specified::NonNegativeNumber;
26use crate::values::CSSFloat;
27use crate::{Zero, ZeroNoPercent};
28use app_units::AU_PER_PX;
29use cssparser::{match_ignore_ascii_case, Parser, Token};
30use debug_unreachable::debug_unreachable;
31use std::cmp;
32use std::fmt::{self, Write};
33use style_traits::values::specified::AllowedNumericType;
34use style_traits::{
35    CssString, CssWriter, NumericValue, ParseError, ParsingMode, SpecifiedValueInfo,
36    StyleParseErrorKind, ToCss, ToTyped, TypedValue, UnitValue,
37};
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    // Return the canonical unit for this value.
877    fn canonical_unit(&self) -> Option<&'static str> {
878        Some("px")
879    }
880
881    // Convert this value to the specified unit, if possible.
882    fn to(&self, unit: &str) -> Result<Self, ()> {
883        let px = self.to_px();
884
885        Ok(match_ignore_ascii_case! { unit,
886            "px" => Self::Px(px),
887            "in" => Self::In(px / PX_PER_IN),
888            "cm" => Self::Cm(px / PX_PER_CM),
889            "mm" => Self::Mm(px / PX_PER_MM),
890            "q" => Self::Q(px / PX_PER_Q),
891            "pt" => Self::Pt(px / PX_PER_PT),
892            "pc" => Self::Pc(px / PX_PER_PC),
893             _ => return Err(()),
894        })
895    }
896
897    /// Convert this into a pixel value.
898    #[inline]
899    pub fn to_px(&self) -> CSSFloat {
900        match *self {
901            Self::Px(value) => value,
902            Self::In(value) => value * PX_PER_IN,
903            Self::Cm(value) => value * PX_PER_CM,
904            Self::Mm(value) => value * PX_PER_MM,
905            Self::Q(value) => value * PX_PER_Q,
906            Self::Pt(value) => value * PX_PER_PT,
907            Self::Pc(value) => value * PX_PER_PC,
908        }
909    }
910
911    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
912    where
913        O: Fn(f32, f32) -> f32,
914    {
915        Ok(Self::Px(op(self.to_px(), other.to_px())))
916    }
917
918    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
919        Self::Px(op(self.to_px()))
920    }
921}
922
923impl ToComputedValue for AbsoluteLength {
924    type ComputedValue = CSSPixelLength;
925
926    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
927        CSSPixelLength::new(self.to_px())
928            .zoom(context.builder.effective_zoom)
929            .finite()
930    }
931
932    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
933        Self::Px(computed.px())
934    }
935}
936
937impl PartialOrd for AbsoluteLength {
938    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
939        self.to_px().partial_cmp(&other.to_px())
940    }
941}
942
943/// A container query length.
944///
945/// <https://drafts.csswg.org/css-contain-3/#container-lengths>
946#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
947#[repr(u8)]
948pub enum ContainerRelativeLength {
949    /// 1% of query container's width
950    #[css(dimension)]
951    Cqw(CSSFloat),
952    /// 1% of query container's height
953    #[css(dimension)]
954    Cqh(CSSFloat),
955    /// 1% of query container's inline size
956    #[css(dimension)]
957    Cqi(CSSFloat),
958    /// 1% of query container's block size
959    #[css(dimension)]
960    Cqb(CSSFloat),
961    /// The smaller value of `cqi` or `cqb`
962    #[css(dimension)]
963    Cqmin(CSSFloat),
964    /// The larger value of `cqi` or `cqb`
965    #[css(dimension)]
966    Cqmax(CSSFloat),
967}
968
969impl ContainerRelativeLength {
970    fn unitless_value(&self) -> CSSFloat {
971        match *self {
972            Self::Cqw(v)
973            | Self::Cqh(v)
974            | Self::Cqi(v)
975            | Self::Cqb(v)
976            | Self::Cqmin(v)
977            | Self::Cqmax(v) => v,
978        }
979    }
980
981    // Return the unit, as a string.
982    fn unit(&self) -> &'static str {
983        match *self {
984            Self::Cqw(_) => "cqw",
985            Self::Cqh(_) => "cqh",
986            Self::Cqi(_) => "cqi",
987            Self::Cqb(_) => "cqb",
988            Self::Cqmin(_) => "cqmin",
989            Self::Cqmax(_) => "cqmax",
990        }
991    }
992
993    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
994    where
995        O: Fn(f32, f32) -> f32,
996    {
997        use self::ContainerRelativeLength::*;
998
999        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1000            return Err(());
1001        }
1002
1003        Ok(match (self, other) {
1004            (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)),
1005            (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)),
1006            (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)),
1007            (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)),
1008            (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)),
1009            (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)),
1010
1011            // See https://github.com/rust-lang/rust/issues/68867, then
1012            // https://github.com/rust-lang/rust/pull/95161. rustc isn't
1013            // able to figure it own on its own so we help.
1014            _ => unsafe {
1015                match *self {
1016                    Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
1017                }
1018                debug_unreachable!("Forgot to handle unit in try_op()")
1019            },
1020        })
1021    }
1022
1023    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
1024        match self {
1025            Self::Cqw(x) => Self::Cqw(op(*x)),
1026            Self::Cqh(x) => Self::Cqh(op(*x)),
1027            Self::Cqi(x) => Self::Cqi(op(*x)),
1028            Self::Cqb(x) => Self::Cqb(op(*x)),
1029            Self::Cqmin(x) => Self::Cqmin(op(*x)),
1030            Self::Cqmax(x) => Self::Cqmax(op(*x)),
1031        }
1032    }
1033
1034    /// Computes the given container-relative length.
1035    pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
1036        if context.for_non_inherited_property {
1037            context.rule_cache_conditions.borrow_mut().set_uncacheable();
1038        }
1039        context
1040            .builder
1041            .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
1042
1043        // TODO(emilio, bug 1894104): Need to handle zoom here, probably something like
1044        // container_zoom - effective_zoom or so. See
1045        // https://github.com/w3c/csswg-drafts/issues/10268
1046        let size = context.get_container_size_query();
1047        let (factor, container_length) = match *self {
1048            Self::Cqw(v) => (v, size.get_container_width(context)),
1049            Self::Cqh(v) => (v, size.get_container_height(context)),
1050            Self::Cqi(v) => (v, size.get_container_inline_size(context)),
1051            Self::Cqb(v) => (v, size.get_container_block_size(context)),
1052            Self::Cqmin(v) => (
1053                v,
1054                cmp::min(
1055                    size.get_container_inline_size(context),
1056                    size.get_container_block_size(context),
1057                ),
1058            ),
1059            Self::Cqmax(v) => (
1060                v,
1061                cmp::max(
1062                    size.get_container_inline_size(context),
1063                    size.get_container_block_size(context),
1064                ),
1065            ),
1066        };
1067        CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()
1068    }
1069}
1070
1071/// A `<length>` without taking `calc` expressions into account
1072///
1073/// <https://drafts.csswg.org/css-values/#lengths>
1074#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
1075#[repr(u8)]
1076pub enum NoCalcLength {
1077    /// An absolute length
1078    ///
1079    /// <https://drafts.csswg.org/css-values/#absolute-length>
1080    Absolute(AbsoluteLength),
1081
1082    /// A font-relative length:
1083    ///
1084    /// <https://drafts.csswg.org/css-values/#font-relative-lengths>
1085    FontRelative(FontRelativeLength),
1086
1087    /// A viewport-relative length.
1088    ///
1089    /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
1090    ViewportPercentage(ViewportPercentageLength),
1091
1092    /// A container query length.
1093    ///
1094    /// <https://drafts.csswg.org/css-contain-3/#container-lengths>
1095    ContainerRelative(ContainerRelativeLength),
1096    /// HTML5 "character width", as defined in HTML5 § 14.5.4.
1097    ///
1098    /// This cannot be specified by the user directly and is only generated by
1099    /// `Stylist::synthesize_rules_for_legacy_attributes()`.
1100    ServoCharacterWidth(CharacterWidth),
1101}
1102
1103impl NoCalcLength {
1104    /// Return the unitless, raw value.
1105    pub fn unitless_value(&self) -> CSSFloat {
1106        match *self {
1107            Self::Absolute(v) => v.unitless_value(),
1108            Self::FontRelative(v) => v.unitless_value(),
1109            Self::ViewportPercentage(v) => v.unitless_value(),
1110            Self::ContainerRelative(v) => v.unitless_value(),
1111            Self::ServoCharacterWidth(c) => c.0 as f32,
1112        }
1113    }
1114
1115    /// Return the unit, as a string.
1116    pub fn unit(&self) -> &'static str {
1117        match *self {
1118            Self::Absolute(v) => v.unit(),
1119            Self::FontRelative(v) => v.unit(),
1120            Self::ViewportPercentage(v) => v.unit(),
1121            Self::ContainerRelative(v) => v.unit(),
1122            Self::ServoCharacterWidth(_) => "",
1123        }
1124    }
1125
1126    /// Return the canonical unit for this value, if one exists.
1127    pub fn canonical_unit(&self) -> Option<&'static str> {
1128        match *self {
1129            Self::Absolute(v) => v.canonical_unit(),
1130            _ => None,
1131        }
1132    }
1133
1134    /// Convert this value to the specified unit, if possible.
1135    pub fn to(&self, unit: &str) -> Result<Self, ()> {
1136        match self {
1137            Self::Absolute(v) => Ok(Self::Absolute(v.to(unit)?)),
1138            _ => Err(()),
1139        }
1140    }
1141
1142    /// Returns whether the value of this length without unit is less than zero.
1143    pub fn is_negative(&self) -> bool {
1144        self.unitless_value().is_sign_negative()
1145    }
1146
1147    /// Returns whether the value of this length without unit is equal to zero.
1148    pub fn is_zero(&self) -> bool {
1149        self.unitless_value() == 0.0
1150    }
1151
1152    /// Returns whether the value of this length without unit is infinite.
1153    pub fn is_infinite(&self) -> bool {
1154        self.unitless_value().is_infinite()
1155    }
1156
1157    /// Returns whether the value of this length without unit is NaN.
1158    pub fn is_nan(&self) -> bool {
1159        self.unitless_value().is_nan()
1160    }
1161
1162    /// Whether text-only zoom should be applied to this length.
1163    ///
1164    /// Generally, font-dependent/relative units don't get text-only-zoomed,
1165    /// because the font they're relative to should be zoomed already.
1166    pub fn should_zoom_text(&self) -> bool {
1167        match *self {
1168            Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true,
1169            Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false,
1170        }
1171    }
1172
1173    /// Parse a given absolute or relative dimension.
1174    pub fn parse_dimension_with_flags(
1175        parsing_mode: ParsingMode,
1176        in_page_rule: bool,
1177        value: CSSFloat,
1178        unit: &str,
1179    ) -> Result<Self, ()> {
1180        let allows_computational_dependence = parsing_mode.allows_computational_dependence();
1181
1182        Ok(match_ignore_ascii_case! { unit,
1183            "px" => Self::Absolute(AbsoluteLength::Px(value)),
1184            "in" => Self::Absolute(AbsoluteLength::In(value)),
1185            "cm" => Self::Absolute(AbsoluteLength::Cm(value)),
1186            "mm" => Self::Absolute(AbsoluteLength::Mm(value)),
1187            "q" => Self::Absolute(AbsoluteLength::Q(value)),
1188            "pt" => Self::Absolute(AbsoluteLength::Pt(value)),
1189            "pc" => Self::Absolute(AbsoluteLength::Pc(value)),
1190            // font-relative
1191            "em" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Em(value)),
1192            "ex" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ex(value)),
1193            "rex" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rex(value)),
1194            "ch" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ch(value)),
1195            "rch" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rch(value)),
1196            "cap" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Cap(value)),
1197            "rcap" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rcap(value)),
1198            "ic" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ic(value)),
1199            "ric" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ric(value)),
1200            "rem" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rem(value)),
1201            "lh" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Lh(value)),
1202            "rlh" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rlh(value)),
1203            // viewport percentages
1204            "vw" if !in_page_rule => {
1205                Self::ViewportPercentage(ViewportPercentageLength::Vw(value))
1206            },
1207            "svw" if !in_page_rule => {
1208                Self::ViewportPercentage(ViewportPercentageLength::Svw(value))
1209            },
1210            "lvw" if !in_page_rule => {
1211                Self::ViewportPercentage(ViewportPercentageLength::Lvw(value))
1212            },
1213            "dvw" if !in_page_rule => {
1214                Self::ViewportPercentage(ViewportPercentageLength::Dvw(value))
1215            },
1216            "vh" if !in_page_rule => {
1217                Self::ViewportPercentage(ViewportPercentageLength::Vh(value))
1218            },
1219            "svh" if !in_page_rule => {
1220                Self::ViewportPercentage(ViewportPercentageLength::Svh(value))
1221            },
1222            "lvh" if !in_page_rule => {
1223                Self::ViewportPercentage(ViewportPercentageLength::Lvh(value))
1224            },
1225            "dvh" if !in_page_rule => {
1226                Self::ViewportPercentage(ViewportPercentageLength::Dvh(value))
1227            },
1228            "vmin" if !in_page_rule => {
1229                Self::ViewportPercentage(ViewportPercentageLength::Vmin(value))
1230            },
1231            "svmin" if !in_page_rule => {
1232                Self::ViewportPercentage(ViewportPercentageLength::Svmin(value))
1233            },
1234            "lvmin" if !in_page_rule => {
1235                Self::ViewportPercentage(ViewportPercentageLength::Lvmin(value))
1236            },
1237            "dvmin" if !in_page_rule => {
1238                Self::ViewportPercentage(ViewportPercentageLength::Dvmin(value))
1239            },
1240            "vmax" if !in_page_rule => {
1241                Self::ViewportPercentage(ViewportPercentageLength::Vmax(value))
1242            },
1243            "svmax" if !in_page_rule => {
1244                Self::ViewportPercentage(ViewportPercentageLength::Svmax(value))
1245            },
1246            "lvmax" if !in_page_rule => {
1247                Self::ViewportPercentage(ViewportPercentageLength::Lvmax(value))
1248            },
1249            "dvmax" if !in_page_rule => {
1250                Self::ViewportPercentage(ViewportPercentageLength::Dvmax(value))
1251            },
1252            "vb" if !in_page_rule => {
1253                Self::ViewportPercentage(ViewportPercentageLength::Vb(value))
1254            },
1255            "svb" if !in_page_rule => {
1256                Self::ViewportPercentage(ViewportPercentageLength::Svb(value))
1257            },
1258            "lvb" if !in_page_rule => {
1259                Self::ViewportPercentage(ViewportPercentageLength::Lvb(value))
1260            },
1261            "dvb" if !in_page_rule => {
1262                Self::ViewportPercentage(ViewportPercentageLength::Dvb(value))
1263            },
1264            "vi" if !in_page_rule => {
1265                Self::ViewportPercentage(ViewportPercentageLength::Vi(value))
1266            },
1267            "svi" if !in_page_rule => {
1268                Self::ViewportPercentage(ViewportPercentageLength::Svi(value))
1269            },
1270            "lvi" if !in_page_rule => {
1271                Self::ViewportPercentage(ViewportPercentageLength::Lvi(value))
1272            },
1273            "dvi" if !in_page_rule => {
1274                Self::ViewportPercentage(ViewportPercentageLength::Dvi(value))
1275            },
1276            // Container query lengths. Inherit the limitation from viewport units since
1277            // we may fall back to them.
1278            "cqw" if !in_page_rule && cfg!(feature = "gecko") => {
1279                Self::ContainerRelative(ContainerRelativeLength::Cqw(value))
1280            },
1281            "cqh" if !in_page_rule && cfg!(feature = "gecko") => {
1282                Self::ContainerRelative(ContainerRelativeLength::Cqh(value))
1283            },
1284            "cqi" if !in_page_rule && cfg!(feature = "gecko") => {
1285                Self::ContainerRelative(ContainerRelativeLength::Cqi(value))
1286            },
1287            "cqb" if !in_page_rule && cfg!(feature = "gecko") => {
1288                Self::ContainerRelative(ContainerRelativeLength::Cqb(value))
1289            },
1290            "cqmin" if !in_page_rule && cfg!(feature = "gecko") => {
1291                Self::ContainerRelative(ContainerRelativeLength::Cqmin(value))
1292            },
1293            "cqmax" if !in_page_rule && cfg!(feature = "gecko") => {
1294                Self::ContainerRelative(ContainerRelativeLength::Cqmax(value))
1295            },
1296            _ => return Err(()),
1297        })
1298    }
1299
1300    /// Parse a given absolute or relative dimension.
1301    pub fn parse_dimension_with_context(
1302        context: &ParserContext,
1303        value: CSSFloat,
1304        unit: &str,
1305    ) -> Result<Self, ()> {
1306        Self::parse_dimension_with_flags(context.parsing_mode, context.in_page_rule(), value, unit)
1307    }
1308
1309    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
1310    where
1311        O: Fn(f32, f32) -> f32,
1312    {
1313        use self::NoCalcLength::*;
1314
1315        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1316            return Err(());
1317        }
1318
1319        Ok(match (self, other) {
1320            (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?),
1321            (&FontRelative(ref one), &FontRelative(ref other)) => {
1322                FontRelative(one.try_op(other, op)?)
1323            },
1324            (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
1325                ViewportPercentage(one.try_op(other, op)?)
1326            },
1327            (&ContainerRelative(ref one), &ContainerRelative(ref other)) => {
1328                ContainerRelative(one.try_op(other, op)?)
1329            },
1330            (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
1331                ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32))
1332            },
1333            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
1334            // able to figure it own on its own so we help.
1335            _ => unsafe {
1336                match *self {
1337                    Absolute(..)
1338                    | FontRelative(..)
1339                    | ViewportPercentage(..)
1340                    | ContainerRelative(..)
1341                    | ServoCharacterWidth(..) => {},
1342                }
1343                debug_unreachable!("Forgot to handle unit in try_op()")
1344            },
1345        })
1346    }
1347
1348    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
1349        use self::NoCalcLength::*;
1350
1351        match self {
1352            Absolute(ref one) => Absolute(one.map(op)),
1353            FontRelative(ref one) => FontRelative(one.map(op)),
1354            ViewportPercentage(ref one) => ViewportPercentage(one.map(op)),
1355            ContainerRelative(ref one) => ContainerRelative(one.map(op)),
1356            ServoCharacterWidth(ref one) => {
1357                ServoCharacterWidth(CharacterWidth(op(one.0 as f32) as i32))
1358            },
1359        }
1360    }
1361
1362    /// Get a px value without context (so only absolute units can be handled).
1363    #[inline]
1364    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1365        match *self {
1366            Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
1367            _ => Err(()),
1368        }
1369    }
1370
1371    /// Get a px value without a full style context; this can handle either
1372    /// absolute or (if a font metrics getter is provided) font-relative units.
1373    #[cfg(feature = "gecko")]
1374    #[inline]
1375    pub fn to_computed_pixel_length_with_font_metrics(
1376        &self,
1377        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1378    ) -> Result<CSSFloat, ()> {
1379        match *self {
1380            Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
1381            Self::FontRelative(fr) => {
1382                if let Some(getter) = get_font_metrics {
1383                    fr.to_computed_pixel_length_with_font_metrics(getter)
1384                } else {
1385                    Err(())
1386                }
1387            },
1388            _ => Err(()),
1389        }
1390    }
1391
1392    /// Get an absolute length from a px value.
1393    #[inline]
1394    pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
1395        NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
1396    }
1397}
1398
1399impl ToCss for NoCalcLength {
1400    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1401    where
1402        W: Write,
1403    {
1404        crate::values::serialize_specified_dimension(
1405            self.unitless_value(),
1406            self.unit(),
1407            false,
1408            dest,
1409        )
1410    }
1411}
1412
1413impl ToTyped for NoCalcLength {
1414    fn to_typed(&self) -> Option<TypedValue> {
1415        let value = self.unitless_value();
1416        let unit = CssString::from(self.unit());
1417        Some(TypedValue::Numeric(NumericValue::Unit(UnitValue {
1418            value,
1419            unit,
1420        })))
1421    }
1422}
1423
1424impl SpecifiedValueInfo for NoCalcLength {}
1425
1426impl PartialOrd for NoCalcLength {
1427    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1428        use self::NoCalcLength::*;
1429
1430        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1431            return None;
1432        }
1433
1434        match (self, other) {
1435            (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),
1436            (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),
1437            (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
1438                one.partial_cmp(other)
1439            },
1440            (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other),
1441            (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
1442                one.0.partial_cmp(&other.0)
1443            },
1444            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
1445            // able to figure it own on its own so we help.
1446            _ => unsafe {
1447                match *self {
1448                    Absolute(..)
1449                    | FontRelative(..)
1450                    | ViewportPercentage(..)
1451                    | ContainerRelative(..)
1452                    | ServoCharacterWidth(..) => {},
1453                }
1454                debug_unreachable!("Forgot an arm in partial_cmp?")
1455            },
1456        }
1457    }
1458}
1459
1460impl Zero for NoCalcLength {
1461    fn zero() -> Self {
1462        NoCalcLength::Absolute(AbsoluteLength::Px(0.))
1463    }
1464
1465    fn is_zero(&self) -> bool {
1466        NoCalcLength::is_zero(self)
1467    }
1468}
1469
1470/// An extension to `NoCalcLength` to parse `calc` expressions.
1471/// This is commonly used for the `<length>` values.
1472///
1473/// <https://drafts.csswg.org/css-values/#lengths>
1474#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1475#[typed_value(derive_fields)]
1476pub enum Length {
1477    /// The internal length type that cannot parse `calc`
1478    NoCalc(NoCalcLength),
1479    /// A calc expression.
1480    ///
1481    /// <https://drafts.csswg.org/css-values/#calc-notation>
1482    Calc(Box<CalcLengthPercentage>),
1483}
1484
1485impl From<NoCalcLength> for Length {
1486    #[inline]
1487    fn from(len: NoCalcLength) -> Self {
1488        Length::NoCalc(len)
1489    }
1490}
1491
1492impl PartialOrd for FontRelativeLength {
1493    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1494        use self::FontRelativeLength::*;
1495
1496        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1497            return None;
1498        }
1499
1500        match (self, other) {
1501            (&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
1502            (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
1503            (&Rex(ref one), &Rex(ref other)) => one.partial_cmp(other),
1504            (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
1505            (&Rch(ref one), &Rch(ref other)) => one.partial_cmp(other),
1506            (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),
1507            (&Rcap(ref one), &Rcap(ref other)) => one.partial_cmp(other),
1508            (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other),
1509            (&Ric(ref one), &Ric(ref other)) => one.partial_cmp(other),
1510            (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
1511            (&Lh(ref one), &Lh(ref other)) => one.partial_cmp(other),
1512            (&Rlh(ref one), &Rlh(ref other)) => one.partial_cmp(other),
1513            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
1514            // able to figure it own on its own so we help.
1515            _ => unsafe {
1516                match *self {
1517                    Em(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..) | Ic(..)
1518                    | Ric(..) | Rem(..) | Lh(..) | Rlh(..) => {},
1519                }
1520                debug_unreachable!("Forgot an arm in partial_cmp?")
1521            },
1522        }
1523    }
1524}
1525
1526impl PartialOrd for ContainerRelativeLength {
1527    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1528        use self::ContainerRelativeLength::*;
1529
1530        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1531            return None;
1532        }
1533
1534        match (self, other) {
1535            (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other),
1536            (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other),
1537            (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other),
1538            (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other),
1539            (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other),
1540            (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other),
1541
1542            // See https://github.com/rust-lang/rust/issues/68867, then
1543            // https://github.com/rust-lang/rust/pull/95161. rustc isn't
1544            // able to figure it own on its own so we help.
1545            _ => unsafe {
1546                match *self {
1547                    Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
1548                }
1549                debug_unreachable!("Forgot to handle unit in partial_cmp()")
1550            },
1551        }
1552    }
1553}
1554
1555impl PartialOrd for ViewportPercentageLength {
1556    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1557        use self::ViewportPercentageLength::*;
1558
1559        if std::mem::discriminant(self) != std::mem::discriminant(other) {
1560            return None;
1561        }
1562
1563        match (self, other) {
1564            (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),
1565            (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other),
1566            (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other),
1567            (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other),
1568            (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),
1569            (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other),
1570            (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other),
1571            (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other),
1572            (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),
1573            (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other),
1574            (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other),
1575            (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other),
1576            (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),
1577            (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other),
1578            (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other),
1579            (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other),
1580            (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other),
1581            (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other),
1582            (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other),
1583            (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other),
1584            (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other),
1585            (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other),
1586            (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other),
1587            (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other),
1588            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
1589            // able to figure it own on its own so we help.
1590            _ => unsafe {
1591                match *self {
1592                    Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)
1593                    | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)
1594                    | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)
1595                    | Svi(..) | Lvi(..) | Dvi(..) => {},
1596                }
1597                debug_unreachable!("Forgot an arm in partial_cmp?")
1598            },
1599        }
1600    }
1601}
1602
1603impl Length {
1604    #[inline]
1605    fn parse_internal<'i, 't>(
1606        context: &ParserContext,
1607        input: &mut Parser<'i, 't>,
1608        num_context: AllowedNumericType,
1609        allow_quirks: AllowQuirks,
1610    ) -> Result<Self, ParseError<'i>> {
1611        let location = input.current_source_location();
1612        let token = input.next()?;
1613        match *token {
1614            Token::Dimension {
1615                value, ref unit, ..
1616            } if num_context.is_ok(context.parsing_mode, value) => {
1617                NoCalcLength::parse_dimension_with_context(context, value, unit)
1618                    .map(Length::NoCalc)
1619                    .map_err(|()| location.new_unexpected_token_error(token.clone()))
1620            },
1621            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1622                if value != 0.
1623                    && !context.parsing_mode.allows_unitless_lengths()
1624                    && !allow_quirks.allowed(context.quirks_mode)
1625                {
1626                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1627                }
1628                Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
1629                    value,
1630                ))))
1631            },
1632            Token::Function(ref name) => {
1633                let function = CalcNode::math_function(context, name, location)?;
1634                let calc = CalcNode::parse_length(context, input, num_context, function)?;
1635                Ok(Length::Calc(Box::new(calc)))
1636            },
1637            ref token => return Err(location.new_unexpected_token_error(token.clone())),
1638        }
1639    }
1640
1641    /// Parse a non-negative length
1642    #[inline]
1643    pub fn parse_non_negative<'i, 't>(
1644        context: &ParserContext,
1645        input: &mut Parser<'i, 't>,
1646    ) -> Result<Self, ParseError<'i>> {
1647        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1648    }
1649
1650    /// Parse a non-negative length, allowing quirks.
1651    #[inline]
1652    pub fn parse_non_negative_quirky<'i, 't>(
1653        context: &ParserContext,
1654        input: &mut Parser<'i, 't>,
1655        allow_quirks: AllowQuirks,
1656    ) -> Result<Self, ParseError<'i>> {
1657        Self::parse_internal(
1658            context,
1659            input,
1660            AllowedNumericType::NonNegative,
1661            allow_quirks,
1662        )
1663    }
1664
1665    /// Get an absolute length from a px value.
1666    #[inline]
1667    pub fn from_px(px_value: CSSFloat) -> Length {
1668        Length::NoCalc(NoCalcLength::from_px(px_value))
1669    }
1670
1671    /// Get a px value without context.
1672    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1673        match *self {
1674            Self::NoCalc(ref l) => l.to_computed_pixel_length_without_context(),
1675            Self::Calc(ref l) => l.to_computed_pixel_length_without_context(),
1676        }
1677    }
1678
1679    /// Get a px value, with an optional GeckoFontMetrics getter to resolve font-relative units.
1680    #[cfg(feature = "gecko")]
1681    pub fn to_computed_pixel_length_with_font_metrics(
1682        &self,
1683        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1684    ) -> Result<CSSFloat, ()> {
1685        match *self {
1686            Self::NoCalc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
1687            Self::Calc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
1688        }
1689    }
1690}
1691
1692impl Parse for Length {
1693    fn parse<'i, 't>(
1694        context: &ParserContext,
1695        input: &mut Parser<'i, 't>,
1696    ) -> Result<Self, ParseError<'i>> {
1697        Self::parse_quirky(context, input, AllowQuirks::No)
1698    }
1699}
1700
1701impl Zero for Length {
1702    fn zero() -> Self {
1703        Length::NoCalc(NoCalcLength::zero())
1704    }
1705
1706    fn is_zero(&self) -> bool {
1707        // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
1708        // non-zero here?
1709        match *self {
1710            Length::NoCalc(ref l) => l.is_zero(),
1711            Length::Calc(..) => false,
1712        }
1713    }
1714}
1715
1716impl Length {
1717    /// Parses a length, with quirks.
1718    pub fn parse_quirky<'i, 't>(
1719        context: &ParserContext,
1720        input: &mut Parser<'i, 't>,
1721        allow_quirks: AllowQuirks,
1722    ) -> Result<Self, ParseError<'i>> {
1723        Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
1724    }
1725}
1726
1727/// A wrapper of Length, whose value must be >= 0.
1728pub type NonNegativeLength = NonNegative<Length>;
1729
1730impl Parse for NonNegativeLength {
1731    #[inline]
1732    fn parse<'i, 't>(
1733        context: &ParserContext,
1734        input: &mut Parser<'i, 't>,
1735    ) -> Result<Self, ParseError<'i>> {
1736        Ok(NonNegative(Length::parse_non_negative(context, input)?))
1737    }
1738}
1739
1740impl From<NoCalcLength> for NonNegativeLength {
1741    #[inline]
1742    fn from(len: NoCalcLength) -> Self {
1743        NonNegative(Length::NoCalc(len))
1744    }
1745}
1746
1747impl From<Length> for NonNegativeLength {
1748    #[inline]
1749    fn from(len: Length) -> Self {
1750        NonNegative(len)
1751    }
1752}
1753
1754impl NonNegativeLength {
1755    /// Get an absolute length from a px value.
1756    #[inline]
1757    pub fn from_px(px_value: CSSFloat) -> Self {
1758        Length::from_px(px_value.max(0.)).into()
1759    }
1760
1761    /// Parses a non-negative length, optionally with quirks.
1762    #[inline]
1763    pub fn parse_quirky<'i, 't>(
1764        context: &ParserContext,
1765        input: &mut Parser<'i, 't>,
1766        allow_quirks: AllowQuirks,
1767    ) -> Result<Self, ParseError<'i>> {
1768        Ok(NonNegative(Length::parse_non_negative_quirky(
1769            context,
1770            input,
1771            allow_quirks,
1772        )?))
1773    }
1774}
1775
1776/// A `<length-percentage>` value. This can be either a `<length>`, a
1777/// `<percentage>`, or a combination of both via `calc()`.
1778///
1779/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
1780#[allow(missing_docs)]
1781#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1782#[typed_value(derive_fields)]
1783pub enum LengthPercentage {
1784    Length(NoCalcLength),
1785    Percentage(computed::Percentage),
1786    Calc(Box<CalcLengthPercentage>),
1787}
1788
1789impl From<Length> for LengthPercentage {
1790    fn from(len: Length) -> LengthPercentage {
1791        match len {
1792            Length::NoCalc(l) => LengthPercentage::Length(l),
1793            Length::Calc(l) => LengthPercentage::Calc(l),
1794        }
1795    }
1796}
1797
1798impl From<NoCalcLength> for LengthPercentage {
1799    #[inline]
1800    fn from(len: NoCalcLength) -> Self {
1801        LengthPercentage::Length(len)
1802    }
1803}
1804
1805impl From<Percentage> for LengthPercentage {
1806    #[inline]
1807    fn from(pc: Percentage) -> Self {
1808        if let Some(clamping_mode) = pc.calc_clamping_mode() {
1809            LengthPercentage::Calc(Box::new(CalcLengthPercentage {
1810                clamping_mode,
1811                node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
1812            }))
1813        } else {
1814            LengthPercentage::Percentage(computed::Percentage(pc.get()))
1815        }
1816    }
1817}
1818
1819impl From<computed::Percentage> for LengthPercentage {
1820    #[inline]
1821    fn from(pc: computed::Percentage) -> Self {
1822        LengthPercentage::Percentage(pc)
1823    }
1824}
1825
1826impl Parse for LengthPercentage {
1827    #[inline]
1828    fn parse<'i, 't>(
1829        context: &ParserContext,
1830        input: &mut Parser<'i, 't>,
1831    ) -> Result<Self, ParseError<'i>> {
1832        Self::parse_quirky(context, input, AllowQuirks::No)
1833    }
1834}
1835
1836impl LengthPercentage {
1837    #[inline]
1838    /// Returns a `0%` value.
1839    pub fn zero_percent() -> LengthPercentage {
1840        LengthPercentage::Percentage(computed::Percentage::zero())
1841    }
1842
1843    #[inline]
1844    /// Returns a `100%` value.
1845    pub fn hundred_percent() -> LengthPercentage {
1846        LengthPercentage::Percentage(computed::Percentage::hundred())
1847    }
1848
1849    fn parse_internal<'i, 't>(
1850        context: &ParserContext,
1851        input: &mut Parser<'i, 't>,
1852        num_context: AllowedNumericType,
1853        allow_quirks: AllowQuirks,
1854        allow_anchor: AllowAnchorPositioningFunctions,
1855    ) -> Result<Self, ParseError<'i>> {
1856        let location = input.current_source_location();
1857        let token = input.next()?;
1858        match *token {
1859            Token::Dimension {
1860                value, ref unit, ..
1861            } if num_context.is_ok(context.parsing_mode, value) => {
1862                return NoCalcLength::parse_dimension_with_context(context, value, unit)
1863                    .map(LengthPercentage::Length)
1864                    .map_err(|()| location.new_unexpected_token_error(token.clone()));
1865            },
1866            Token::Percentage { unit_value, .. }
1867                if num_context.is_ok(context.parsing_mode, unit_value) =>
1868            {
1869                return Ok(LengthPercentage::Percentage(computed::Percentage(
1870                    unit_value,
1871                )));
1872            },
1873            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1874                if value != 0.
1875                    && !context.parsing_mode.allows_unitless_lengths()
1876                    && !allow_quirks.allowed(context.quirks_mode)
1877                {
1878                    return Err(location.new_unexpected_token_error(token.clone()));
1879                } else {
1880                    return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
1881                }
1882            },
1883            Token::Function(ref name) => {
1884                let function = CalcNode::math_function(context, name, location)?;
1885                let calc = CalcNode::parse_length_or_percentage(
1886                    context,
1887                    input,
1888                    num_context,
1889                    function,
1890                    allow_anchor,
1891                )?;
1892                Ok(LengthPercentage::Calc(Box::new(calc)))
1893            },
1894            _ => return Err(location.new_unexpected_token_error(token.clone())),
1895        }
1896    }
1897
1898    /// Parses allowing the unitless length quirk.
1899    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1900    #[inline]
1901    pub fn parse_quirky<'i, 't>(
1902        context: &ParserContext,
1903        input: &mut Parser<'i, 't>,
1904        allow_quirks: AllowQuirks,
1905    ) -> Result<Self, ParseError<'i>> {
1906        Self::parse_internal(
1907            context,
1908            input,
1909            AllowedNumericType::All,
1910            allow_quirks,
1911            AllowAnchorPositioningFunctions::No,
1912        )
1913    }
1914
1915    /// Parses allowing the unitless length quirk, as well as allowing
1916    /// anchor-positioning related function, `anchor-size()`.
1917    #[inline]
1918    fn parse_quirky_with_anchor_size_function<'i, 't>(
1919        context: &ParserContext,
1920        input: &mut Parser<'i, 't>,
1921        allow_quirks: AllowQuirks,
1922    ) -> Result<Self, ParseError<'i>> {
1923        Self::parse_internal(
1924            context,
1925            input,
1926            AllowedNumericType::All,
1927            allow_quirks,
1928            AllowAnchorPositioningFunctions::AllowAnchorSize,
1929        )
1930    }
1931
1932    /// Parses allowing the unitless length quirk, as well as allowing
1933    /// anchor-positioning related functions, `anchor()` and `anchor-size()`.
1934    #[inline]
1935    pub fn parse_quirky_with_anchor_functions<'i, 't>(
1936        context: &ParserContext,
1937        input: &mut Parser<'i, 't>,
1938        allow_quirks: AllowQuirks,
1939    ) -> Result<Self, ParseError<'i>> {
1940        Self::parse_internal(
1941            context,
1942            input,
1943            AllowedNumericType::All,
1944            allow_quirks,
1945            AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,
1946        )
1947    }
1948
1949    /// Parses non-negative length, allowing the unitless length quirk,
1950    /// as well as allowing `anchor-size()`.
1951    pub fn parse_non_negative_with_anchor_size<'i, 't>(
1952        context: &ParserContext,
1953        input: &mut Parser<'i, 't>,
1954        allow_quirks: AllowQuirks,
1955    ) -> Result<Self, ParseError<'i>> {
1956        Self::parse_internal(
1957            context,
1958            input,
1959            AllowedNumericType::NonNegative,
1960            allow_quirks,
1961            AllowAnchorPositioningFunctions::AllowAnchorSize,
1962        )
1963    }
1964
1965    /// Parse a non-negative length.
1966    ///
1967    /// FIXME(emilio): This should be not public and we should use
1968    /// NonNegativeLengthPercentage instead.
1969    #[inline]
1970    pub fn parse_non_negative<'i, 't>(
1971        context: &ParserContext,
1972        input: &mut Parser<'i, 't>,
1973    ) -> Result<Self, ParseError<'i>> {
1974        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1975    }
1976
1977    /// Parse a non-negative length, with quirks.
1978    #[inline]
1979    pub fn parse_non_negative_quirky<'i, 't>(
1980        context: &ParserContext,
1981        input: &mut Parser<'i, 't>,
1982        allow_quirks: AllowQuirks,
1983    ) -> Result<Self, ParseError<'i>> {
1984        Self::parse_internal(
1985            context,
1986            input,
1987            AllowedNumericType::NonNegative,
1988            allow_quirks,
1989            AllowAnchorPositioningFunctions::No,
1990        )
1991    }
1992
1993    /// Returns self as specified::calc::CalcNode.
1994    /// Note that this expect the clamping_mode is AllowedNumericType::All for Calc. The caller
1995    /// should take care about it when using this function.
1996    fn to_calc_node(self) -> CalcNode {
1997        match self {
1998            LengthPercentage::Length(l) => CalcNode::Leaf(calc::Leaf::Length(l)),
1999            LengthPercentage::Percentage(p) => CalcNode::Leaf(calc::Leaf::Percentage(p.0)),
2000            LengthPercentage::Calc(p) => p.node,
2001        }
2002    }
2003
2004    /// Construct the value representing `calc(100% - self)`.
2005    pub fn hundred_percent_minus(self, clamping_mode: AllowedNumericType) -> Self {
2006        let mut sum = smallvec::SmallVec::<[CalcNode; 2]>::new();
2007        sum.push(CalcNode::Leaf(calc::Leaf::Percentage(1.0)));
2008
2009        let mut node = self.to_calc_node();
2010        node.negate();
2011        sum.push(node);
2012
2013        let calc = CalcNode::Sum(sum.into_boxed_slice().into());
2014        LengthPercentage::Calc(Box::new(
2015            calc.into_length_or_percentage(clamping_mode).unwrap(),
2016        ))
2017    }
2018}
2019
2020impl Zero for LengthPercentage {
2021    fn zero() -> Self {
2022        LengthPercentage::Length(NoCalcLength::zero())
2023    }
2024
2025    fn is_zero(&self) -> bool {
2026        match *self {
2027            LengthPercentage::Length(l) => l.is_zero(),
2028            LengthPercentage::Percentage(p) => p.0 == 0.0,
2029            LengthPercentage::Calc(_) => false,
2030        }
2031    }
2032}
2033
2034impl ZeroNoPercent for LengthPercentage {
2035    fn is_zero_no_percent(&self) -> bool {
2036        match *self {
2037            LengthPercentage::Percentage(_) => false,
2038            _ => self.is_zero(),
2039        }
2040    }
2041}
2042
2043/// Check if this equal to a specific percentage.
2044pub trait EqualsPercentage {
2045    /// Returns true if this is a specific percentage value. This should exclude calc() even if it
2046    /// only contains percentage component.
2047    fn equals_percentage(&self, v: CSSFloat) -> bool;
2048}
2049
2050impl EqualsPercentage for LengthPercentage {
2051    fn equals_percentage(&self, v: CSSFloat) -> bool {
2052        match *self {
2053            LengthPercentage::Percentage(p) => p.0 == v,
2054            _ => false,
2055        }
2056    }
2057}
2058
2059/// A specified type for `<length-percentage> | auto`.
2060pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
2061
2062impl LengthPercentageOrAuto {
2063    /// Returns a value representing `0%`.
2064    #[inline]
2065    pub fn zero_percent() -> Self {
2066        generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
2067    }
2068
2069    /// Parses a length or a percentage, allowing the unitless length quirk.
2070    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
2071    #[inline]
2072    pub fn parse_quirky<'i, 't>(
2073        context: &ParserContext,
2074        input: &mut Parser<'i, 't>,
2075        allow_quirks: AllowQuirks,
2076    ) -> Result<Self, ParseError<'i>> {
2077        Self::parse_with(context, input, |context, input| {
2078            LengthPercentage::parse_quirky(context, input, allow_quirks)
2079        })
2080    }
2081}
2082
2083/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
2084pub type NonNegativeLengthPercentageOrAuto =
2085    generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
2086
2087impl NonNegativeLengthPercentageOrAuto {
2088    /// Returns a value representing `0%`.
2089    #[inline]
2090    pub fn zero_percent() -> Self {
2091        generics::LengthPercentageOrAuto::LengthPercentage(
2092            NonNegativeLengthPercentage::zero_percent(),
2093        )
2094    }
2095
2096    /// Parses a non-negative length-percentage, allowing the unitless length
2097    /// quirk.
2098    #[inline]
2099    pub fn parse_quirky<'i, 't>(
2100        context: &ParserContext,
2101        input: &mut Parser<'i, 't>,
2102        allow_quirks: AllowQuirks,
2103    ) -> Result<Self, ParseError<'i>> {
2104        Self::parse_with(context, input, |context, input| {
2105            NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
2106        })
2107    }
2108}
2109
2110/// A wrapper of LengthPercentage, whose value must be >= 0.
2111pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
2112
2113/// Either a NonNegativeLengthPercentage or the `normal` keyword.
2114pub type NonNegativeLengthPercentageOrNormal =
2115    GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
2116
2117impl From<NoCalcLength> for NonNegativeLengthPercentage {
2118    #[inline]
2119    fn from(len: NoCalcLength) -> Self {
2120        NonNegative(LengthPercentage::from(len))
2121    }
2122}
2123
2124impl Parse for NonNegativeLengthPercentage {
2125    #[inline]
2126    fn parse<'i, 't>(
2127        context: &ParserContext,
2128        input: &mut Parser<'i, 't>,
2129    ) -> Result<Self, ParseError<'i>> {
2130        Self::parse_quirky(context, input, AllowQuirks::No)
2131    }
2132}
2133
2134impl NonNegativeLengthPercentage {
2135    #[inline]
2136    /// Returns a `0%` value.
2137    pub fn zero_percent() -> Self {
2138        NonNegative(LengthPercentage::zero_percent())
2139    }
2140
2141    /// Parses a length or a percentage, allowing the unitless length quirk.
2142    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
2143    #[inline]
2144    pub fn parse_quirky<'i, 't>(
2145        context: &ParserContext,
2146        input: &mut Parser<'i, 't>,
2147        allow_quirks: AllowQuirks,
2148    ) -> Result<Self, ParseError<'i>> {
2149        LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
2150    }
2151
2152    /// Parses a length or a percentage, allowing the unitless length quirk,
2153    /// as well as allowing `anchor-size()`.
2154    #[inline]
2155    pub fn parse_non_negative_with_anchor_size<'i, 't>(
2156        context: &ParserContext,
2157        input: &mut Parser<'i, 't>,
2158        allow_quirks: AllowQuirks,
2159    ) -> Result<Self, ParseError<'i>> {
2160        LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)
2161            .map(NonNegative)
2162    }
2163}
2164
2165/// Either a `<length>` or the `auto` keyword.
2166///
2167/// Note that we use LengthPercentage just for convenience, since it pretty much
2168/// is everything we care about, but we could just add a similar LengthOrAuto
2169/// instead if we think getting rid of this weirdness is worth it.
2170pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
2171
2172impl LengthOrAuto {
2173    /// Parses a length, allowing the unitless length quirk.
2174    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
2175    #[inline]
2176    pub fn parse_quirky<'i, 't>(
2177        context: &ParserContext,
2178        input: &mut Parser<'i, 't>,
2179        allow_quirks: AllowQuirks,
2180    ) -> Result<Self, ParseError<'i>> {
2181        Self::parse_with(context, input, |context, input| {
2182            Length::parse_quirky(context, input, allow_quirks)
2183        })
2184    }
2185}
2186
2187/// Either a non-negative `<length>` or the `auto` keyword.
2188pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
2189
2190/// Either a `<length>` or a `<number>`.
2191pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
2192
2193/// A specified value for `min-width`, `min-height`, `width` or `height` property.
2194pub type Size = GenericSize<NonNegativeLengthPercentage>;
2195
2196impl Parse for Size {
2197    fn parse<'i, 't>(
2198        context: &ParserContext,
2199        input: &mut Parser<'i, 't>,
2200    ) -> Result<Self, ParseError<'i>> {
2201        Size::parse_quirky(context, input, AllowQuirks::No)
2202    }
2203}
2204
2205macro_rules! parse_size_non_length {
2206    ($size:ident, $input:expr, $allow_webkit_fill_available:expr,
2207     $auto_or_none:expr => $auto_or_none_ident:ident) => {{
2208        let size = $input.try_parse(|input| {
2209            Ok(try_match_ident_ignore_ascii_case! { input,
2210                "min-content" | "-moz-min-content" => $size::MinContent,
2211                "max-content" | "-moz-max-content" => $size::MaxContent,
2212                "fit-content" | "-moz-fit-content" => $size::FitContent,
2213                #[cfg(feature = "gecko")]
2214                "-moz-available" => $size::MozAvailable,
2215                "-webkit-fill-available" if $allow_webkit_fill_available => $size::WebkitFillAvailable,
2216                "stretch" if is_stretch_enabled() => $size::Stretch,
2217                $auto_or_none => $size::$auto_or_none_ident,
2218            })
2219        });
2220        if size.is_ok() {
2221            return size;
2222        }
2223    }};
2224}
2225
2226fn is_webkit_fill_available_enabled_in_width_and_height() -> bool {
2227    static_prefs::pref!("layout.css.webkit-fill-available.enabled")
2228}
2229
2230fn is_webkit_fill_available_enabled_in_all_size_properties() -> bool {
2231    // For convenience at the callsites, we check both prefs here,
2232    // since both must be 'true' in order for the keyword to be
2233    // enabled in all size properties.
2234    static_prefs::pref!("layout.css.webkit-fill-available.enabled")
2235        && static_prefs::pref!("layout.css.webkit-fill-available.all-size-properties.enabled")
2236}
2237
2238fn is_stretch_enabled() -> bool {
2239    static_prefs::pref!("layout.css.stretch-size-keyword.enabled")
2240}
2241
2242fn is_fit_content_function_enabled() -> bool {
2243    static_prefs::pref!("layout.css.fit-content-function.enabled")
2244}
2245
2246macro_rules! parse_fit_content_function {
2247    ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
2248        if is_fit_content_function_enabled() {
2249            if let Ok(length) = $input.try_parse(|input| {
2250                input.expect_function_matching("fit-content")?;
2251                input.parse_nested_block(|i| {
2252                    NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
2253                })
2254            }) {
2255                return Ok($size::FitContentFunction(length));
2256            }
2257        }
2258    };
2259}
2260
2261#[derive(Clone, Copy, PartialEq, Eq)]
2262enum ParseAnchorFunctions {
2263    Yes,
2264    No,
2265}
2266
2267impl Size {
2268    /// Parses, with quirks.
2269    pub fn parse_quirky<'i, 't>(
2270        context: &ParserContext,
2271        input: &mut Parser<'i, 't>,
2272        allow_quirks: AllowQuirks,
2273    ) -> Result<Self, ParseError<'i>> {
2274        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
2275        Self::parse_quirky_internal(
2276            context,
2277            input,
2278            allow_quirks,
2279            allow_webkit_fill_available,
2280            ParseAnchorFunctions::Yes,
2281        )
2282    }
2283
2284    /// Parses for flex-basis: <width>
2285    pub fn parse_size_for_flex_basis_width<'i, 't>(
2286        context: &ParserContext,
2287        input: &mut Parser<'i, 't>,
2288    ) -> Result<Self, ParseError<'i>> {
2289        Self::parse_quirky_internal(
2290            context,
2291            input,
2292            AllowQuirks::No,
2293            true,
2294            ParseAnchorFunctions::No,
2295        )
2296    }
2297
2298    /// Parses, with quirks and configurable support for
2299    /// whether the '-webkit-fill-available' keyword is allowed.
2300    /// TODO(dholbert) Fold this function into callsites in bug 1989073 when
2301    /// removing 'layout.css.webkit-fill-available.all-size-properties.enabled'.
2302    fn parse_quirky_internal<'i, 't>(
2303        context: &ParserContext,
2304        input: &mut Parser<'i, 't>,
2305        allow_quirks: AllowQuirks,
2306        allow_webkit_fill_available: bool,
2307        allow_anchor_functions: ParseAnchorFunctions,
2308    ) -> Result<Self, ParseError<'i>> {
2309        parse_size_non_length!(Size, input, allow_webkit_fill_available,
2310                               "auto" => Auto);
2311        parse_fit_content_function!(Size, input, context, allow_quirks);
2312
2313        let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes
2314            && static_prefs::pref!("layout.css.anchor-positioning.enabled");
2315        match input
2316            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
2317        {
2318            Ok(length) => return Ok(GenericSize::LengthPercentage(length)),
2319            Err(e) if !allow_anchor => return Err(e.into()),
2320            Err(_) => (),
2321        };
2322        if let Ok(length) = input.try_parse(|i| {
2323            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
2324                context,
2325                i,
2326                allow_quirks,
2327            )
2328        }) {
2329            return Ok(GenericSize::AnchorContainingCalcFunction(length));
2330        }
2331        Ok(Self::AnchorSizeFunction(Box::new(
2332            GenericAnchorSizeFunction::parse(context, input)?,
2333        )))
2334    }
2335
2336    /// Parse a size for width or height, where -webkit-fill-available
2337    /// support is only controlled by one pref (vs. other properties where
2338    /// there's an additional pref check):
2339    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with
2340    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.
2341    pub fn parse_size_for_width_or_height_quirky<'i, 't>(
2342        context: &ParserContext,
2343        input: &mut Parser<'i, 't>,
2344        allow_quirks: AllowQuirks,
2345    ) -> Result<Self, ParseError<'i>> {
2346        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
2347        Self::parse_quirky_internal(
2348            context,
2349            input,
2350            allow_quirks,
2351            allow_webkit_fill_available,
2352            ParseAnchorFunctions::Yes,
2353        )
2354    }
2355
2356    /// Parse a size for width or height, where -webkit-fill-available
2357    /// support is only controlled by one pref (vs. other properties where
2358    /// there's an additional pref check):
2359    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with
2360    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.
2361    pub fn parse_size_for_width_or_height<'i, 't>(
2362        context: &ParserContext,
2363        input: &mut Parser<'i, 't>,
2364    ) -> Result<Self, ParseError<'i>> {
2365        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
2366        Self::parse_quirky_internal(
2367            context,
2368            input,
2369            AllowQuirks::No,
2370            allow_webkit_fill_available,
2371            ParseAnchorFunctions::Yes,
2372        )
2373    }
2374
2375    /// Returns `0%`.
2376    #[inline]
2377    pub fn zero_percent() -> Self {
2378        GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
2379    }
2380}
2381
2382/// A specified value for `max-width` or `max-height` property.
2383pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
2384
2385impl Parse for MaxSize {
2386    fn parse<'i, 't>(
2387        context: &ParserContext,
2388        input: &mut Parser<'i, 't>,
2389    ) -> Result<Self, ParseError<'i>> {
2390        MaxSize::parse_quirky(context, input, AllowQuirks::No)
2391    }
2392}
2393
2394impl MaxSize {
2395    /// Parses, with quirks.
2396    pub fn parse_quirky<'i, 't>(
2397        context: &ParserContext,
2398        input: &mut Parser<'i, 't>,
2399        allow_quirks: AllowQuirks,
2400    ) -> Result<Self, ParseError<'i>> {
2401        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
2402        parse_size_non_length!(MaxSize, input, allow_webkit_fill_available,
2403                               "none" => None);
2404        parse_fit_content_function!(MaxSize, input, context, allow_quirks);
2405
2406        match input
2407            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
2408        {
2409            Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),
2410            Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2411                return Err(e.into())
2412            },
2413            Err(_) => (),
2414        };
2415        if let Ok(length) = input.try_parse(|i| {
2416            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
2417                context,
2418                i,
2419                allow_quirks,
2420            )
2421        }) {
2422            return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));
2423        }
2424        Ok(Self::AnchorSizeFunction(Box::new(
2425            GenericAnchorSizeFunction::parse(context, input)?,
2426        )))
2427    }
2428}
2429
2430/// A specified non-negative `<length>` | `<number>`.
2431pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
2432
2433/// A specified value for `margin` properties.
2434pub type Margin = GenericMargin<LengthPercentage>;
2435
2436impl Margin {
2437    /// Parses a margin type, allowing the unitless length quirk.
2438    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
2439    #[inline]
2440    pub fn parse_quirky<'i, 't>(
2441        context: &ParserContext,
2442        input: &mut Parser<'i, 't>,
2443        allow_quirks: AllowQuirks,
2444    ) -> Result<Self, ParseError<'i>> {
2445        if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2446        {
2447            return Ok(Self::LengthPercentage(l));
2448        }
2449        match input.try_parse(|i| i.expect_ident_matching("auto")) {
2450            Ok(_) => return Ok(Self::Auto),
2451            Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2452                return Err(e.into())
2453            },
2454            Err(_) => (),
2455        };
2456        if let Ok(l) = input.try_parse(|i| {
2457            LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)
2458        }) {
2459            return Ok(Self::AnchorContainingCalcFunction(l));
2460        }
2461        let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?;
2462        Ok(Self::AnchorSizeFunction(Box::new(inner)))
2463    }
2464}
2465
2466impl Parse for Margin {
2467    fn parse<'i, 't>(
2468        context: &ParserContext,
2469        input: &mut Parser<'i, 't>,
2470    ) -> Result<Self, ParseError<'i>> {
2471        Self::parse_quirky(context, input, AllowQuirks::No)
2472    }
2473}