/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! [Length values][length].
//!
//! [length]: https://drafts.csswg.org/css-values/#lengths

use super::{AllowQuirks, Number, Percentage, ToComputedValue};
use crate::computed_value_flags::ComputedValueFlags;
use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::GeckoFontMetrics;
use crate::parser::{Parse, ParserContext};
use crate::values::computed::{self, CSSPixelLength, Context, FontSize};
use crate::values::generics::length as generics;
use crate::values::generics::length::{
    GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal,
    GenericMargin, GenericMaxSize, GenericSize,
};
use crate::values::generics::NonNegative;
use crate::values::specified::calc::{self, AllowAnchorPositioningFunctions, CalcNode};
use crate::values::specified::font::QueryFontMetricsFlags;
use crate::values::specified::NonNegativeNumber;
use crate::values::CSSFloat;
use crate::{Zero, ZeroNoPercent};
use app_units::AU_PER_PX;
use cssparser::{Parser, Token};
use std::cmp;
use std::fmt::{self, Write};
use style_traits::values::specified::AllowedNumericType;
use style_traits::{
    CssString, CssWriter, NumericValue, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
    ToTyped, TypedValue,
};

pub use super::image::Image;
pub use super::image::{EndingShape as GradientEndingShape, Gradient};
pub use crate::values::specified::calc::CalcLengthPercentage;

/// Number of pixels per inch
pub const PX_PER_IN: CSSFloat = 96.;
/// Number of pixels per centimeter
pub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54;
/// Number of pixels per millimeter
pub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4;
/// Number of pixels per quarter
pub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.;
/// Number of pixels per point
pub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.;
/// Number of pixels per pica
pub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.;

/// A font relative length. Note that if any new value is
/// added here, `custom_properties::NonCustomReferences::from_unit`
/// must also be updated. Consult the comment in that function as to why.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum FontRelativeLength {
    /// A "em" value: https://drafts.csswg.org/css-values/#em
    #[css(dimension)]
    Em(CSSFloat),
    /// A "ex" value: https://drafts.csswg.org/css-values/#ex
    #[css(dimension)]
    Ex(CSSFloat),
    /// A "rex" value: https://drafts.csswg.org/css-values/#rex
    #[css(dimension)]
    Rex(CSSFloat),
    /// A "ch" value: https://drafts.csswg.org/css-values/#ch
    #[css(dimension)]
    Ch(CSSFloat),
    /// A "rch" value: https://drafts.csswg.org/css-values/#rch
    #[css(dimension)]
    Rch(CSSFloat),
    /// A "cap" value: https://drafts.csswg.org/css-values/#cap
    #[css(dimension)]
    Cap(CSSFloat),
    /// A "rcap" value: https://drafts.csswg.org/css-values/#rcap
    #[css(dimension)]
    Rcap(CSSFloat),
    /// An "ic" value: https://drafts.csswg.org/css-values/#ic
    #[css(dimension)]
    Ic(CSSFloat),
    /// A "ric" value: https://drafts.csswg.org/css-values/#ric
    #[css(dimension)]
    Ric(CSSFloat),
    /// A "rem" value: https://drafts.csswg.org/css-values/#rem
    #[css(dimension)]
    Rem(CSSFloat),
    /// A "lh" value: https://drafts.csswg.org/css-values/#lh
    #[css(dimension)]
    Lh(CSSFloat),
    /// A "rlh" value: https://drafts.csswg.org/css-values/#rlh
    #[css(dimension)]
    Rlh(CSSFloat),
}

/// A source to resolve font-relative units against
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FontBaseSize {
    /// Use the font-size of the current element.
    CurrentStyle,
    /// Use the inherited font-size.
    InheritedStyle,
}

/// A source to resolve font-relative line-height units against.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LineHeightBase {
    /// Use the line-height of the current element.
    CurrentStyle,
    /// Use the inherited line-height.
    InheritedStyle,
}

impl FontBaseSize {
    /// Calculate the actual size for a given context
    pub fn resolve(&self, context: &Context) -> computed::FontSize {
        let style = context.style();
        match *self {
            Self::CurrentStyle => style.get_font().clone_font_size(),
            Self::InheritedStyle => {
                // If we're using the size from our inherited style, we still need to apply our
                // own zoom.
                let zoom = style.effective_zoom_for_inheritance;
                style.get_parent_font().clone_font_size().zoom(zoom)
            },
        }
    }
}

impl FontRelativeLength {
    /// Unit identifier for `em`.
    pub const EM: &'static str = "em";
    /// Unit identifier for `ex`.
    pub const EX: &'static str = "ex";
    /// Unit identifier for `rex`.
    pub const REX: &'static str = "rex";
    /// Unit identifier for `ch`.
    pub const CH: &'static str = "ch";
    /// Unit identifier for `rch`.
    pub const RCH: &'static str = "rch";
    /// Unit identifier for `cap`.
    pub const CAP: &'static str = "cap";
    /// Unit identifier for `rcap`.
    pub const RCAP: &'static str = "rcap";
    /// Unit identifier for `ic`.
    pub const IC: &'static str = "ic";
    /// Unit identifier for `ric`.
    pub const RIC: &'static str = "ric";
    /// Unit identifier for `rem`.
    pub const REM: &'static str = "rem";
    /// Unit identifier for `lh`.
    pub const LH: &'static str = "lh";
    /// Unit identifier for `rlh`.
    pub const RLH: &'static str = "rlh";

    /// Return the unitless, raw value.
    fn unitless_value(&self) -> CSSFloat {
        match *self {
            Self::Em(v)
            | Self::Ex(v)
            | Self::Rex(v)
            | Self::Ch(v)
            | Self::Rch(v)
            | Self::Cap(v)
            | Self::Rcap(v)
            | Self::Ic(v)
            | Self::Ric(v)
            | Self::Rem(v)
            | Self::Lh(v)
            | Self::Rlh(v) => v,
        }
    }

