servo/components/style/values/computed/length.rs
author Emilio Cobos Álvarez <emilio@crisal.io>
Fri, 19 Oct 2018 00:46:28 +0200
changeset 490350 9b412ade97d9c655a7599a5ea1cc95b9179c63a4
parent 489882 963840088024c60bc7e52d6046312989a2935705
permissions -rw-r--r--
Bug 1500260 - Fix various tidy issues and Servo build. r=emilio

/* 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 http://mozilla.org/MPL/2.0/. */

//! `<length>` computed values, and related ones.

use app_units::Au;
use ordered_float::NotNan;
use std::fmt::{self, Write};
use std::ops::{Add, Neg};
use style_traits::{CssWriter, ToCss};
use style_traits::values::specified::AllowedNumericType;
use super::{Context, Number, Percentage, ToComputedValue};
use values::{specified, Auto, CSSFloat, Either, Normal};
use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::NonNegative;
use values::generics::length::{MaxLength as GenericMaxLength, MozLength as GenericMozLength};
use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
use values::specified::length::ViewportPercentageLength;

pub use super::image::Image;
pub use values::specified::url::UrlOrNone;
pub use values::specified::{Angle, BorderStyle, Time};

impl ToComputedValue for specified::NoCalcLength {
    type ComputedValue = CSSPixelLength;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            specified::NoCalcLength::Absolute(length) => length.to_computed_value(context),
            specified::NoCalcLength::FontRelative(length) => {
                length.to_computed_value(context, FontBaseSize::CurrentStyle)
            },
            specified::NoCalcLength::ViewportPercentage(length) => {
                length.to_computed_value(context.viewport_size_for_viewport_unit_resolution())
            },
            specified::NoCalcLength::ServoCharacterWidth(length) => {
                length.to_computed_value(context.style().get_font().clone_font_size().size())
            },
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        specified::NoCalcLength::Absolute(AbsoluteLength::Px(computed.px()))
    }
}

impl ToComputedValue for specified::Length {
    type ComputedValue = CSSPixelLength;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            specified::Length::NoCalc(l) => l.to_computed_value(context),
            specified::Length::Calc(ref calc) => calc.to_computed_value(context).length(),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        specified::Length::NoCalc(specified::NoCalcLength::from_computed_value(computed))
    }
}

#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedZero)]
pub struct CalcLengthOrPercentage {
    #[animation(constant)]
    pub clamping_mode: AllowedNumericType,
    length: Length,
    pub percentage: Option<Percentage>,
}

impl ComputeSquaredDistance for CalcLengthOrPercentage {
    #[inline]
    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
        // FIXME(nox): This looks incorrect to me, to add a distance between lengths
        // with a distance between percentages.
        Ok(self
            .unclamped_length()
            .compute_squared_distance(&other.unclamped_length())? +
            self.percentage()
                .compute_squared_distance(&other.percentage())?)
    }
}

impl CalcLengthOrPercentage {
    /// Returns a new `CalcLengthOrPercentage`.
    #[inline]
    pub fn new(length: Length, percentage: Option<Percentage>) -> Self {
        Self::with_clamping_mode(length, percentage, AllowedNumericType::All)
    }

    /// Returns a new `CalcLengthOrPercentage` with a specific clamping mode.
    #[inline]
    pub fn with_clamping_mode(
        length: Length,
        percentage: Option<Percentage>,
        clamping_mode: AllowedNumericType,
    ) -> Self {
        Self {
            clamping_mode,
            length,
            percentage,
        }
    }

    /// Returns this `calc()` as a `<length>`.
    ///
    /// Panics in debug mode if a percentage is present in the expression.
    #[inline]
    pub fn length(&self) -> CSSPixelLength {
        debug_assert!(self.percentage.is_none());
        self.length_component()
    }

    /// Returns the length component of this `calc()`
    #[inline]
    pub fn length_component(&self) -> CSSPixelLength {
        CSSPixelLength::new(self.clamping_mode.clamp(self.length.px()))
    }

    /// Returns the `<length>` component of this `calc()`, unclamped.
    #[inline]
    pub fn unclamped_length(&self) -> CSSPixelLength {
        self.length
    }

