style/values/specified/
length.rs

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