    // Return the unit, as a string.
    fn unit(&self) -> &'static str {
        match *self {
            Self::Em(_) => Self::EM,
            Self::Ex(_) => Self::EX,
            Self::Rex(_) => Self::REX,
            Self::Ch(_) => Self::CH,
            Self::Rch(_) => Self::RCH,
            Self::Cap(_) => Self::CAP,
            Self::Rcap(_) => Self::RCAP,
            Self::Ic(_) => Self::IC,
            Self::Ric(_) => Self::RIC,
            Self::Rem(_) => Self::REM,
            Self::Lh(_) => Self::LH,
            Self::Rlh(_) => Self::RLH,
        }
    }

    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
    where
        O: Fn(f32, f32) -> f32,
    {
        use self::FontRelativeLength::*;

        if std::mem::discriminant(self) != std::mem::discriminant(other) {
            return Err(());
        }

        Ok(match (self, other) {
            (&Em(one), &Em(other)) => Em(op(one, other)),
            (&Ex(one), &Ex(other)) => Ex(op(one, other)),
            (&Rex(one), &Rex(other)) => Rex(op(one, other)),
            (&Ch(one), &Ch(other)) => Ch(op(one, other)),
            (&Rch(one), &Rch(other)) => Rch(op(one, other)),
            (&Cap(one), &Cap(other)) => Cap(op(one, other)),
            (&Rcap(one), &Rcap(other)) => Rcap(op(one, other)),
            (&Ic(one), &Ic(other)) => Ic(op(one, other)),
            (&Ric(one), &Ric(other)) => Ric(op(one, other)),
            (&Rem(one), &Rem(other)) => Rem(op(one, other)),
            (&Lh(one), &Lh(other)) => Lh(op(one, other)),
            (&Rlh(one), &Rlh(other)) => Rlh(op(one, other)),
            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
            // able to figure it own on its own so we help.
            _ => unsafe {
                match *self {
                    Em(..) | Rem(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..)
                    | Ic(..) | Ric(..) | Lh(..) | Rlh(..) => {},
                }
                debug_unreachable!("Forgot to handle unit in try_op()")
            },
        })
    }

    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
        match self {
            Self::Em(x) => Self::Em(op(*x)),
            Self::Ex(x) => Self::Ex(op(*x)),
            Self::Rex(x) => Self::Rex(op(*x)),
            Self::Ch(x) => Self::Ch(op(*x)),
            Self::Rch(x) => Self::Rch(op(*x)),
            Self::Cap(x) => Self::Cap(op(*x)),
            Self::Rcap(x) => Self::Rcap(op(*x)),
            Self::Ic(x) => Self::Ic(op(*x)),
            Self::Ric(x) => Self::Ric(op(*x)),
            Self::Rem(x) => Self::Rem(op(*x)),
            Self::Lh(x) => Self::Lh(op(*x)),
            Self::Rlh(x) => Self::Rlh(op(*x)),
        }
    }

    /// Computes the font-relative length.
    pub fn to_computed_value(
        &self,
        context: &Context,
        base_size: FontBaseSize,
        line_height_base: LineHeightBase,
    ) -> computed::Length {
        let (reference_size, length) =
            self.reference_font_size_and_length(context, base_size, line_height_base);
        (reference_size * length).finite()
    }

    /// Computes the length, given a GeckoFontMetrics getter to resolve font-relative units.
    #[cfg(feature = "gecko")]
    pub fn to_computed_pixel_length_with_font_metrics(
        &self,
        get_font_metrics: impl Fn() -> GeckoFontMetrics,
    ) -> Result<CSSFloat, ()> {
        let metrics = get_font_metrics();
        Ok(match *self {
            Self::Em(v) => v * metrics.mComputedEmSize.px(),
            Self::Ex(v) => v * metrics.mXSize.px(),
            Self::Ch(v) => v * metrics.mChSize.px(),
            Self::Cap(v) => v * metrics.mCapHeight.px(),
            Self::Ic(v) => v * metrics.mIcWidth.px(),
            // `lh`, `rlh` are unsupported as we have no line-height context
            // `rem`, `rex`, `rch`, `rcap`, and `ric` are unsupported as we have no root font context.
            Self::Lh(_)
            | Self::Rlh(_)
            | Self::Rem(_)
            | Self::Rex(_)
            | Self::Rch(_)
            | Self::Rcap(_)
            | Self::Ric(_) => return Err(()),
        })
    }

    /// Return reference font size.
    ///
    /// We use the base_size flag to pass a different size for computing
    /// font-size and unconstrained font-size.
    ///
    /// This returns a pair, the first one is the reference font size, and the
    /// second one is the unpacked relative length.
    fn reference_font_size_and_length(
        &self,
        context: &Context,
        base_size: FontBaseSize,
        line_height_base: LineHeightBase,
    ) -> (computed::Length, CSSFloat) {
        fn query_font_metrics(
            context: &Context,
            base_size: FontBaseSize,
            orientation: FontMetricsOrientation,
            flags: QueryFontMetricsFlags,
        ) -> FontMetrics {
            context.query_font_metrics(base_size, orientation, flags)
        }

        fn ex_size(
            context: &Context,
            base_size: FontBaseSize,
            reference_font_size: &FontSize,
        ) -> computed::Length {
            // The x-height is an intrinsically horizontal metric.
            let metrics = query_font_metrics(
                context,
                base_size,
                FontMetricsOrientation::Horizontal,
                QueryFontMetricsFlags::empty(),
            );
            metrics.x_height_or_default(reference_font_size.used_size())
        }

        fn ch_size(
            context: &Context,
            base_size: FontBaseSize,
            reference_font_size: &FontSize,
        ) -> computed::Length {
            // https://drafts.csswg.org/css-values/#ch:
            //
            //     Equal to the used advance measure of the “0” (ZERO,
            //     U+0030) glyph in the font used to render it. (The advance
            //     measure of a glyph is its advance width or height,
            //     whichever is in the inline axis of the element.)
            //
            let metrics = query_font_metrics(
                context,
                base_size,
                FontMetricsOrientation::MatchContextPreferHorizontal,
                QueryFontMetricsFlags::NEEDS_CH,
            );
            metrics.zero_advance_measure_or_default(
                reference_font_size.used_size(),
                context.style().writing_mode.is_upright(),
            )
        }

        fn cap_size(context: &Context, base_size: FontBaseSize) -> computed::Length {
            let metrics = query_font_metrics(
                context,
                base_size,
                FontMetricsOrientation::Horizontal,
                QueryFontMetricsFlags::empty(),
            );
            metrics.cap_height_or_default()
        }

        fn ic_size(
            context: &Context,
            base_size: FontBaseSize,
            reference_font_size: &FontSize,
        ) -> computed::Length {
            let metrics = query_font_metrics(
                context,
                base_size,
                FontMetricsOrientation::MatchContextPreferVertical,
                QueryFontMetricsFlags::NEEDS_IC,
            );
            metrics.ic_width_or_default(reference_font_size.used_size())
        }

        let reference_font_size = base_size.resolve(context);
        match *self {
            // Local font-relative units
            Self::Em(length) => {
                if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle {
                    context
                        .rule_cache_conditions
                        .borrow_mut()
                        .set_font_size_dependency(reference_font_size.computed_size);
                }

                (reference_font_size.computed_size(), length)
            },
            Self::Lh(length) => {
                // https://drafts.csswg.org/css-values-4/#lh
                //
                //     When specified in media-query, the lh units refer to the
                //     initial values of font and line-height properties.
                //
                let reference_size = if context.in_media_query {
                    context
                        .device()
                        .calc_line_height(
                            &context.default_style().get_font(),
                            context.style().writing_mode,
                            None,
                        )
                        .0
                } else {
                    let line_height = context.builder.calc_line_height(
                        context.device(),
                        line_height_base,
                        context.style().writing_mode,
                    );
                    if context.for_non_inherited_property
                        && line_height_base == LineHeightBase::CurrentStyle
                    {
                        context
                            .rule_cache_conditions
                            .borrow_mut()
                            .set_line_height_dependency(line_height)
                    }
                    line_height.0
                };
                (reference_size, length)
            },
            Self::Ex(length) => (ex_size(context, base_size, &reference_font_size), length),
            Self::Ch(length) => (ch_size(context, base_size, &reference_font_size), length),
            Self::Cap(length) => (cap_size(context, base_size), length),
            Self::Ic(length) => (ic_size(context, base_size, &reference_font_size), length),

            // Root font relative units
            Self::Rex(length) => {
                let reference_size = if context.builder.is_root_element || context.in_media_query {
                    ex_size(context, base_size, &reference_font_size)
                } else {
                    context
                        .device()
                        .root_font_metrics_ex()
                        .zoom(context.builder.effective_zoom)
                };
                (reference_size, length)
            },
            Self::Rch(length) => {
                let reference_size = if context.builder.is_root_element || context.in_media_query {
                    ch_size(context, base_size, &reference_font_size)
                } else {
                    context
                        .device()
                        .root_font_metrics_ch()
                        .zoom(context.builder.effective_zoom)
                };
                (reference_size, length)
            },
            Self::Rcap(length) => {
                let reference_size = if context.builder.is_root_element || context.in_media_query {
                    cap_size(context, base_size)
                } else {
                    context
                        .device()
                        .root_font_metrics_cap()
                        .zoom(context.builder.effective_zoom)
                };
                (reference_size, length)
            },
            Self::Ric(length) => {
                let reference_size = if context.builder.is_root_element || context.in_media_query {
                    ic_size(context, base_size, &reference_font_size)
                } else {
                    context
                        .device()
                        .root_font_metrics_ic()
                        .zoom(context.builder.effective_zoom)
                };
                (reference_size, length)
            },
            Self::Rem(length) => {
                // https://drafts.csswg.org/css-values/#rem:
                //
                //     When specified on the font-size property of the root
                //     element, the rem units refer to the property's initial
                //     value.
                //
                let reference_size = if context.builder.is_root_element || context.in_media_query {
                    reference_font_size.computed_size()
                } else {
                    context
                        .device()
                        .root_font_size()
                        .zoom(context.builder.effective_zoom)
                };
                (reference_size, length)
            },
            Self::Rlh(length) => {
                // https://drafts.csswg.org/css-values-4/#rlh
                //
                //     When specified on the root element, the rlh units refer
                //     to the initial values of font and line-height properties.
                //
                let reference_size = if context.builder.is_root_element {
                    context
                        .builder
                        .calc_line_height(
                            context.device(),
                            line_height_base,
                            context.style().writing_mode,
                        )
                        .0
                } else if context.in_media_query {
                    context
                        .device()
                        .calc_line_height(
                            &context.default_style().get_font(),
                            context.style().writing_mode,
                            None,
                        )
                        .0
                } else {
                    context.device().root_line_height()
                };
                let reference_size = reference_size.zoom(context.builder.effective_zoom);
                (reference_size, length)
            },
        }
    }
}