    /// Return the percentage value as CSSFloat.
    #[inline]
    pub fn percentage(&self) -> CSSFloat {
        self.percentage.map_or(0., |p| p.0)
    }

    /// Convert the computed value into used value.
    #[inline]
    pub fn to_used_value(&self, container_len: Option<Au>) -> Option<Au> {
        self.to_pixel_length(container_len).map(Au::from)
    }

    /// If there are special rules for computing percentages in a value (e.g.
    /// the height property), they apply whenever a calc() expression contains
    /// percentages.
    pub fn to_pixel_length(&self, container_len: Option<Au>) -> Option<Length> {
        match (container_len, self.percentage) {
            (Some(len), Some(percent)) => {
                let pixel = self.length.px() + len.scale_by(percent.0).to_f32_px();
                Some(Length::new(self.clamping_mode.clamp(pixel)))
            },
            (_, None) => Some(self.length()),
            _ => None,
        }
    }
}

impl From<LengthOrPercentage> for CalcLengthOrPercentage {
    fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage {
        match len {
            LengthOrPercentage::Percentage(this) => {
                CalcLengthOrPercentage::new(Length::new(0.), Some(this))
            },
            LengthOrPercentage::Length(this) => CalcLengthOrPercentage::new(this, None),
            LengthOrPercentage::Calc(this) => this,
        }
    }
}

impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
    fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> {
        match len {
            LengthOrPercentageOrAuto::Percentage(this) => {
                Some(CalcLengthOrPercentage::new(Length::new(0.), Some(this)))
            },
            LengthOrPercentageOrAuto::Length(this) => Some(CalcLengthOrPercentage::new(this, None)),
            LengthOrPercentageOrAuto::Calc(this) => Some(this),
            LengthOrPercentageOrAuto::Auto => None,
        }
    }
}

impl From<LengthOrPercentageOrNone> for Option<CalcLengthOrPercentage> {
    fn from(len: LengthOrPercentageOrNone) -> Option<CalcLengthOrPercentage> {
        match len {
            LengthOrPercentageOrNone::Percentage(this) => {
                Some(CalcLengthOrPercentage::new(Length::new(0.), Some(this)))
            },
            LengthOrPercentageOrNone::Length(this) => Some(CalcLengthOrPercentage::new(this, None)),
            LengthOrPercentageOrNone::Calc(this) => Some(this),
            LengthOrPercentageOrNone::None => None,
        }
    }
}

impl ToCss for CalcLengthOrPercentage {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        use num_traits::Zero;

        let length = self.unclamped_length();
        match self.percentage {
            Some(p) => {
                if length.px() == 0. && self.clamping_mode.clamp(p.0) == p.0 {
                    return p.to_css(dest);
                }
            }
            None => {
                if self.clamping_mode.clamp(length.px()) == length.px() {
                    return length.to_css(dest);
                }
            }
        }

        dest.write_str("calc(")?;
        if let Some(percentage) = self.percentage {
            percentage.to_css(dest)?;
            if length.px() != 0. {
                dest.write_str(if length.px() < Zero::zero() {
                    " - "
                } else {
                    " + "
                })?;
                length.abs().to_css(dest)?;
            }
        } else {
            length.to_css(dest)?;
        }

        dest.write_str(")")
    }
}

