/* 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/. */ //! `` computed values, and related ones. use app_units::Au; use logical_geometry::WritingMode; use ordered_float::NotNan; use properties::LonghandId; 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::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, } impl ComputeSquaredDistance for CalcLengthOrPercentage { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { // 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) -> 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, clamping_mode: AllowedNumericType, ) -> Self { Self { clamping_mode, length, percentage, } } /// Returns this `calc()` as a ``. /// /// 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 `` 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) -> Option { 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) -> Option { 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 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 for Option { fn from(len: LengthOrPercentageOrAuto) -> Option { 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 for Option { fn from(len: LengthOrPercentageOrNone) -> Option { 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(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { use num_traits::Zero; let (length, percentage) = match (self.length, self.percentage) { (l, None) => return l.to_css(dest), (l, Some(p)) if l.px() == 0. => return p.to_css(dest), (l, Some(p)) => (l, p), }; dest.write_str("calc(")?; percentage.to_css(dest)?; dest.write_str(if length.px() < Zero::zero() { " - " } else { " + " })?; length.abs().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( &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 { 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 { /// fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result { // 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 { CalcLengthOrPercentage::compute_squared_distance(&(*self).into(), &(*other).into()) } } impl From 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) { 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 { /// fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result { let this = >::from(*self); let other = >::from(*other); Ok(LengthOrPercentageOrAuto::Calc(this.animate( &other, procedure, )? .ok_or(())?)) } #[inline] fn compute_squared_distance_fallback(&self, other: &Self) -> Result { >::compute_squared_distance( &(*self).into(), &(*other).into(), ) } } /// A wrapper of LengthOrPercentageOrAuto, whose value must be >= 0. pub type NonNegativeLengthOrPercentageOrAuto = NonNegative; 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 { /// fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result { let this = >::from(*self); let other = >::from(*other); Ok(LengthOrPercentageOrNone::Calc(this.animate( &other, procedure, )? .ok_or(())?)) } fn compute_squared_distance_fallback(&self, other: &Self) -> Result { >::compute_squared_distance( &(*self).into(), &(*other).into(), ) } } impl LengthOrPercentageOrNone { /// Returns the used value. pub fn to_used_value(&self, containing_length: Au) -> Option { 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; 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 for NonNegativeLengthOrPercentage { #[inline] fn from(length: NonNegativeLength) -> Self { LengthOrPercentage::Length(length.0).into() } } impl From for NonNegativeLengthOrPercentage { #[inline] fn from(lop: LengthOrPercentage) -> Self { NonNegative::(lop) } } impl From for LengthOrPercentage { #[inline] fn from(lop: NonNegativeLengthOrPercentage) -> LengthOrPercentage { lop.0 } } impl NonNegativeLengthOrPercentage { /// Get zero value. #[inline] pub fn zero() -> Self { NonNegative::(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 `` 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(&self, dest: &mut CssWriter) -> 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 for Au { #[inline] fn from(len: CSSPixelLength) -> Self { Au::from_f32_px(len.0) } } impl From for CSSPixelLength { #[inline] fn from(len: Au) -> Self { CSSPixelLength::new(len.to_f32_px()) } } /// An alias of computed `` value. pub type Length = CSSPixelLength; /// Either a computed `` or the `auto` keyword. pub type LengthOrAuto = Either; /// Either a computed `` or a `` value. pub type LengthOrNumber = Either; impl LengthOrNumber { /// Returns `0`. #[inline] pub fn zero() -> Self { Either::Second(0.) } } /// Either a computed `` or the `normal` keyword. pub type LengthOrNormal = Either; /// A wrapper of Length, whose value must be >= 0. pub type NonNegativeLength = NonNegative; 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 for NonNegativeLength { type Output = Self; fn add(self, other: Self) -> Self { NonNegativeLength::new(self.px() + other.px()) } } impl From for NonNegativeLength { #[inline] fn from(len: Length) -> Self { NonNegative(len) } } impl From for NonNegativeLength { #[inline] fn from(au: Au) -> Self { NonNegative(au.into()) } } impl From 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; /// Either a computed NonNegativeLength or the `normal` keyword. pub type NonNegativeLengthOrNormal = Either; /// Either a computed NonNegativeLengthOrPercentage or the `normal` keyword. pub type NonNegativeLengthOrPercentageOrNormal = Either; /// 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, ToCss)] pub enum ExtremumLength { MozMaxContent, MozMinContent, MozFitContent, MozAvailable, } impl ExtremumLength { /// Returns whether this size keyword can be used for the given writing-mode /// and property. /// /// TODO: After these values are supported for both axes (and maybe /// unprefixed, see bug 1322780) all this complexity can go away, and /// everything can be derived (no need for uncacheable stuff). fn valid_for(&self, wm: WritingMode, longhand: LonghandId) -> bool { // We only make sense on the inline axis. match longhand { // FIXME(emilio): The flex-basis thing is not quite clear... LonghandId::FlexBasis | LonghandId::MinWidth | LonghandId::MaxWidth | LonghandId::Width => !wm.is_vertical(), LonghandId::MinHeight | LonghandId::MaxHeight | LonghandId::Height => wm.is_vertical(), LonghandId::MinInlineSize | LonghandId::MaxInlineSize | LonghandId::InlineSize => true, // The block-* properties are rejected at parse-time, so they're // unexpected here. _ => { debug_assert!( false, "Unexpected property using ExtremumLength: {:?}", longhand, ); false }, } } } /// A value suitable for a `min-width`, `min-height`, `width` or `height` /// property. /// /// See values/specified/length.rs for more details. #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(MallocSizeOf))] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToAnimatedZero, ToCss)] pub enum MozLength { LengthOrPercentageOrAuto(LengthOrPercentageOrAuto), #[animation(error)] ExtremumLength(ExtremumLength), } impl MozLength { /// Returns the `auto` value. #[inline] pub fn auto() -> Self { MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::Auto) } } impl ToComputedValue for specified::MozLength { type ComputedValue = MozLength; #[inline] fn to_computed_value(&self, context: &Context) -> MozLength { debug_assert!( context.for_non_inherited_property.is_some(), "Someone added a MozLength to an inherited property? Evil!" ); match *self { specified::MozLength::LengthOrPercentageOrAuto(ref lopoa) => { MozLength::LengthOrPercentageOrAuto(lopoa.to_computed_value(context)) }, specified::MozLength::ExtremumLength(ext) => { context .rule_cache_conditions .borrow_mut() .set_writing_mode_dependency(context.builder.writing_mode); if !ext.valid_for( context.builder.writing_mode, context.for_non_inherited_property.unwrap(), ) { MozLength::auto() } else { MozLength::ExtremumLength(ext) } }, } } #[inline] fn from_computed_value(computed: &MozLength) -> Self { match *computed { MozLength::LengthOrPercentageOrAuto(ref lopoa) => { specified::MozLength::LengthOrPercentageOrAuto( specified::LengthOrPercentageOrAuto::from_computed_value(lopoa), ) }, MozLength::ExtremumLength(ext) => specified::MozLength::ExtremumLength(ext), } } } /// A value suitable for a `max-width` or `max-height` property. /// See values/specified/length.rs for more details. #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(MallocSizeOf))] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToCss)] pub enum MaxLength { LengthOrPercentageOrNone(LengthOrPercentageOrNone), #[animation(error)] ExtremumLength(ExtremumLength), } impl MaxLength { /// Returns the `none` value. #[inline] pub fn none() -> Self { MaxLength::LengthOrPercentageOrNone(LengthOrPercentageOrNone::None) } } impl ToComputedValue for specified::MaxLength { type ComputedValue = MaxLength; #[inline] fn to_computed_value(&self, context: &Context) -> MaxLength { debug_assert!( context.for_non_inherited_property.is_some(), "Someone added a MaxLength to an inherited property? Evil!" ); match *self { specified::MaxLength::LengthOrPercentageOrNone(ref lopon) => { MaxLength::LengthOrPercentageOrNone(lopon.to_computed_value(context)) }, specified::MaxLength::ExtremumLength(ext) => { context .rule_cache_conditions .borrow_mut() .set_writing_mode_dependency(context.builder.writing_mode); if !ext.valid_for( context.builder.writing_mode, context.for_non_inherited_property.unwrap(), ) { MaxLength::none() } else { MaxLength::ExtremumLength(ext) } }, } } #[inline] fn from_computed_value(computed: &MaxLength) -> Self { match *computed { MaxLength::LengthOrPercentageOrNone(ref lopon) => { specified::MaxLength::LengthOrPercentageOrNone( specified::LengthOrPercentageOrNone::from_computed_value(&lopon), ) }, MaxLength::ExtremumLength(ref ext) => specified::MaxLength::ExtremumLength(ext.clone()), } } }