/// https://drafts.csswg.org/css-values/#viewport-variants
pub enum ViewportVariant {
    /// https://drafts.csswg.org/css-values/#ua-default-viewport-size
    UADefault,
    /// https://drafts.csswg.org/css-values/#small-viewport-percentage-units
    Small,
    /// https://drafts.csswg.org/css-values/#large-viewport-percentage-units
    Large,
    /// https://drafts.csswg.org/css-values/#dynamic-viewport-percentage-units
    Dynamic,
}

/// https://drafts.csswg.org/css-values/#viewport-relative-units
#[derive(PartialEq)]
enum ViewportUnit {
    /// *vw units.
    Vw,
    /// *vh units.
    Vh,
    /// *vmin units.
    Vmin,
    /// *vmax units.
    Vmax,
    /// *vb units.
    Vb,
    /// *vi units.
    Vi,
}

/// A viewport-relative length.
///
/// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum ViewportPercentageLength {
    /// <https://drafts.csswg.org/css-values/#valdef-length-vw>
    #[css(dimension)]
    Vw(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-svw>
    #[css(dimension)]
    Svw(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-lvw>
    #[css(dimension)]
    Lvw(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-dvw>
    #[css(dimension)]
    Dvw(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-vh>
    #[css(dimension)]
    Vh(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-svh>
    #[css(dimension)]
    Svh(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-lvh>
    #[css(dimension)]
    Lvh(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-dvh>
    #[css(dimension)]
    Dvh(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-vmin>
    #[css(dimension)]
    Vmin(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-svmin>
    #[css(dimension)]
    Svmin(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-lvmin>
    #[css(dimension)]
    Lvmin(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-dvmin>
    #[css(dimension)]
    Dvmin(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-vmax>
    #[css(dimension)]
    Vmax(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-svmax>
    #[css(dimension)]
    Svmax(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-lvmax>
    #[css(dimension)]
    Lvmax(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-dvmax>
    #[css(dimension)]
    Dvmax(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-vb>
    #[css(dimension)]
    Vb(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-svb>
    #[css(dimension)]
    Svb(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-lvb>
    #[css(dimension)]
    Lvb(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-dvb>
    #[css(dimension)]
    Dvb(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-vi>
    #[css(dimension)]
    Vi(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-svi>
    #[css(dimension)]
    Svi(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-lvi>
    #[css(dimension)]
    Lvi(CSSFloat),
    /// <https://drafts.csswg.org/css-values/#valdef-length-dvi>
    #[css(dimension)]
    Dvi(CSSFloat),
}

impl ViewportPercentageLength {
    /// Return the unitless, raw value.
    fn unitless_value(&self) -> CSSFloat {
        self.unpack().2
    }

    // Return the unit, as a string.
    fn unit(&self) -> &'static str {
        match *self {
            Self::Vw(_) => "vw",
            Self::Lvw(_) => "lvw",
            Self::Svw(_) => "svw",
            Self::Dvw(_) => "dvw",
            Self::Vh(_) => "vh",
            Self::Svh(_) => "svh",
            Self::Lvh(_) => "lvh",
            Self::Dvh(_) => "dvh",
            Self::Vmin(_) => "vmin",
            Self::Svmin(_) => "svmin",
            Self::Lvmin(_) => "lvmin",
            Self::Dvmin(_) => "dvmin",
            Self::Vmax(_) => "vmax",
            Self::Svmax(_) => "svmax",
            Self::Lvmax(_) => "lvmax",
            Self::Dvmax(_) => "dvmax",
            Self::Vb(_) => "vb",
            Self::Svb(_) => "svb",
            Self::Lvb(_) => "lvb",
            Self::Dvb(_) => "dvb",
            Self::Vi(_) => "vi",
            Self::Svi(_) => "svi",
            Self::Lvi(_) => "lvi",
            Self::Dvi(_) => "dvi",
        }
    }

    fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) {
        match *self {
            Self::Vw(v) => (ViewportVariant::UADefault, ViewportUnit::Vw, v),
            Self::Svw(v) => (ViewportVariant::Small, ViewportUnit::Vw, v),
            Self::Lvw(v) => (ViewportVariant::Large, ViewportUnit::Vw, v),
            Self::Dvw(v) => (ViewportVariant::Dynamic, ViewportUnit::Vw, v),
            Self::Vh(v) => (ViewportVariant::UADefault, ViewportUnit::Vh, v),
            Self::Svh(v) => (ViewportVariant::Small, ViewportUnit::Vh, v),
            Self::Lvh(v) => (ViewportVariant::Large, ViewportUnit::Vh, v),
            Self::Dvh(v) => (ViewportVariant::Dynamic, ViewportUnit::Vh, v),
            Self::Vmin(v) => (ViewportVariant::UADefault, ViewportUnit::Vmin, v),
            Self::Svmin(v) => (ViewportVariant::Small, ViewportUnit::Vmin, v),
            Self::Lvmin(v) => (ViewportVariant::Large, ViewportUnit::Vmin, v),
            Self::Dvmin(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmin, v),
            Self::Vmax(v) => (ViewportVariant::UADefault, ViewportUnit::Vmax, v),
            Self::Svmax(v) => (ViewportVariant::Small, ViewportUnit::Vmax, v),
            Self::Lvmax(v) => (ViewportVariant::Large, ViewportUnit::Vmax, v),
            Self::Dvmax(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmax, v),
            Self::Vb(v) => (ViewportVariant::UADefault, ViewportUnit::Vb, v),
            Self::Svb(v) => (ViewportVariant::Small, ViewportUnit::Vb, v),
            Self::Lvb(v) => (ViewportVariant::Large, ViewportUnit::Vb, v),
            Self::Dvb(v) => (ViewportVariant::Dynamic, ViewportUnit::Vb, v),
            Self::Vi(v) => (ViewportVariant::UADefault, ViewportUnit::Vi, v),
            Self::Svi(v) => (ViewportVariant::Small, ViewportUnit::Vi, v),
            Self::Lvi(v) => (ViewportVariant::Large, ViewportUnit::Vi, v),
            Self::Dvi(v) => (ViewportVariant::Dynamic, ViewportUnit::Vi, v),
        }
    }

    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
    where
        O: Fn(f32, f32) -> f32,
    {
        use self::ViewportPercentageLength::*;

        if std::mem::discriminant(self) != std::mem::discriminant(other) {
            return Err(());
        }

        Ok(match (self, other) {
            (&Vw(one), &Vw(other)) => Vw(op(one, other)),
            (&Svw(one), &Svw(other)) => Svw(op(one, other)),
            (&Lvw(one), &Lvw(other)) => Lvw(op(one, other)),
            (&Dvw(one), &Dvw(other)) => Dvw(op(one, other)),
            (&Vh(one), &Vh(other)) => Vh(op(one, other)),
            (&Svh(one), &Svh(other)) => Svh(op(one, other)),
            (&Lvh(one), &Lvh(other)) => Lvh(op(one, other)),
            (&Dvh(one), &Dvh(other)) => Dvh(op(one, other)),
            (&Vmin(one), &Vmin(other)) => Vmin(op(one, other)),
            (&Svmin(one), &Svmin(other)) => Svmin(op(one, other)),
            (&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)),
            (&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)),
            (&Vmax(one), &Vmax(other)) => Vmax(op(one, other)),
            (&Svmax(one), &Svmax(other)) => Svmax(op(one, other)),
            (&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)),
            (&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)),
            (&Vb(one), &Vb(other)) => Vb(op(one, other)),
            (&Svb(one), &Svb(other)) => Svb(op(one, other)),
            (&Lvb(one), &Lvb(other)) => Lvb(op(one, other)),
            (&Dvb(one), &Dvb(other)) => Dvb(op(one, other)),
            (&Vi(one), &Vi(other)) => Vi(op(one, other)),
            (&Svi(one), &Svi(other)) => Svi(op(one, other)),
            (&Lvi(one), &Lvi(other)) => Lvi(op(one, other)),
            (&Dvi(one), &Dvi(other)) => Dvi(op(one, other)),
            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
            // able to figure it own on its own so we help.
            _ => unsafe {
                match *self {
                    Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)
                    | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)
                    | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)
                    | Svi(..) | Lvi(..) | Dvi(..) => {},
                }
                debug_unreachable!("Forgot to handle unit in try_op()")
            },
        })
    }

    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
        match self {
            Self::Vw(x) => Self::Vw(op(*x)),
            Self::Svw(x) => Self::Svw(op(*x)),
            Self::Lvw(x) => Self::Lvw(op(*x)),
            Self::Dvw(x) => Self::Dvw(op(*x)),
            Self::Vh(x) => Self::Vh(op(*x)),
            Self::Svh(x) => Self::Svh(op(*x)),
            Self::Lvh(x) => Self::Lvh(op(*x)),
            Self::Dvh(x) => Self::Dvh(op(*x)),
            Self::Vmin(x) => Self::Vmin(op(*x)),
            Self::Svmin(x) => Self::Svmin(op(*x)),
            Self::Lvmin(x) => Self::Lvmin(op(*x)),
            Self::Dvmin(x) => Self::Dvmin(op(*x)),
            Self::Vmax(x) => Self::Vmax(op(*x)),
            Self::Svmax(x) => Self::Svmax(op(*x)),
            Self::Lvmax(x) => Self::Lvmax(op(*x)),
            Self::Dvmax(x) => Self::Dvmax(op(*x)),
            Self::Vb(x) => Self::Vb(op(*x)),
            Self::Svb(x) => Self::Svb(op(*x)),
            Self::Lvb(x) => Self::Lvb(op(*x)),
            Self::Dvb(x) => Self::Dvb(op(*x)),
            Self::Vi(x) => Self::Vi(op(*x)),
            Self::Svi(x) => Self::Svi(op(*x)),
            Self::Lvi(x) => Self::Lvi(op(*x)),
            Self::Dvi(x) => Self::Dvi(op(*x)),
        }
    }

    /// Computes the given viewport-relative length for the given viewport size.
    pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
        let (variant, unit, factor) = self.unpack();
        let size = context.viewport_size_for_viewport_unit_resolution(variant);
        let length: app_units::Au = match unit {
            ViewportUnit::Vw => size.width,
            ViewportUnit::Vh => size.height,
            ViewportUnit::Vmin => cmp::min(size.width, size.height),
            ViewportUnit::Vmax => cmp::max(size.width, size.height),
            ViewportUnit::Vi | ViewportUnit::Vb => {
                context
                    .rule_cache_conditions
                    .borrow_mut()
                    .set_writing_mode_dependency(context.builder.writing_mode);
                if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() {
                    size.width
                } else {
                    size.height
                }
            },
        };

        // NOTE: This is in app units!
        let length = context.builder.effective_zoom.zoom(length.0 as f32);

        // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform.
        // See bug 989802. We truncate so that adding multiple viewport units that add up to 100
        // does not overflow due to rounding differences. We convert appUnits to CSS px manually
        // here to avoid premature clamping by going through the Au type.
        let trunc_scaled =
            ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;
        CSSPixelLength::new(crate::values::normalize(trunc_scaled))
    }
}

/// HTML5 "character width", as defined in HTML5 § 14.5.4.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(C)]
pub struct CharacterWidth(pub i32);

impl CharacterWidth {
    /// Computes the given character width.
    pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length {
        // This applies the *converting a character width to pixels* algorithm
        // as specified in HTML5 § 14.5.4.
        //
        // TODO(pcwalton): Find these from the font.
        let average_advance = reference_font_size * 0.5;
        let max_advance = reference_font_size;
        (average_advance * (self.0 as CSSFloat - 1.0) + max_advance).finite()
    }
}

/// Represents an absolute length with its unit
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum AbsoluteLength {
    /// An absolute length in pixels (px)
    #[css(dimension)]
    Px(CSSFloat),
    /// An absolute length in inches (in)
    #[css(dimension)]
    In(CSSFloat),
    /// An absolute length in centimeters (cm)
    #[css(dimension)]
    Cm(CSSFloat),
    /// An absolute length in millimeters (mm)
    #[css(dimension)]
    Mm(CSSFloat),
    /// An absolute length in quarter-millimeters (q)
    #[css(dimension)]
    Q(CSSFloat),
    /// An absolute length in points (pt)
    #[css(dimension)]
    Pt(CSSFloat),
    /// An absolute length in pica (pc)
    #[css(dimension)]
    Pc(CSSFloat),
}

impl AbsoluteLength {
    /// Return the unitless, raw value.
    fn unitless_value(&self) -> CSSFloat {
        match *self {
            Self::Px(v)
            | Self::In(v)
            | Self::Cm(v)
            | Self::Mm(v)
            | Self::Q(v)
            | Self::Pt(v)
            | Self::Pc(v) => v,
        }
    }

    // Return the unit, as a string.
    fn unit(&self) -> &'static str {
        match *self {
            Self::Px(_) => "px",
            Self::In(_) => "in",
            Self::Cm(_) => "cm",
            Self::Mm(_) => "mm",
            Self::Q(_) => "q",
            Self::Pt(_) => "pt",
            Self::Pc(_) => "pc",
        }
    }

    /// Convert this into a pixel value.
    #[inline]
    pub fn to_px(&self) -> CSSFloat {
        match *self {
            Self::Px(value) => value,
            Self::In(value) => value * PX_PER_IN,
            Self::Cm(value) => value * PX_PER_CM,
            Self::Mm(value) => value * PX_PER_MM,
            Self::Q(value) => value * PX_PER_Q,
            Self::Pt(value) => value * PX_PER_PT,
            Self::Pc(value) => value * PX_PER_PC,
        }
    }

    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
    where
        O: Fn(f32, f32) -> f32,
    {
        Ok(Self::Px(op(self.to_px(), other.to_px())))
    }

    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
        Self::Px(op(self.to_px()))
    }
}

impl ToComputedValue for AbsoluteLength {
    type ComputedValue = CSSPixelLength;

    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        CSSPixelLength::new(self.to_px())
            .zoom(context.builder.effective_zoom)
            .finite()
    }

    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        Self::Px(computed.px())
    }
}

impl PartialOrd for AbsoluteLength {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.to_px().partial_cmp(&other.to_px())
    }
}

/// A container query length.
///
/// <https://drafts.csswg.org/css-contain-3/#container-lengths>
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum ContainerRelativeLength {
    /// 1% of query container's width
    #[css(dimension)]
    Cqw(CSSFloat),
    /// 1% of query container's height
    #[css(dimension)]
    Cqh(CSSFloat),
    /// 1% of query container's inline size
    #[css(dimension)]
    Cqi(CSSFloat),
    /// 1% of query container's block size
    #[css(dimension)]
    Cqb(CSSFloat),
    /// The smaller value of `cqi` or `cqb`
    #[css(dimension)]
    Cqmin(CSSFloat),
    /// The larger value of `cqi` or `cqb`
    #[css(dimension)]
    Cqmax(CSSFloat),
}

impl ContainerRelativeLength {
    fn unitless_value(&self) -> CSSFloat {
        match *self {
            Self::Cqw(v)
            | Self::Cqh(v)
            | Self::Cqi(v)
            | Self::Cqb(v)
            | Self::Cqmin(v)
            | Self::Cqmax(v) => v,
        }
    }

    // Return the unit, as a string.
    fn unit(&self) -> &'static str {
        match *self {
            Self::Cqw(_) => "cqw",
            Self::Cqh(_) => "cqh",
            Self::Cqi(_) => "cqi",
            Self::Cqb(_) => "cqb",
            Self::Cqmin(_) => "cqmin",
            Self::Cqmax(_) => "cqmax",
        }
    }

    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
    where
        O: Fn(f32, f32) -> f32,
    {
        use self::ContainerRelativeLength::*;

        if std::mem::discriminant(self) != std::mem::discriminant(other) {
            return Err(());
        }

        Ok(match (self, other) {
            (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)),
            (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)),
            (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)),
            (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)),
            (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)),
            (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)),

            // See https://github.com/rust-lang/rust/issues/68867, then
            // https://github.com/rust-lang/rust/pull/95161. rustc isn't
            // able to figure it own on its own so we help.
            _ => unsafe {
                match *self {
                    Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
                }
                debug_unreachable!("Forgot to handle unit in try_op()")
            },
        })
    }

    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
        match self {
            Self::Cqw(x) => Self::Cqw(op(*x)),
            Self::Cqh(x) => Self::Cqh(op(*x)),
            Self::Cqi(x) => Self::Cqi(op(*x)),
            Self::Cqb(x) => Self::Cqb(op(*x)),
            Self::Cqmin(x) => Self::Cqmin(op(*x)),
            Self::Cqmax(x) => Self::Cqmax(op(*x)),
        }
    }

    /// Computes the given container-relative length.
    pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
        if context.for_non_inherited_property {
            context.rule_cache_conditions.borrow_mut().set_uncacheable();
        }
        context
            .builder
            .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);

        // TODO(emilio, bug 1894104): Need to handle zoom here, probably something like
        // container_zoom - effective_zoom or so. See
        // https://github.com/w3c/csswg-drafts/issues/10268
        let size = context.get_container_size_query();
        let (factor, container_length) = match *self {
            Self::Cqw(v) => (v, size.get_container_width(context)),
            Self::Cqh(v) => (v, size.get_container_height(context)),
            Self::Cqi(v) => (v, size.get_container_inline_size(context)),
            Self::Cqb(v) => (v, size.get_container_block_size(context)),
            Self::Cqmin(v) => (
                v,
                cmp::min(
                    size.get_container_inline_size(context),
                    size.get_container_block_size(context),
                ),
            ),
            Self::Cqmax(v) => (
                v,
                cmp::max(
                    size.get_container_inline_size(context),
                    size.get_container_block_size(context),
                ),
            ),
        };
        CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()
    }
}