impl specified::CalcLengthOrPercentage {
    /// Compute the value, zooming any absolute units by the zoom function.
    fn to_computed_value_with_zoom<F>(
        &self,
        context: &Context,
        zoom_fn: F,
        base_size: FontBaseSize,
    ) -> CalcLengthOrPercentage
    where
        F: Fn(Length) -> Length,
    {
        use std::f32;
        let mut length = 0.;

        if let Some(absolute) = self.absolute {
            length += zoom_fn(absolute.to_computed_value(context)).px();
        }

        for val in &[
            self.vw.map(ViewportPercentageLength::Vw),
            self.vh.map(ViewportPercentageLength::Vh),
            self.vmin.map(ViewportPercentageLength::Vmin),
            self.vmax.map(ViewportPercentageLength::Vmax),
        ] {
            if let Some(val) = *val {
                let viewport_size = context.viewport_size_for_viewport_unit_resolution();
                length += val.to_computed_value(viewport_size).px();
            }
        }

        for val in &[
            self.ch.map(FontRelativeLength::Ch),
            self.em.map(FontRelativeLength::Em),
            self.ex.map(FontRelativeLength::Ex),
            self.rem.map(FontRelativeLength::Rem),
        ] {
            if let Some(val) = *val {
                length += val.to_computed_value(context, base_size).px();
            }
        }

        CalcLengthOrPercentage {
            clamping_mode: self.clamping_mode,
            length: Length::new(length.min(f32::MAX).max(f32::MIN)),
            percentage: self.percentage,
        }
    }

    /// Compute font-size or line-height taking into account text-zoom if necessary.
    pub fn to_computed_value_zoomed(
        &self,
        context: &Context,
        base_size: FontBaseSize,
    ) -> CalcLengthOrPercentage {
        self.to_computed_value_with_zoom(
            context,
            |abs| context.maybe_zoom_text(abs.into()).0,
            base_size,
        )
    }

    /// Compute the value into pixel length as CSSFloat without context,
    /// so it returns Err(()) if there is any non-absolute unit.
    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
        if self.vw.is_some() ||
            self.vh.is_some() ||
            self.vmin.is_some() ||
            self.vmax.is_some() ||
            self.em.is_some() ||
            self.ex.is_some() ||
            self.ch.is_some() ||
            self.rem.is_some() ||
            self.percentage.is_some()
        {
            return Err(());
        }

        match self.absolute {
            Some(abs) => Ok(abs.to_px()),
            None => {
                debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
                Err(())
            },
        }
    }
}

impl ToComputedValue for specified::CalcLengthOrPercentage {
    type ComputedValue = CalcLengthOrPercentage;

    fn to_computed_value(&self, context: &Context) -> CalcLengthOrPercentage {
        // normal properties don't zoom, and compute em units against the current style's font-size
        self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle)
    }

    #[inline]
    fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self {
        specified::CalcLengthOrPercentage {
            clamping_mode: computed.clamping_mode,
            absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
            percentage: computed.percentage,
            ..Default::default()
        }
    }
}

#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[css(derive_debug)]
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    MallocSizeOf,
    PartialEq,
    ToAnimatedValue,
    ToAnimatedZero,
    ToCss,
)]
#[distance(fallback = "Self::compute_squared_distance_fallback")]
pub enum LengthOrPercentage {
    Length(Length),
    Percentage(Percentage),
    Calc(CalcLengthOrPercentage),
}

impl LengthOrPercentage {
    /// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
    fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
        // Special handling for zero values since these should not require calc().
        if self.is_definitely_zero() {
            return other.to_animated_zero()?.animate(other, procedure);
        }
        if other.is_definitely_zero() {
            return self.animate(&self.to_animated_zero()?, procedure);
        }

        let this = CalcLengthOrPercentage::from(*self);
        let other = CalcLengthOrPercentage::from(*other);
        Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?))
    }

    #[inline]
    fn compute_squared_distance_fallback(&self, other: &Self) -> Result<SquaredDistance, ()> {
        CalcLengthOrPercentage::compute_squared_distance(&(*self).into(), &(*other).into())
    }
}

impl From<Au> for LengthOrPercentage {
    #[inline]
    fn from(length: Au) -> Self {
        LengthOrPercentage::Length(length.into())
    }
}

impl LengthOrPercentage {
    #[inline]
    #[allow(missing_docs)]
    pub fn zero() -> LengthOrPercentage {
        LengthOrPercentage::Length(Length::new(0.))
    }

    #[inline]
    /// 1px length value for SVG defaults
    pub fn one() -> LengthOrPercentage {
        LengthOrPercentage::Length(Length::new(1.))
    }