/// A `<length>` without taking `calc` expressions into account
///
/// <https://drafts.csswg.org/css-values/#lengths>
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
#[repr(u8)]
pub enum NoCalcLength {
    /// An absolute length
    ///
    /// <https://drafts.csswg.org/css-values/#absolute-length>
    Absolute(AbsoluteLength),

    /// A font-relative length:
    ///
    /// <https://drafts.csswg.org/css-values/#font-relative-lengths>
    FontRelative(FontRelativeLength),

    /// A viewport-relative length.
    ///
    /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
    ViewportPercentage(ViewportPercentageLength),

    /// A container query length.
    ///
    /// <https://drafts.csswg.org/css-contain-3/#container-lengths>
    ContainerRelative(ContainerRelativeLength),
    /// HTML5 "character width", as defined in HTML5 § 14.5.4.
    ///
    /// This cannot be specified by the user directly and is only generated by
    /// `Stylist::synthesize_rules_for_legacy_attributes()`.
    ServoCharacterWidth(CharacterWidth),
}

impl NoCalcLength {
    /// Return the unitless, raw value.
    pub fn unitless_value(&self) -> CSSFloat {
        match *self {
            Self::Absolute(v) => v.unitless_value(),
            Self::FontRelative(v) => v.unitless_value(),
            Self::ViewportPercentage(v) => v.unitless_value(),
            Self::ContainerRelative(v) => v.unitless_value(),
            Self::ServoCharacterWidth(c) => c.0 as f32,
        }
    }

    // Return the unit, as a string.
    fn unit(&self) -> &'static str {
        match *self {
            Self::Absolute(v) => v.unit(),
            Self::FontRelative(v) => v.unit(),
            Self::ViewportPercentage(v) => v.unit(),
            Self::ContainerRelative(v) => v.unit(),
            Self::ServoCharacterWidth(_) => "",
        }
    }

    /// Returns whether the value of this length without unit is less than zero.
    pub fn is_negative(&self) -> bool {
        self.unitless_value().is_sign_negative()
    }

    /// Returns whether the value of this length without unit is equal to zero.
    pub fn is_zero(&self) -> bool {
        self.unitless_value() == 0.0
    }

    /// Returns whether the value of this length without unit is infinite.
    pub fn is_infinite(&self) -> bool {
        self.unitless_value().is_infinite()
    }

    /// Returns whether the value of this length without unit is NaN.
    pub fn is_nan(&self) -> bool {
        self.unitless_value().is_nan()
    }

    /// Whether text-only zoom should be applied to this length.
    ///
    /// Generally, font-dependent/relative units don't get text-only-zoomed,
    /// because the font they're relative to should be zoomed already.
    pub fn should_zoom_text(&self) -> bool {
        match *self {
            Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true,
            Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false,
        }
    }

    /// Parse a given absolute or relative dimension.
    pub fn parse_dimension(
        context: &ParserContext,
        value: CSSFloat,
        unit: &str,
    ) -> Result<Self, ()> {
        Ok(match_ignore_ascii_case! { unit,
            "px" => Self::Absolute(AbsoluteLength::Px(value)),
            "in" => Self::Absolute(AbsoluteLength::In(value)),
            "cm" => Self::Absolute(AbsoluteLength::Cm(value)),
            "mm" => Self::Absolute(AbsoluteLength::Mm(value)),
            "q" => Self::Absolute(AbsoluteLength::Q(value)),
            "pt" => Self::Absolute(AbsoluteLength::Pt(value)),
            "pc" => Self::Absolute(AbsoluteLength::Pc(value)),
            // font-relative
            "em" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Em(value)),
            "ex" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ex(value)),
            "rex" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rex(value)),
            "ch" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ch(value)),
            "rch" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rch(value)),
            "cap" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Cap(value)),
            "rcap" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rcap(value)),
            "ic" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ic(value)),
            "ric" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ric(value)),
            "rem" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rem(value)),
            "lh" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Lh(value)),
            "rlh" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rlh(value)),
            // viewport percentages
            "vw" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Vw(value))
            },
            "svw" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Svw(value))
            },
            "lvw" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Lvw(value))
            },
            "dvw" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Dvw(value))
            },
            "vh" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Vh(value))
            },
            "svh" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Svh(value))
            },
            "lvh" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Lvh(value))
            },
            "dvh" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Dvh(value))
            },
            "vmin" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Vmin(value))
            },
            "svmin" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Svmin(value))
            },
            "lvmin" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Lvmin(value))
            },
            "dvmin" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Dvmin(value))
            },
            "vmax" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Vmax(value))
            },
            "svmax" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Svmax(value))
            },
            "lvmax" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Lvmax(value))
            },
            "dvmax" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Dvmax(value))
            },
            "vb" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Vb(value))
            },
            "svb" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Svb(value))
            },
            "lvb" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Lvb(value))
            },
            "dvb" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Dvb(value))
            },
            "vi" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Vi(value))
            },
            "svi" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Svi(value))
            },
            "lvi" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Lvi(value))
            },
            "dvi" if !context.in_page_rule() => {
                Self::ViewportPercentage(ViewportPercentageLength::Dvi(value))
            },
            // Container query lengths. Inherit the limitation from viewport units since
            // we may fall back to them.
            "cqw" if !context.in_page_rule() && cfg!(feature = "gecko") => {
                Self::ContainerRelative(ContainerRelativeLength::Cqw(value))
            },
            "cqh" if !context.in_page_rule() && cfg!(feature = "gecko") => {
                Self::ContainerRelative(ContainerRelativeLength::Cqh(value))
            },
            "cqi" if !context.in_page_rule() && cfg!(feature = "gecko") => {
                Self::ContainerRelative(ContainerRelativeLength::Cqi(value))
            },
            "cqb" if !context.in_page_rule() && cfg!(feature = "gecko") => {
                Self::ContainerRelative(ContainerRelativeLength::Cqb(value))
            },
            "cqmin" if !context.in_page_rule() && cfg!(feature = "gecko") => {
                Self::ContainerRelative(ContainerRelativeLength::Cqmin(value))
            },
            "cqmax" if !context.in_page_rule() && cfg!(feature = "gecko") => {
                Self::ContainerRelative(ContainerRelativeLength::Cqmax(value))
            },
            _ => return Err(()),
        })
    }

    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
    where
        O: Fn(f32, f32) -> f32,
    {
        use self::NoCalcLength::*;

        if std::mem::discriminant(self) != std::mem::discriminant(other) {
            return Err(());
        }

        Ok(match (self, other) {
            (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?),
            (&FontRelative(ref one), &FontRelative(ref other)) => {
                FontRelative(one.try_op(other, op)?)
            },
            (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
                ViewportPercentage(one.try_op(other, op)?)
            },
            (&ContainerRelative(ref one), &ContainerRelative(ref other)) => {
                ContainerRelative(one.try_op(other, op)?)
            },
            (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
                ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32))
            },
            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
            // able to figure it own on its own so we help.
            _ => unsafe {
                match *self {
                    Absolute(..)
                    | FontRelative(..)
                    | ViewportPercentage(..)
                    | ContainerRelative(..)
                    | ServoCharacterWidth(..) => {},
                }
                debug_unreachable!("Forgot to handle unit in try_op()")
            },
        })
    }

    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
        use self::NoCalcLength::*;

        match self {
            Absolute(ref one) => Absolute(one.map(op)),
            FontRelative(ref one) => FontRelative(one.map(op)),
            ViewportPercentage(ref one) => ViewportPercentage(one.map(op)),
            ContainerRelative(ref one) => ContainerRelative(one.map(op)),
            ServoCharacterWidth(ref one) => {
                ServoCharacterWidth(CharacterWidth(op(one.0 as f32) as i32))
            },
        }
    }

    /// Get a px value without context (so only absolute units can be handled).
    #[inline]
    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
        match *self {
            Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
            _ => Err(()),
        }
    }

    /// Get a px value without a full style context; this can handle either
    /// absolute or (if a font metrics getter is provided) font-relative units.
    #[cfg(feature = "gecko")]
    #[inline]
    pub fn to_computed_pixel_length_with_font_metrics(
        &self,
        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
    ) -> Result<CSSFloat, ()> {
        match *self {
            Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
            Self::FontRelative(fr) => {
                if let Some(getter) = get_font_metrics {
                    fr.to_computed_pixel_length_with_font_metrics(getter)
                } else {
                    Err(())
                }
            },
            _ => Err(()),
        }
    }

    /// Get an absolute length from a px value.
    #[inline]
    pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
        NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
    }
}