    /// Returns true if the computed value is absolute 0 or 0%.
    ///
    /// (Returns false for calc() values, even if ones that may resolve to zero.)
    #[inline]
    pub fn is_definitely_zero(&self) -> bool {
        use self::LengthOrPercentage::*;
        match *self {
            Length(l) => l.px() == 0.0,
            Percentage(p) => p.0 == 0.0,
            Calc(_) => false,
        }
    }

    // CSSFloat doesn't implement Hash, so does CSSPixelLength. Therefore, we still use Au as the
    // hash key.
    #[allow(missing_docs)]
    pub fn to_hash_key(&self) -> (Au, NotNan<f32>) {
        use self::LengthOrPercentage::*;
        match *self {
            Length(l) => (Au::from(l), NotNan::new(0.0).unwrap()),
            Percentage(p) => (Au(0), NotNan::new(p.0).unwrap()),
            Calc(c) => (
                Au::from(c.unclamped_length()),
                NotNan::new(c.percentage()).unwrap(),
            ),
        }
    }

    /// Returns the used value.
    pub fn to_used_value(&self, containing_length: Au) -> Au {
        Au::from(self.to_pixel_length(containing_length))
    }

    /// Returns the used value as CSSPixelLength.
    pub fn to_pixel_length(&self, containing_length: Au) -> Length {
        match *self {
            LengthOrPercentage::Length(length) => length,
            LengthOrPercentage::Percentage(p) => containing_length.scale_by(p.0).into(),
            LengthOrPercentage::Calc(ref calc) => {
                calc.to_pixel_length(Some(containing_length)).unwrap()
            },
        }
    }

    /// Returns the clamped non-negative values.
    #[inline]
    pub fn clamp_to_non_negative(self) -> Self {
        match self {
            LengthOrPercentage::Length(length) => {
                LengthOrPercentage::Length(length.clamp_to_non_negative())
            },
            LengthOrPercentage::Percentage(percentage) => {
                LengthOrPercentage::Percentage(percentage.clamp_to_non_negative())
            },
            _ => self,
        }
    }
}

impl ToComputedValue for specified::LengthOrPercentage {
    type ComputedValue = LengthOrPercentage;

    fn to_computed_value(&self, context: &Context) -> LengthOrPercentage {
        match *self {
            specified::LengthOrPercentage::Length(ref value) => {
                LengthOrPercentage::Length(value.to_computed_value(context))
            },
            specified::LengthOrPercentage::Percentage(value) => {
                LengthOrPercentage::Percentage(value)
            },
            specified::LengthOrPercentage::Calc(ref calc) => {
                LengthOrPercentage::Calc((**calc).to_computed_value(context))
            },
        }
    }

    fn from_computed_value(computed: &LengthOrPercentage) -> Self {
        match *computed {
            LengthOrPercentage::Length(value) => {
                specified::LengthOrPercentage::Length(ToComputedValue::from_computed_value(&value))
            },
            LengthOrPercentage::Percentage(value) => {
                specified::LengthOrPercentage::Percentage(value)
            },
            LengthOrPercentage::Calc(ref calc) => specified::LengthOrPercentage::Calc(Box::new(
                ToComputedValue::from_computed_value(calc),
            )),
        }
    }
}

#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[css(derive_debug)]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq, ToCss)]
#[distance(fallback = "Self::compute_squared_distance_fallback")]
pub enum LengthOrPercentageOrAuto {
    Length(Length),
    Percentage(Percentage),
    Auto,
    Calc(CalcLengthOrPercentage),
}

impl LengthOrPercentageOrAuto {
    /// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
    fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
        let this = <Option<CalcLengthOrPercentage>>::from(*self);
        let other = <Option<CalcLengthOrPercentage>>::from(*other);
        Ok(LengthOrPercentageOrAuto::Calc(
            this.animate(&other, procedure)?.ok_or(())?,
        ))
    }

    #[inline]
    fn compute_squared_distance_fallback(&self, other: &Self) -> Result<SquaredDistance, ()> {
        <Option<CalcLengthOrPercentage>>::compute_squared_distance(
            &(*self).into(),
            &(*other).into(),
        )
    }
}

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