impl ToCss for NoCalcLength {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        crate::values::serialize_specified_dimension(
            self.unitless_value(),
            self.unit(),
            false,
            dest,
        )
    }
}

impl ToTyped for NoCalcLength {
    fn to_typed(&self) -> Option<TypedValue> {
        let value = self.unitless_value();
        let unit = CssString::from(self.unit());
        Some(TypedValue::Numeric(NumericValue::Unit { value, unit }))
    }
}

impl SpecifiedValueInfo for NoCalcLength {}

impl PartialOrd for NoCalcLength {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        use self::NoCalcLength::*;

        if std::mem::discriminant(self) != std::mem::discriminant(other) {
            return None;
        }

        match (self, other) {
            (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),
            (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),
            (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
                one.partial_cmp(other)
            },
            (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other),
            (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
                one.0.partial_cmp(&other.0)
            },
            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
            // able to figure it own on its own so we help.
            _ => unsafe {
                match *self {
                    Absolute(..)
                    | FontRelative(..)
                    | ViewportPercentage(..)
                    | ContainerRelative(..)
                    | ServoCharacterWidth(..) => {},
                }
                debug_unreachable!("Forgot an arm in partial_cmp?")
            },
        }
    }
}

impl Zero for NoCalcLength {
    fn zero() -> Self {
        NoCalcLength::Absolute(AbsoluteLength::Px(0.))
    }

    fn is_zero(&self) -> bool {
        NoCalcLength::is_zero(self)
    }
}

/// An extension to `NoCalcLength` to parse `calc` expressions.
/// This is commonly used for the `<length>` values.
///
/// <https://drafts.csswg.org/css-values/#lengths>
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
#[typed_value(derive_fields)]
pub enum Length {
    /// The internal length type that cannot parse `calc`
    NoCalc(NoCalcLength),
    /// A calc expression.
    ///
    /// <https://drafts.csswg.org/css-values/#calc-notation>
    Calc(Box<CalcLengthPercentage>),
}

impl From<NoCalcLength> for Length {
    #[inline]
    fn from(len: NoCalcLength) -> Self {
        Length::NoCalc(len)
    }
}

impl PartialOrd for FontRelativeLength {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        use self::FontRelativeLength::*;

        if std::mem::discriminant(self) != std::mem::discriminant(other) {
            return None;
        }

        match (self, other) {
            (&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
            (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
            (&Rex(ref one), &Rex(ref other)) => one.partial_cmp(other),
            (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
            (&Rch(ref one), &Rch(ref other)) => one.partial_cmp(other),
            (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),
            (&Rcap(ref one), &Rcap(ref other)) => one.partial_cmp(other),
            (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other),
            (&Ric(ref one), &Ric(ref other)) => one.partial_cmp(other),
            (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
            (&Lh(ref one), &Lh(ref other)) => one.partial_cmp(other),
            (&Rlh(ref one), &Rlh(ref other)) => one.partial_cmp(other),
            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
            // able to figure it own on its own so we help.
            _ => unsafe {
                match *self {
                    Em(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..) | Ic(..)
                    | Ric(..) | Rem(..) | Lh(..) | Rlh(..) => {},
                }
                debug_unreachable!("Forgot an arm in partial_cmp?")
            },
        }
    }
}

impl PartialOrd for ContainerRelativeLength {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        use self::ContainerRelativeLength::*;

        if std::mem::discriminant(self) != std::mem::discriminant(other) {
            return None;
        }

        match (self, other) {
            (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other),
            (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other),
            (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other),
            (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other),
            (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other),
            (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other),

            // See https://github.com/rust-lang/rust/issues/68867, then
            // https://github.com/rust-lang/rust/pull/95161. rustc isn't
            // able to figure it own on its own so we help.
            _ => unsafe {
                match *self {
                    Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
                }
                debug_unreachable!("Forgot to handle unit in partial_cmp()")
            },
        }
    }
}

impl PartialOrd for ViewportPercentageLength {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        use self::ViewportPercentageLength::*;

        if std::mem::discriminant(self) != std::mem::discriminant(other) {
            return None;
        }

        match (self, other) {
            (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),
            (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other),
            (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other),
            (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other),
            (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),
            (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other),
            (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other),
            (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other),
            (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),
            (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other),
            (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other),
            (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other),
            (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),
            (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other),
            (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other),
            (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other),
            (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other),
            (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other),
            (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other),
            (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other),
            (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other),
            (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other),
            (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other),
            (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other),
            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
            // able to figure it own on its own so we help.
            _ => unsafe {
                match *self {
                    Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)
                    | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)
                    | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)
                    | Svi(..) | Lvi(..) | Dvi(..) => {},
                }
                debug_unreachable!("Forgot an arm in partial_cmp?")
            },
        }
    }
}

impl Length {
    #[inline]
    fn parse_internal<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        num_context: AllowedNumericType,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        let location = input.current_source_location();
        let token = input.next()?;
        match *token {
            Token::Dimension {
                value, ref unit, ..
            } if num_context.is_ok(context.parsing_mode, value) => {
                NoCalcLength::parse_dimension(context, value, unit)
                    .map(Length::NoCalc)
                    .map_err(|()| location.new_unexpected_token_error(token.clone()))
            },
            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                if value != 0.
                    && !context.parsing_mode.allows_unitless_lengths()
                    && !allow_quirks.allowed(context.quirks_mode)
                {
                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                }
                Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
                    value,
                ))))
            },
            Token::Function(ref name) => {
                let function = CalcNode::math_function(context, name, location)?;
                let calc = CalcNode::parse_length(context, input, num_context, function)?;
                Ok(Length::Calc(Box::new(calc)))
            },
            ref token => return Err(location.new_unexpected_token_error(token.clone())),
        }
    }

    /// Parse a non-negative length
    #[inline]
    pub fn parse_non_negative<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
    }

    /// Parse a non-negative length, allowing quirks.
    #[inline]
    pub fn parse_non_negative_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_internal(
            context,
            input,
            AllowedNumericType::NonNegative,
            allow_quirks,
        )
    }

    /// Get an absolute length from a px value.
    #[inline]
    pub fn from_px(px_value: CSSFloat) -> Length {
        Length::NoCalc(NoCalcLength::from_px(px_value))
    }

    /// Get a px value without context.
    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
        match *self {
            Self::NoCalc(ref l) => l.to_computed_pixel_length_without_context(),
            Self::Calc(ref l) => l.to_computed_pixel_length_without_context(),
        }
    }

    /// Get a px value, with an optional GeckoFontMetrics getter to resolve font-relative units.
    #[cfg(feature = "gecko")]
    pub fn to_computed_pixel_length_with_font_metrics(
        &self,
        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
    ) -> Result<CSSFloat, ()> {
        match *self {
            Self::NoCalc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
            Self::Calc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
        }
    }
}

impl Parse for Length {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_quirky(context, input, AllowQuirks::No)
    }
}

impl Zero for Length {
    fn zero() -> Self {
        Length::NoCalc(NoCalcLength::zero())
    }

    fn is_zero(&self) -> bool {
        // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
        // non-zero here?
        match *self {
            Length::NoCalc(ref l) => l.is_zero(),
            Length::Calc(..) => false,
        }
    }
}

impl Length {
    /// Parses a length, with quirks.
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
    }
}

/// A wrapper of Length, whose value must be >= 0.
pub type NonNegativeLength = NonNegative<Length>;

impl Parse for NonNegativeLength {
    #[inline]
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Ok(NonNegative(Length::parse_non_negative(context, input)?))
    }
}