impl NonNegativeLengthOrPercentageOrAuto {
    /// `auto`
    #[inline]
    pub fn auto() -> Self {
        NonNegative(LengthOrPercentageOrAuto::Auto)
    }
}

impl ToAnimatedValue for NonNegativeLengthOrPercentageOrAuto {
    type AnimatedValue = LengthOrPercentageOrAuto;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        self.0
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        NonNegative(animated.clamp_to_non_negative())
    }
}

impl LengthOrPercentageOrAuto {
    /// Returns true if the computed value is absolute 0 or 0%.
    ///
    /// (Returns false for calc() values, even if ones that may resolve to zero.)
    #[inline]
    pub fn is_definitely_zero(&self) -> bool {
        use self::LengthOrPercentageOrAuto::*;
        match *self {
            Length(l) => l.px() == 0.0,
            Percentage(p) => p.0 == 0.0,
            Calc(_) | Auto => false,
        }
    }

    fn clamp_to_non_negative(self) -> Self {
        use self::LengthOrPercentageOrAuto::*;
        match self {
            Length(l) => Length(l.clamp_to_non_negative()),
            Percentage(p) => Percentage(p.clamp_to_non_negative()),
            _ => self,
        }
    }
}

impl ToComputedValue for specified::LengthOrPercentageOrAuto {
    type ComputedValue = LengthOrPercentageOrAuto;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrAuto {
        match *self {
            specified::LengthOrPercentageOrAuto::Length(ref value) => {
                LengthOrPercentageOrAuto::Length(value.to_computed_value(context))
            },
            specified::LengthOrPercentageOrAuto::Percentage(value) => {
                LengthOrPercentageOrAuto::Percentage(value)
            },
            specified::LengthOrPercentageOrAuto::Auto => LengthOrPercentageOrAuto::Auto,
            specified::LengthOrPercentageOrAuto::Calc(ref calc) => {
                LengthOrPercentageOrAuto::Calc((**calc).to_computed_value(context))
            },
        }
    }

    #[inline]
    fn from_computed_value(computed: &LengthOrPercentageOrAuto) -> Self {
        match *computed {
            LengthOrPercentageOrAuto::Auto => specified::LengthOrPercentageOrAuto::Auto,
            LengthOrPercentageOrAuto::Length(value) => specified::LengthOrPercentageOrAuto::Length(
                ToComputedValue::from_computed_value(&value),
            ),
            LengthOrPercentageOrAuto::Percentage(value) => {
                specified::LengthOrPercentageOrAuto::Percentage(value)
            },
            LengthOrPercentageOrAuto::Calc(calc) => specified::LengthOrPercentageOrAuto::Calc(
                Box::new(ToComputedValue::from_computed_value(&calc)),
            ),
        }
    }
}

#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[css(derive_debug)]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)]
#[distance(fallback = "Self::compute_squared_distance_fallback")]
pub enum LengthOrPercentageOrNone {
    Length(Length),
    Percentage(Percentage),
    Calc(CalcLengthOrPercentage),
    None,
}

impl LengthOrPercentageOrNone {
    /// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
    fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
        let this = <Option<CalcLengthOrPercentage>>::from(*self);
        let other = <Option<CalcLengthOrPercentage>>::from(*other);
        Ok(LengthOrPercentageOrNone::Calc(
            this.animate(&other, procedure)?.ok_or(())?,
        ))
    }

    fn compute_squared_distance_fallback(&self, other: &Self) -> Result<SquaredDistance, ()> {
        <Option<CalcLengthOrPercentage>>::compute_squared_distance(
            &(*self).into(),
            &(*other).into(),
        )
    }
}

impl LengthOrPercentageOrNone {
    /// Returns the used value.
    pub fn to_used_value(&self, containing_length: Au) -> Option<Au> {
        match *self {
            LengthOrPercentageOrNone::None => None,
            LengthOrPercentageOrNone::Length(length) => Some(Au::from(length)),
            LengthOrPercentageOrNone::Percentage(percent) => {
                Some(containing_length.scale_by(percent.0))
            },
            LengthOrPercentageOrNone::Calc(ref calc) => calc.to_used_value(Some(containing_length)),
        }
    }
}

impl ToComputedValue for specified::LengthOrPercentageOrNone {
    type ComputedValue = LengthOrPercentageOrNone;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrNone {
        match *self {
            specified::LengthOrPercentageOrNone::Length(ref value) => {
                LengthOrPercentageOrNone::Length(value.to_computed_value(context))
            },
            specified::LengthOrPercentageOrNone::Percentage(value) => {
                LengthOrPercentageOrNone::Percentage(value)
            },
            specified::LengthOrPercentageOrNone::Calc(ref calc) => {
                LengthOrPercentageOrNone::Calc((**calc).to_computed_value(context))
            },
            specified::LengthOrPercentageOrNone::None => LengthOrPercentageOrNone::None,
        }
    }

    #[inline]
    fn from_computed_value(computed: &LengthOrPercentageOrNone) -> Self {
        match *computed {
            LengthOrPercentageOrNone::None => specified::LengthOrPercentageOrNone::None,
            LengthOrPercentageOrNone::Length(value) => specified::LengthOrPercentageOrNone::Length(
                ToComputedValue::from_computed_value(&value),
            ),
            LengthOrPercentageOrNone::Percentage(value) => {
                specified::LengthOrPercentageOrNone::Percentage(value)
            },
            LengthOrPercentageOrNone::Calc(calc) => specified::LengthOrPercentageOrNone::Calc(
                Box::new(ToComputedValue::from_computed_value(&calc)),
            ),
        }
    }
}

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

impl ToAnimatedValue for NonNegativeLengthOrPercentage {
    type AnimatedValue = LengthOrPercentage;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        self.into()
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        animated.clamp_to_non_negative().into()
    }
}

impl From<NonNegativeLength> for NonNegativeLengthOrPercentage {
    #[inline]
    fn from(length: NonNegativeLength) -> Self {
        LengthOrPercentage::Length(length.0).into()
    }
}

impl From<LengthOrPercentage> for NonNegativeLengthOrPercentage {
    #[inline]
    fn from(lop: LengthOrPercentage) -> Self {
        NonNegative::<LengthOrPercentage>(lop)
    }
}

impl From<NonNegativeLengthOrPercentage> for LengthOrPercentage {
    #[inline]
    fn from(lop: NonNegativeLengthOrPercentage) -> LengthOrPercentage {
        lop.0
    }
}

impl NonNegativeLengthOrPercentage {
    /// Get zero value.
    #[inline]
    pub fn zero() -> Self {
        NonNegative::<LengthOrPercentage>(LengthOrPercentage::zero())
    }

    /// Returns true if the computed value is absolute 0 or 0%.
    #[inline]
    pub fn is_definitely_zero(&self) -> bool {
        self.0.is_definitely_zero()
    }

    /// Returns the used value.
    #[inline]
    pub fn to_used_value(&self, containing_length: Au) -> Au {
        self.0.to_used_value(containing_length)
    }
}

/// The computed `<length>` value.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    PartialOrd,
    ToAnimatedValue,
    ToAnimatedZero,
)]
pub struct CSSPixelLength(CSSFloat);

impl CSSPixelLength {
    /// Return a new CSSPixelLength.
    #[inline]
    pub fn new(px: CSSFloat) -> Self {
        CSSPixelLength(px)
    }

    /// Return the containing pixel value.
    #[inline]
    pub fn px(&self) -> CSSFloat {
        self.0
    }

    #[inline]
    fn clamp_to_non_negative(self) -> Self {
        Self::new(self.px().max(0.))
    }

    /// Return the length with app_unit i32 type.
    #[inline]
    pub fn to_i32_au(&self) -> i32 {
        Au::from(*self).0
    }

    /// Return the absolute value of this length.
    pub fn abs(self) -> Self {
        CSSPixelLength::new(self.0.abs())
    }

    /// Zero value
    pub fn zero() -> Self {
        CSSPixelLength::new(0.)
    }
}

impl ToCss for CSSPixelLength {
    #[inline]
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.0.to_css(dest)?;
        dest.write_str("px")
    }
}