impl From<NoCalcLength> for NonNegativeLength {
    #[inline]
    fn from(len: NoCalcLength) -> Self {
        NonNegative(Length::NoCalc(len))
    }
}

impl From<Length> for NonNegativeLength {
    #[inline]
    fn from(len: Length) -> Self {
        NonNegative(len)
    }
}

impl NonNegativeLength {
    /// Get an absolute length from a px value.
    #[inline]
    pub fn from_px(px_value: CSSFloat) -> Self {
        Length::from_px(px_value.max(0.)).into()
    }

    /// Parses a non-negative length, optionally with quirks.
    #[inline]
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Ok(NonNegative(Length::parse_non_negative_quirky(
            context,
            input,
            allow_quirks,
        )?))
    }
}

/// A `<length-percentage>` value. This can be either a `<length>`, a
/// `<percentage>`, or a combination of both via `calc()`.
///
/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
#[allow(missing_docs)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
pub enum LengthPercentage {
    Length(NoCalcLength),
    Percentage(computed::Percentage),
    Calc(Box<CalcLengthPercentage>),
}

impl From<Length> for LengthPercentage {
    fn from(len: Length) -> LengthPercentage {
        match len {
            Length::NoCalc(l) => LengthPercentage::Length(l),
            Length::Calc(l) => LengthPercentage::Calc(l),
        }
    }
}

impl From<NoCalcLength> for LengthPercentage {
    #[inline]
    fn from(len: NoCalcLength) -> Self {
        LengthPercentage::Length(len)
    }
}

impl From<Percentage> for LengthPercentage {
    #[inline]
    fn from(pc: Percentage) -> Self {
        if let Some(clamping_mode) = pc.calc_clamping_mode() {
            LengthPercentage::Calc(Box::new(CalcLengthPercentage {
                clamping_mode,
                node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
            }))
        } else {
            LengthPercentage::Percentage(computed::Percentage(pc.get()))
        }
    }
}

impl From<computed::Percentage> for LengthPercentage {
    #[inline]
    fn from(pc: computed::Percentage) -> Self {
        LengthPercentage::Percentage(pc)
    }
}

impl Parse for LengthPercentage {
    #[inline]
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_quirky(context, input, AllowQuirks::No)
    }
}

impl LengthPercentage {
    #[inline]
    /// Returns a `0%` value.
    pub fn zero_percent() -> LengthPercentage {
        LengthPercentage::Percentage(computed::Percentage::zero())
    }

    #[inline]
    /// Returns a `100%` value.
    pub fn hundred_percent() -> LengthPercentage {
        LengthPercentage::Percentage(computed::Percentage::hundred())
    }

    fn parse_internal<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        num_context: AllowedNumericType,
        allow_quirks: AllowQuirks,
        allow_anchor: AllowAnchorPositioningFunctions,
    ) -> Result<Self, ParseError<'i>> {
        let location = input.current_source_location();
        let token = input.next()?;
        match *token {
            Token::Dimension {
                value, ref unit, ..
            } if num_context.is_ok(context.parsing_mode, value) => {
                return NoCalcLength::parse_dimension(context, value, unit)
                    .map(LengthPercentage::Length)
                    .map_err(|()| location.new_unexpected_token_error(token.clone()));
            },
            Token::Percentage { unit_value, .. }
                if num_context.is_ok(context.parsing_mode, unit_value) =>
            {
                return Ok(LengthPercentage::Percentage(computed::Percentage(
                    unit_value,
                )));
            },
            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                if value != 0.
                    && !context.parsing_mode.allows_unitless_lengths()
                    && !allow_quirks.allowed(context.quirks_mode)
                {
                    return Err(location.new_unexpected_token_error(token.clone()));
                } else {
                    return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
                }
            },
            Token::Function(ref name) => {
                let function = CalcNode::math_function(context, name, location)?;
                let calc = CalcNode::parse_length_or_percentage(
                    context,
                    input,
                    num_context,
                    function,
                    allow_anchor,
                )?;
                Ok(LengthPercentage::Calc(Box::new(calc)))
            },
            _ => return Err(location.new_unexpected_token_error(token.clone())),
        }
    }

    /// Parses allowing the unitless length quirk.
    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
    #[inline]
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_internal(
            context,
            input,
            AllowedNumericType::All,
            allow_quirks,
            AllowAnchorPositioningFunctions::No,
        )
    }

    /// Parses allowing the unitless length quirk, as well as allowing
    /// anchor-positioning related function, `anchor-size()`.
    #[inline]
    fn parse_quirky_with_anchor_size_function<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_internal(
            context,
            input,
            AllowedNumericType::All,
            allow_quirks,
            AllowAnchorPositioningFunctions::AllowAnchorSize,
        )
    }

    /// Parses allowing the unitless length quirk, as well as allowing
    /// anchor-positioning related functions, `anchor()` and `anchor-size()`.
    #[inline]
    pub fn parse_quirky_with_anchor_functions<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_internal(
            context,
            input,
            AllowedNumericType::All,
            allow_quirks,
            AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,
        )
    }

    /// Parses non-negative length, allowing the unitless length quirk,
    /// as well as allowing `anchor-size()`.
    pub fn parse_non_negative_with_anchor_size<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_internal(
            context,
            input,
            AllowedNumericType::NonNegative,
            allow_quirks,
            AllowAnchorPositioningFunctions::AllowAnchorSize,
        )
    }

    /// Parse a non-negative length.
    ///
    /// FIXME(emilio): This should be not public and we should use
    /// NonNegativeLengthPercentage instead.
    #[inline]
    pub fn parse_non_negative<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
    }

    /// Parse a non-negative length, with quirks.
    #[inline]
    pub fn parse_non_negative_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_internal(
            context,
            input,
            AllowedNumericType::NonNegative,
            allow_quirks,
            AllowAnchorPositioningFunctions::No,
        )
    }

    /// Returns self as specified::calc::CalcNode.
    /// Note that this expect the clamping_mode is AllowedNumericType::All for Calc. The caller
    /// should take care about it when using this function.
    fn to_calc_node(self) -> CalcNode {
        match self {
            LengthPercentage::Length(l) => CalcNode::Leaf(calc::Leaf::Length(l)),
            LengthPercentage::Percentage(p) => CalcNode::Leaf(calc::Leaf::Percentage(p.0)),
            LengthPercentage::Calc(p) => p.node,
        }
    }

    /// Construct the value representing `calc(100% - self)`.
    pub fn hundred_percent_minus(self, clamping_mode: AllowedNumericType) -> Self {
        let mut sum = smallvec::SmallVec::<[CalcNode; 2]>::new();
        sum.push(CalcNode::Leaf(calc::Leaf::Percentage(1.0)));

        let mut node = self.to_calc_node();
        node.negate();
        sum.push(node);

        let calc = CalcNode::Sum(sum.into_boxed_slice().into());
        LengthPercentage::Calc(Box::new(
            calc.into_length_or_percentage(clamping_mode).unwrap(),
        ))
    }
}

impl Zero for LengthPercentage {
    fn zero() -> Self {
        LengthPercentage::Length(NoCalcLength::zero())
    }

    fn is_zero(&self) -> bool {
        match *self {
            LengthPercentage::Length(l) => l.is_zero(),
            LengthPercentage::Percentage(p) => p.0 == 0.0,
            LengthPercentage::Calc(_) => false,
        }
    }
}

impl ZeroNoPercent for LengthPercentage {
    fn is_zero_no_percent(&self) -> bool {
        match *self {
            LengthPercentage::Percentage(_) => false,
            _ => self.is_zero(),
        }
    }
}

/// A specified type for `<length-percentage> | auto`.
pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;

impl LengthPercentageOrAuto {
    /// Returns a value representing `0%`.
    #[inline]
    pub fn zero_percent() -> Self {
        generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
    }

    /// Parses a length or a percentage, allowing the unitless length quirk.
    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
    #[inline]
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_with(context, input, |context, input| {
            LengthPercentage::parse_quirky(context, input, allow_quirks)
        })
    }
}

/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
pub type NonNegativeLengthPercentageOrAuto =
    generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;

impl NonNegativeLengthPercentageOrAuto {
    /// Returns a value representing `0%`.
    #[inline]
    pub fn zero_percent() -> Self {
        generics::LengthPercentageOrAuto::LengthPercentage(
            NonNegativeLengthPercentage::zero_percent(),
        )
    }

    /// Parses a non-negative length-percentage, allowing the unitless length
    /// quirk.
    #[inline]
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_with(context, input, |context, input| {
            NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
        })
    }
}