impl Neg for CSSPixelLength {
    type Output = Self;

    #[inline]
    fn neg(self) -> Self {
        CSSPixelLength::new(-self.0)
    }
}

impl From<CSSPixelLength> for Au {
    #[inline]
    fn from(len: CSSPixelLength) -> Self {
        Au::from_f32_px(len.0)
    }
}

impl From<Au> for CSSPixelLength {
    #[inline]
    fn from(len: Au) -> Self {
        CSSPixelLength::new(len.to_f32_px())
    }
}

/// An alias of computed `<length>` value.
pub type Length = CSSPixelLength;

/// Either a computed `<length>` or the `auto` keyword.
pub type LengthOrAuto = Either<Length, Auto>;

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

impl LengthOrNumber {
    /// Returns `0`.
    #[inline]
    pub fn zero() -> Self {
        Either::Second(0.)
    }
}

/// Either a computed `<length>` or the `normal` keyword.
pub type LengthOrNormal = Either<Length, Normal>;

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

impl ToAnimatedValue for NonNegativeLength {
    type AnimatedValue = Length;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        self.0
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        NonNegativeLength::new(animated.px().max(0.))
    }
}

impl NonNegativeLength {
    /// Create a NonNegativeLength.
    #[inline]
    pub fn new(px: CSSFloat) -> Self {
        NonNegative(Length::new(px.max(0.)))
    }

    /// Return a zero value.
    #[inline]
    pub fn zero() -> Self {
        Self::new(0.)
    }

    /// Return the pixel value of |NonNegativeLength|.
    #[inline]
    pub fn px(&self) -> CSSFloat {
        self.0.px()
    }

    #[inline]
    /// Ensures it is non negative
    pub fn clamp(self) -> Self {
        if (self.0).0 < 0. {
            Self::zero()
        } else {
            self
        }
    }

    /// Scale this NonNegativeLength.
    /// We scale NonNegativeLength by zero if the factor is negative because it doesn't
    /// make sense to scale a negative factor on a non-negative length.
    #[inline]
    pub fn scale_by(&self, factor: f32) -> Self {
        Self::new(self.0.px() * factor.max(0.))
    }
}

impl Add<NonNegativeLength> for NonNegativeLength {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        NonNegativeLength::new(self.px() + other.px())
    }
}

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

impl From<Au> for NonNegativeLength {
    #[inline]
    fn from(au: Au) -> Self {
        NonNegative(au.into())
    }
}

impl From<NonNegativeLength> for Au {
    #[inline]
    fn from(non_negative_len: NonNegativeLength) -> Self {
        Au::from(non_negative_len.0)
    }
}

/// Either a computed NonNegativeLength or the `auto` keyword.
pub type NonNegativeLengthOrAuto = Either<NonNegativeLength, Auto>;

/// Either a computed NonNegativeLength or the `normal` keyword.
pub type NonNegativeLengthOrNormal = Either<NonNegativeLength, Normal>;

/// Either a computed NonNegativeLengthOrPercentage or the `normal` keyword.
pub type NonNegativeLengthOrPercentageOrNormal = Either<NonNegativeLengthOrPercentage, Normal>;

/// A type for possible values for min- and max- flavors of width, height,
/// block-size, and inline-size.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo,
         ToComputedValue, ToCss)]
pub enum ExtremumLength {
    MozMaxContent,
    MozMinContent,
    MozFitContent,
    MozAvailable,
}

/// A computed value for `min-width`, `min-height`, `width` or `height` property.
pub type MozLength = GenericMozLength<LengthOrPercentageOrAuto>;

impl MozLength {
    /// Returns the `auto` value.
    #[inline]
    pub fn auto() -> Self {
        GenericMozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::Auto)
    }
}

/// A computed value for `max-width` or `min-height` property.
pub type MaxLength = GenericMaxLength<LengthOrPercentageOrNone>;

impl MaxLength {
    /// Returns the `none` value.
    #[inline]
    pub fn none() -> Self {
        GenericMaxLength::LengthOrPercentageOrNone(LengthOrPercentageOrNone::None)
    }
}