/// A wrapper of LengthPercentage, whose value must be >= 0.
pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;

/// Either a NonNegativeLengthPercentage or the `normal` keyword.
pub type NonNegativeLengthPercentageOrNormal =
    GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;

impl From<NoCalcLength> for NonNegativeLengthPercentage {
    #[inline]
    fn from(len: NoCalcLength) -> Self {
        NonNegative(LengthPercentage::from(len))
    }
}

impl Parse for NonNegativeLengthPercentage {
    #[inline]
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_quirky(context, input, AllowQuirks::No)
    }
}

impl NonNegativeLengthPercentage {
    #[inline]
    /// Returns a `0%` value.
    pub fn zero_percent() -> Self {
        NonNegative(LengthPercentage::zero_percent())
    }

    /// Parses a length or a percentage, allowing the unitless length quirk.
    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
    #[inline]
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
    }

    /// Parses a length or a percentage, allowing the unitless length quirk,
    /// as well as allowing `anchor-size()`.
    #[inline]
    pub fn parse_non_negative_with_anchor_size<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)
            .map(NonNegative)
    }
}

/// Either a `<length>` or the `auto` keyword.
///
/// Note that we use LengthPercentage just for convenience, since it pretty much
/// is everything we care about, but we could just add a similar LengthOrAuto
/// instead if we think getting rid of this weirdness is worth it.
pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;

impl LengthOrAuto {
    /// Parses a length, allowing the unitless length quirk.
    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
    #[inline]
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_with(context, input, |context, input| {
            Length::parse_quirky(context, input, allow_quirks)
        })
    }
}

/// Either a non-negative `<length>` or the `auto` keyword.
pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;

/// Either a `<length>` or a `<number>`.
pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;

/// A specified value for `min-width`, `min-height`, `width` or `height` property.
pub type Size = GenericSize<NonNegativeLengthPercentage>;

impl Parse for Size {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Size::parse_quirky(context, input, AllowQuirks::No)
    }
}

macro_rules! parse_size_non_length {
    ($size:ident, $input:expr, $allow_webkit_fill_available:expr,
     $auto_or_none:expr => $auto_or_none_ident:ident) => {{
        let size = $input.try_parse(|input| {
            Ok(try_match_ident_ignore_ascii_case! { input,
                "min-content" | "-moz-min-content" => $size::MinContent,
                "max-content" | "-moz-max-content" => $size::MaxContent,
                "fit-content" | "-moz-fit-content" => $size::FitContent,
                #[cfg(feature = "gecko")]
                "-moz-available" => $size::MozAvailable,
                "-webkit-fill-available" if $allow_webkit_fill_available => $size::WebkitFillAvailable,
                "stretch" if is_stretch_enabled() => $size::Stretch,
                $auto_or_none => $size::$auto_or_none_ident,
            })
        });
        if size.is_ok() {
            return size;
        }
    }};
}

fn is_webkit_fill_available_enabled_in_width_and_height() -> bool {
    static_prefs::pref!("layout.css.webkit-fill-available.enabled")
}

fn is_webkit_fill_available_enabled_in_all_size_properties() -> bool {
    // For convenience at the callsites, we check both prefs here,
    // since both must be 'true' in order for the keyword to be
    // enabled in all size properties.
    static_prefs::pref!("layout.css.webkit-fill-available.enabled")
        && static_prefs::pref!("layout.css.webkit-fill-available.all-size-properties.enabled")
}

fn is_stretch_enabled() -> bool {
    static_prefs::pref!("layout.css.stretch-size-keyword.enabled")
}

fn is_fit_content_function_enabled() -> bool {
    static_prefs::pref!("layout.css.fit-content-function.enabled")
}

macro_rules! parse_fit_content_function {
    ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
        if is_fit_content_function_enabled() {
            if let Ok(length) = $input.try_parse(|input| {
                input.expect_function_matching("fit-content")?;
                input.parse_nested_block(|i| {
                    NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
                })
            }) {
                return Ok($size::FitContentFunction(length));
            }
        }
    };
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum ParseAnchorFunctions {
    Yes,
    No,
}

impl Size {
    /// Parses, with quirks.
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
        Self::parse_quirky_internal(
            context,
            input,
            allow_quirks,
            allow_webkit_fill_available,
            ParseAnchorFunctions::Yes,
        )
    }

    /// Parses for flex-basis: <width>
    pub fn parse_size_for_flex_basis_width<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_quirky_internal(
            context,
            input,
            AllowQuirks::No,
            true,
            ParseAnchorFunctions::No,
        )
    }

    /// Parses, with quirks and configurable support for
    /// whether the '-webkit-fill-available' keyword is allowed.
    /// TODO(dholbert) Fold this function into callsites in bug 1989073 when
    /// removing 'layout.css.webkit-fill-available.all-size-properties.enabled'.
    fn parse_quirky_internal<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
        allow_webkit_fill_available: bool,
        allow_anchor_functions: ParseAnchorFunctions,
    ) -> Result<Self, ParseError<'i>> {
        parse_size_non_length!(Size, input, allow_webkit_fill_available,
                               "auto" => Auto);
        parse_fit_content_function!(Size, input, context, allow_quirks);

        let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes
            && static_prefs::pref!("layout.css.anchor-positioning.enabled");
        match input
            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
        {
            Ok(length) => return Ok(GenericSize::LengthPercentage(length)),
            Err(e) if !allow_anchor => return Err(e.into()),
            Err(_) => (),
        };
        if let Ok(length) = input.try_parse(|i| {
            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
                context,
                i,
                allow_quirks,
            )
        }) {
            return Ok(GenericSize::AnchorContainingCalcFunction(length));
        }
        Ok(Self::AnchorSizeFunction(Box::new(
            GenericAnchorSizeFunction::parse(context, input)?,
        )))
    }

    /// Parse a size for width or height, where -webkit-fill-available
    /// support is only controlled by one pref (vs. other properties where
    /// there's an additional pref check):
    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with
    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.
    pub fn parse_size_for_width_or_height_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
        Self::parse_quirky_internal(
            context,
            input,
            allow_quirks,
            allow_webkit_fill_available,
            ParseAnchorFunctions::Yes,
        )
    }

    /// Parse a size for width or height, where -webkit-fill-available
    /// support is only controlled by one pref (vs. other properties where
    /// there's an additional pref check):
    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with
    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.
    pub fn parse_size_for_width_or_height<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
        Self::parse_quirky_internal(
            context,
            input,
            AllowQuirks::No,
            allow_webkit_fill_available,
            ParseAnchorFunctions::Yes,
        )
    }

    /// Returns `0%`.
    #[inline]
    pub fn zero_percent() -> Self {
        GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
    }
}

/// A specified value for `max-width` or `max-height` property.
pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;

impl Parse for MaxSize {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        MaxSize::parse_quirky(context, input, AllowQuirks::No)
    }
}

impl MaxSize {
    /// Parses, with quirks.
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
        parse_size_non_length!(MaxSize, input, allow_webkit_fill_available,
                               "none" => None);
        parse_fit_content_function!(MaxSize, input, context, allow_quirks);

        match input
            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
        {
            Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),
            Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
                return Err(e.into())
            },
            Err(_) => (),
        };
        if let Ok(length) = input.try_parse(|i| {
            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
                context,
                i,
                allow_quirks,
            )
        }) {
            return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));
        }
        Ok(Self::AnchorSizeFunction(Box::new(
            GenericAnchorSizeFunction::parse(context, input)?,
        )))
    }
}

/// A specified non-negative `<length>` | `<number>`.
pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;

/// A specified value for `margin` properties.
pub type Margin = GenericMargin<LengthPercentage>;

impl Margin {
    /// Parses a margin type, allowing the unitless length quirk.
    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
    #[inline]
    pub fn parse_quirky<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_quirks: AllowQuirks,
    ) -> Result<Self, ParseError<'i>> {
        if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
        {
            return Ok(Self::LengthPercentage(l));
        }
        match input.try_parse(|i| i.expect_ident_matching("auto")) {
            Ok(_) => return Ok(Self::Auto),
            Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
                return Err(e.into())
            },
            Err(_) => (),
        };
        if let Ok(l) = input.try_parse(|i| {
            LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)
        }) {
            return Ok(Self::AnchorContainingCalcFunction(l));
        }
        let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?;
        Ok(Self::AnchorSizeFunction(Box::new(inner)))
    }
}

impl Parse for Margin {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_quirky(context, input, AllowQuirks::No)
    }
}
