diff options
author | Yuki Izumi <kivikakk@github.com> | 2016-11-05 17:41:50 +1100 |
---|---|---|
committer | Yuki Izumi <kivikakk@github.com> | 2016-11-06 14:11:37 +1100 |
commit | f33f5605ba4a6684ac4af61b5d2d1d75ddfdb6d2 (patch) | |
tree | 03a32b66077ec4a1231c545f530ec1a64fca1857 | |
parent | 465619ca37c2a6bc7f60d25bf8bbd064e6c79ac0 (diff) | |
download | servo-f33f5605ba4a6684ac4af61b5d2d1d75ddfdb6d2.tar.gz servo-f33f5605ba4a6684ac4af61b5d2d1d75ddfdb6d2.zip |
Refactor style lengths per #13584
-rw-r--r-- | components/style/values/computed/length.rs | 508 | ||||
-rw-r--r-- | components/style/values/computed/mod.rs | 499 | ||||
-rw-r--r-- | components/style/values/specified/length.rs | 1004 | ||||
-rw-r--r-- | components/style/values/specified/mod.rs | 998 |
4 files changed, 1524 insertions, 1485 deletions
diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs new file mode 100644 index 00000000000..72056f0bbdd --- /dev/null +++ b/components/style/values/computed/length.rs @@ -0,0 +1,508 @@ +/* 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/. */ + +use app_units::Au; +use ordered_float::NotNaN; +use std::fmt; +use super::{ToComputedValue, Context}; +use values::{CSSFloat, LocalToCss, specified}; + +pub use cssparser::Color as CSSColor; +pub use super::image::{EndingShape as GradientShape, Gradient, GradientKind, Image}; +pub use super::image::{LengthOrKeyword, LengthOrPercentageOrKeyword}; +pub use values::specified::{Angle, BorderStyle, Time, UrlExtraData, UrlOrNone}; + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct CalcLengthOrPercentage { + pub length: Option<Au>, + pub percentage: Option<CSSFloat>, +} + +impl CalcLengthOrPercentage { + #[inline] + pub fn length(&self) -> Au { + self.length.unwrap_or(Au(0)) + } + + #[inline] + pub fn percentage(&self) -> CSSFloat { + self.percentage.unwrap_or(0.) + } +} + +impl From<LengthOrPercentage> for CalcLengthOrPercentage { + fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage { + match len { + LengthOrPercentage::Percentage(this) => { + CalcLengthOrPercentage { + length: None, + percentage: Some(this), + } + } + LengthOrPercentage::Length(this) => { + CalcLengthOrPercentage { + length: Some(this), + percentage: None, + } + } + LengthOrPercentage::Calc(this) => { + this + } + } + } +} + +impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> { + fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> { + match len { + LengthOrPercentageOrAuto::Percentage(this) => { + Some(CalcLengthOrPercentage { + length: None, + percentage: Some(this), + }) + } + LengthOrPercentageOrAuto::Length(this) => { + Some(CalcLengthOrPercentage { + length: Some(this), + percentage: None, + }) + } + LengthOrPercentageOrAuto::Calc(this) => { + Some(this) + } + LengthOrPercentageOrAuto::Auto => { + None + } + } + } +} + +impl ::cssparser::ToCss for CalcLengthOrPercentage { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match (self.length, self.percentage) { + (None, Some(p)) => write!(dest, "{}%", p * 100.), + (Some(l), None) => write!(dest, "{}px", Au::to_px(l)), + (Some(l), Some(p)) => write!(dest, "calc({}px + {}%)", Au::to_px(l), p * 100.), + _ => unreachable!() + } + } +} + +impl ToComputedValue for specified::CalcLengthOrPercentage { + type ComputedValue = CalcLengthOrPercentage; + + fn to_computed_value(&self, context: &Context) -> CalcLengthOrPercentage { + self.compute_from_viewport_and_font_size(context.viewport_size(), + context.style().get_font().clone_font_size(), + context.style().root_font_size()) + + } + + #[inline] + fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self { + specified::CalcLengthOrPercentage { + absolute: computed.length, + percentage: computed.percentage.map(specified::Percentage), + ..Default::default() + } + } +} + +#[derive(PartialEq, Clone, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentage { + Length(Au), + Percentage(CSSFloat), + Calc(CalcLengthOrPercentage), +} + +impl LengthOrPercentage { + #[inline] + pub fn zero() -> LengthOrPercentage { + LengthOrPercentage::Length(Au(0)) + } + + /// 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(Au(0)) | Percentage(0.0) => true, + Length(_) | Percentage(_) | Calc(_) => false + } + } + + pub fn to_hash_key(&self) -> (Au, NotNaN<f32>) { + use self::LengthOrPercentage::*; + match *self { + Length(l) => (l, NotNaN::new(0.0).unwrap()), + Percentage(p) => (Au(0), NotNaN::new(p).unwrap()), + Calc(c) => (c.length(), NotNaN::new(c.percentage()).unwrap()), + } + } +} + +impl fmt::Debug for LengthOrPercentage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LengthOrPercentage::Length(length) => write!(f, "{:?}", length), + LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage * 100.), + LengthOrPercentage::Calc(calc) => write!(f, "{:?}", calc), + } + } +} + +impl ToComputedValue for specified::LengthOrPercentage { + type ComputedValue = LengthOrPercentage; + + fn to_computed_value(&self, context: &Context) -> LengthOrPercentage { + match *self { + specified::LengthOrPercentage::Length(value) => { + LengthOrPercentage::Length(value.to_computed_value(context)) + } + specified::LengthOrPercentage::Percentage(value) => { + LengthOrPercentage::Percentage(value.0) + } + specified::LengthOrPercentage::Calc(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(specified::Percentage(value)) + } + LengthOrPercentage::Calc(calc) => { + specified::LengthOrPercentage::Calc( + ToComputedValue::from_computed_value(&calc) + ) + } + } + } +} + +impl ::cssparser::ToCss for LengthOrPercentage { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentage::Length(length) => length.to_css(dest), + LengthOrPercentage::Percentage(percentage) + => write!(dest, "{}%", percentage * 100.), + LengthOrPercentage::Calc(calc) => calc.to_css(dest), + } + } +} + +#[derive(PartialEq, Clone, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentageOrAuto { + Length(Au), + Percentage(CSSFloat), + Auto, + Calc(CalcLengthOrPercentage), +} + +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(Au(0)) | Percentage(0.0) => true, + Length(_) | Percentage(_) | Calc(_) | Auto => false + } + } +} + +impl fmt::Debug for LengthOrPercentageOrAuto { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LengthOrPercentageOrAuto::Length(length) => write!(f, "{:?}", length), + LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage * 100.), + LengthOrPercentageOrAuto::Auto => write!(f, "auto"), + LengthOrPercentageOrAuto::Calc(calc) => write!(f, "{:?}", calc), + } + } +} + +impl ToComputedValue for specified::LengthOrPercentageOrAuto { + type ComputedValue = LengthOrPercentageOrAuto; + + #[inline] + fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrAuto { + match *self { + specified::LengthOrPercentageOrAuto::Length(value) => { + LengthOrPercentageOrAuto::Length(value.to_computed_value(context)) + } + specified::LengthOrPercentageOrAuto::Percentage(value) => { + LengthOrPercentageOrAuto::Percentage(value.0) + } + specified::LengthOrPercentageOrAuto::Auto => { + LengthOrPercentageOrAuto::Auto + } + specified::LengthOrPercentageOrAuto::Calc(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(specified::Percentage(value)) + } + LengthOrPercentageOrAuto::Calc(calc) => { + specified::LengthOrPercentageOrAuto::Calc( + ToComputedValue::from_computed_value(&calc) + ) + } + } + } +} + +impl ::cssparser::ToCss for LengthOrPercentageOrAuto { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentageOrAuto::Length(length) => length.to_css(dest), + LengthOrPercentageOrAuto::Percentage(percentage) + => write!(dest, "{}%", percentage * 100.), + LengthOrPercentageOrAuto::Auto => dest.write_str("auto"), + LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest), + } + } +} + +#[derive(PartialEq, Clone, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentageOrAutoOrContent { + Length(Au), + Percentage(CSSFloat), + Calc(CalcLengthOrPercentage), + Auto, + Content +} + +impl fmt::Debug for LengthOrPercentageOrAutoOrContent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LengthOrPercentageOrAutoOrContent::Length(length) => write!(f, "{:?}", length), + LengthOrPercentageOrAutoOrContent::Percentage(percentage) => write!(f, "{}%", percentage * 100.), + LengthOrPercentageOrAutoOrContent::Calc(calc) => write!(f, "{:?}", calc), + LengthOrPercentageOrAutoOrContent::Auto => write!(f, "auto"), + LengthOrPercentageOrAutoOrContent::Content => write!(f, "content") + } + } +} + +impl ToComputedValue for specified::LengthOrPercentageOrAutoOrContent { + type ComputedValue = LengthOrPercentageOrAutoOrContent; + + #[inline] + fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrAutoOrContent { + match *self { + specified::LengthOrPercentageOrAutoOrContent::Length(value) => { + LengthOrPercentageOrAutoOrContent::Length(value.to_computed_value(context)) + }, + specified::LengthOrPercentageOrAutoOrContent::Percentage(value) => { + LengthOrPercentageOrAutoOrContent::Percentage(value.0) + }, + specified::LengthOrPercentageOrAutoOrContent::Calc(calc) => { + LengthOrPercentageOrAutoOrContent::Calc(calc.to_computed_value(context)) + }, + specified::LengthOrPercentageOrAutoOrContent::Auto => { + LengthOrPercentageOrAutoOrContent::Auto + }, + specified::LengthOrPercentageOrAutoOrContent::Content => { + LengthOrPercentageOrAutoOrContent::Content + } + } + } + + + #[inline] + fn from_computed_value(computed: &LengthOrPercentageOrAutoOrContent) -> Self { + match *computed { + LengthOrPercentageOrAutoOrContent::Auto => { + specified::LengthOrPercentageOrAutoOrContent::Auto + } + LengthOrPercentageOrAutoOrContent::Content => { + specified::LengthOrPercentageOrAutoOrContent::Content + } + LengthOrPercentageOrAutoOrContent::Length(value) => { + specified::LengthOrPercentageOrAutoOrContent::Length( + ToComputedValue::from_computed_value(&value) + ) + } + LengthOrPercentageOrAutoOrContent::Percentage(value) => { + specified::LengthOrPercentageOrAutoOrContent::Percentage(specified::Percentage(value)) + } + LengthOrPercentageOrAutoOrContent::Calc(calc) => { + specified::LengthOrPercentageOrAutoOrContent::Calc( + ToComputedValue::from_computed_value(&calc) + ) + } + } + } +} + +impl ::cssparser::ToCss for LengthOrPercentageOrAutoOrContent { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentageOrAutoOrContent::Length(length) => length.to_css(dest), + LengthOrPercentageOrAutoOrContent::Percentage(percentage) + => write!(dest, "{}%", percentage * 100.), + LengthOrPercentageOrAutoOrContent::Calc(calc) => calc.to_css(dest), + LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"), + LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content") + } + } +} + +#[derive(PartialEq, Clone, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentageOrNone { + Length(Au), + Percentage(CSSFloat), + Calc(CalcLengthOrPercentage), + None, +} + +impl fmt::Debug for LengthOrPercentageOrNone { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LengthOrPercentageOrNone::Length(length) => write!(f, "{:?}", length), + LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage * 100.), + LengthOrPercentageOrNone::Calc(calc) => write!(f, "{:?}", calc), + LengthOrPercentageOrNone::None => write!(f, "none"), + } + } +} + +impl ToComputedValue for specified::LengthOrPercentageOrNone { + type ComputedValue = LengthOrPercentageOrNone; + + #[inline] + fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrNone { + match *self { + specified::LengthOrPercentageOrNone::Length(value) => { + LengthOrPercentageOrNone::Length(value.to_computed_value(context)) + } + specified::LengthOrPercentageOrNone::Percentage(value) => { + LengthOrPercentageOrNone::Percentage(value.0) + } + specified::LengthOrPercentageOrNone::Calc(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(specified::Percentage(value)) + } + LengthOrPercentageOrNone::Calc(calc) => { + specified::LengthOrPercentageOrNone::Calc( + ToComputedValue::from_computed_value(&calc) + ) + } + } + } +} + +impl ::cssparser::ToCss for LengthOrPercentageOrNone { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentageOrNone::Length(length) => length.to_css(dest), + LengthOrPercentageOrNone::Percentage(percentage) => + write!(dest, "{}%", percentage * 100.), + LengthOrPercentageOrNone::Calc(calc) => calc.to_css(dest), + LengthOrPercentageOrNone::None => dest.write_str("none"), + } + } +} + +#[derive(PartialEq, Clone, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrNone { + Length(Au), + None, +} + +impl fmt::Debug for LengthOrNone { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LengthOrNone::Length(length) => write!(f, "{:?}", length), + LengthOrNone::None => write!(f, "none"), + } + } +} + +impl ToComputedValue for specified::LengthOrNone { + type ComputedValue = LengthOrNone; + + #[inline] + fn to_computed_value(&self, context: &Context) -> LengthOrNone { + match *self { + specified::LengthOrNone::Length(specified::Length::Calc(calc, range)) => { + LengthOrNone::Length(range.clamp(calc.to_computed_value(context).length())) + } + specified::LengthOrNone::Length(value) => { + LengthOrNone::Length(value.to_computed_value(context)) + } + specified::LengthOrNone::None => { + LengthOrNone::None + } + } + } + + #[inline] + fn from_computed_value(computed: &LengthOrNone) -> Self { + match *computed { + LengthOrNone::Length(au) => { + specified::LengthOrNone::Length(ToComputedValue::from_computed_value(&au)) + } + LengthOrNone::None => { + specified::LengthOrNone::None + } + } + } +} + +impl ::cssparser::ToCss for LengthOrNone { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrNone::Length(length) => length.to_css(dest), + LengthOrNone::None => dest.write_str("none"), + } + } +} diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 42abdf5b933..de5dfa76844 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -4,19 +4,20 @@ use app_units::Au; use euclid::size::Size2D; -use ordered_float::NotNaN; use properties::ComputedValues; use std::fmt; use super::{CSSFloat, specified}; -use super::LocalToCss; pub use cssparser::Color as CSSColor; pub use self::image::{EndingShape as GradientShape, Gradient, GradientKind, Image}; pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword}; pub use super::specified::{Angle, BorderStyle, Time, UrlExtraData, UrlOrNone}; +pub use self::length::{CalcLengthOrPercentage, LengthOrPercentage, LengthOrPercentageOrAuto}; +pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; pub mod basic_shape; pub mod image; +pub mod length; pub mod position; pub struct Context<'a> { @@ -110,104 +111,6 @@ impl ToComputedValue for specified::Length { } } -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct CalcLengthOrPercentage { - pub length: Option<Au>, - pub percentage: Option<CSSFloat>, -} - -impl CalcLengthOrPercentage { - #[inline] - pub fn length(&self) -> Au { - self.length.unwrap_or(Au(0)) - } - - #[inline] - pub fn percentage(&self) -> CSSFloat { - self.percentage.unwrap_or(0.) - } -} - -impl From<LengthOrPercentage> for CalcLengthOrPercentage { - fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage { - match len { - LengthOrPercentage::Percentage(this) => { - CalcLengthOrPercentage { - length: None, - percentage: Some(this), - } - } - LengthOrPercentage::Length(this) => { - CalcLengthOrPercentage { - length: Some(this), - percentage: None, - } - } - LengthOrPercentage::Calc(this) => { - this - } - } - } -} - -impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> { - fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> { - match len { - LengthOrPercentageOrAuto::Percentage(this) => { - Some(CalcLengthOrPercentage { - length: None, - percentage: Some(this), - }) - } - LengthOrPercentageOrAuto::Length(this) => { - Some(CalcLengthOrPercentage { - length: Some(this), - percentage: None, - }) - } - LengthOrPercentageOrAuto::Calc(this) => { - Some(this) - } - LengthOrPercentageOrAuto::Auto => { - None - } - } - } -} - -impl ::cssparser::ToCss for CalcLengthOrPercentage { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match (self.length, self.percentage) { - (None, Some(p)) => write!(dest, "{}%", p * 100.), - (Some(l), None) => write!(dest, "{}px", Au::to_px(l)), - (Some(l), Some(p)) => write!(dest, "calc({}px + {}%)", Au::to_px(l), p * 100.), - _ => unreachable!() - } - } -} - -impl ToComputedValue for specified::CalcLengthOrPercentage { - type ComputedValue = CalcLengthOrPercentage; - - fn to_computed_value(&self, context: &Context) -> CalcLengthOrPercentage { - self.compute_from_viewport_and_font_size(context.viewport_size(), - context.style().get_font().clone_font_size(), - context.style().root_font_size()) - - } - - #[inline] - fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self { - specified::CalcLengthOrPercentage { - absolute: computed.length, - percentage: computed.percentage.map(specified::Percentage), - ..Default::default() - } - } -} - - #[derive(Debug, PartialEq, Clone, Copy)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>); @@ -244,402 +147,6 @@ impl ::cssparser::ToCss for BorderRadiusSize { } } -#[derive(PartialEq, Clone, Copy)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrPercentage { - Length(Au), - Percentage(CSSFloat), - Calc(CalcLengthOrPercentage), -} - -impl LengthOrPercentage { - #[inline] - pub fn zero() -> LengthOrPercentage { - LengthOrPercentage::Length(Au(0)) - } - - /// 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(Au(0)) | Percentage(0.0) => true, - Length(_) | Percentage(_) | Calc(_) => false - } - } - - pub fn to_hash_key(&self) -> (Au, NotNaN<f32>) { - use self::LengthOrPercentage::*; - match *self { - Length(l) => (l, NotNaN::new(0.0).unwrap()), - Percentage(p) => (Au(0), NotNaN::new(p).unwrap()), - Calc(c) => (c.length(), NotNaN::new(c.percentage()).unwrap()), - } - } -} - -impl fmt::Debug for LengthOrPercentage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrPercentage::Length(length) => write!(f, "{:?}", length), - LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage * 100.), - LengthOrPercentage::Calc(calc) => write!(f, "{:?}", calc), - } - } -} - -impl ToComputedValue for specified::LengthOrPercentage { - type ComputedValue = LengthOrPercentage; - - fn to_computed_value(&self, context: &Context) -> LengthOrPercentage { - match *self { - specified::LengthOrPercentage::Length(value) => { - LengthOrPercentage::Length(value.to_computed_value(context)) - } - specified::LengthOrPercentage::Percentage(value) => { - LengthOrPercentage::Percentage(value.0) - } - specified::LengthOrPercentage::Calc(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(specified::Percentage(value)) - } - LengthOrPercentage::Calc(calc) => { - specified::LengthOrPercentage::Calc( - ToComputedValue::from_computed_value(&calc) - ) - } - } - } -} - -impl ::cssparser::ToCss for LengthOrPercentage { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentage::Length(length) => length.to_css(dest), - LengthOrPercentage::Percentage(percentage) - => write!(dest, "{}%", percentage * 100.), - LengthOrPercentage::Calc(calc) => calc.to_css(dest), - } - } -} - -#[derive(PartialEq, Clone, Copy)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrPercentageOrAuto { - Length(Au), - Percentage(CSSFloat), - Auto, - Calc(CalcLengthOrPercentage), -} - -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(Au(0)) | Percentage(0.0) => true, - Length(_) | Percentage(_) | Calc(_) | Auto => false - } - } -} - -impl fmt::Debug for LengthOrPercentageOrAuto { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrPercentageOrAuto::Length(length) => write!(f, "{:?}", length), - LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage * 100.), - LengthOrPercentageOrAuto::Auto => write!(f, "auto"), - LengthOrPercentageOrAuto::Calc(calc) => write!(f, "{:?}", calc), - } - } -} - -impl ToComputedValue for specified::LengthOrPercentageOrAuto { - type ComputedValue = LengthOrPercentageOrAuto; - - #[inline] - fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrAuto { - match *self { - specified::LengthOrPercentageOrAuto::Length(value) => { - LengthOrPercentageOrAuto::Length(value.to_computed_value(context)) - } - specified::LengthOrPercentageOrAuto::Percentage(value) => { - LengthOrPercentageOrAuto::Percentage(value.0) - } - specified::LengthOrPercentageOrAuto::Auto => { - LengthOrPercentageOrAuto::Auto - } - specified::LengthOrPercentageOrAuto::Calc(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(specified::Percentage(value)) - } - LengthOrPercentageOrAuto::Calc(calc) => { - specified::LengthOrPercentageOrAuto::Calc( - ToComputedValue::from_computed_value(&calc) - ) - } - } - } -} - -impl ::cssparser::ToCss for LengthOrPercentageOrAuto { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentageOrAuto::Length(length) => length.to_css(dest), - LengthOrPercentageOrAuto::Percentage(percentage) - => write!(dest, "{}%", percentage * 100.), - LengthOrPercentageOrAuto::Auto => dest.write_str("auto"), - LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest), - } - } -} - -#[derive(PartialEq, Clone, Copy)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrPercentageOrAutoOrContent { - Length(Au), - Percentage(CSSFloat), - Calc(CalcLengthOrPercentage), - Auto, - Content -} - -impl fmt::Debug for LengthOrPercentageOrAutoOrContent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrPercentageOrAutoOrContent::Length(length) => write!(f, "{:?}", length), - LengthOrPercentageOrAutoOrContent::Percentage(percentage) => write!(f, "{}%", percentage * 100.), - LengthOrPercentageOrAutoOrContent::Calc(calc) => write!(f, "{:?}", calc), - LengthOrPercentageOrAutoOrContent::Auto => write!(f, "auto"), - LengthOrPercentageOrAutoOrContent::Content => write!(f, "content") - } - } -} - -impl ToComputedValue for specified::LengthOrPercentageOrAutoOrContent { - type ComputedValue = LengthOrPercentageOrAutoOrContent; - - #[inline] - fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrAutoOrContent { - match *self { - specified::LengthOrPercentageOrAutoOrContent::Length(value) => { - LengthOrPercentageOrAutoOrContent::Length(value.to_computed_value(context)) - }, - specified::LengthOrPercentageOrAutoOrContent::Percentage(value) => { - LengthOrPercentageOrAutoOrContent::Percentage(value.0) - }, - specified::LengthOrPercentageOrAutoOrContent::Calc(calc) => { - LengthOrPercentageOrAutoOrContent::Calc(calc.to_computed_value(context)) - }, - specified::LengthOrPercentageOrAutoOrContent::Auto => { - LengthOrPercentageOrAutoOrContent::Auto - }, - specified::LengthOrPercentageOrAutoOrContent::Content => { - LengthOrPercentageOrAutoOrContent::Content - } - } - } - - - #[inline] - fn from_computed_value(computed: &LengthOrPercentageOrAutoOrContent) -> Self { - match *computed { - LengthOrPercentageOrAutoOrContent::Auto => { - specified::LengthOrPercentageOrAutoOrContent::Auto - } - LengthOrPercentageOrAutoOrContent::Content => { - specified::LengthOrPercentageOrAutoOrContent::Content - } - LengthOrPercentageOrAutoOrContent::Length(value) => { - specified::LengthOrPercentageOrAutoOrContent::Length( - ToComputedValue::from_computed_value(&value) - ) - } - LengthOrPercentageOrAutoOrContent::Percentage(value) => { - specified::LengthOrPercentageOrAutoOrContent::Percentage(specified::Percentage(value)) - } - LengthOrPercentageOrAutoOrContent::Calc(calc) => { - specified::LengthOrPercentageOrAutoOrContent::Calc( - ToComputedValue::from_computed_value(&calc) - ) - } - } - } -} - -impl ::cssparser::ToCss for LengthOrPercentageOrAutoOrContent { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentageOrAutoOrContent::Length(length) => length.to_css(dest), - LengthOrPercentageOrAutoOrContent::Percentage(percentage) - => write!(dest, "{}%", percentage * 100.), - LengthOrPercentageOrAutoOrContent::Calc(calc) => calc.to_css(dest), - LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"), - LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content") - } - } -} - -#[derive(PartialEq, Clone, Copy)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrPercentageOrNone { - Length(Au), - Percentage(CSSFloat), - Calc(CalcLengthOrPercentage), - None, -} - -impl fmt::Debug for LengthOrPercentageOrNone { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrPercentageOrNone::Length(length) => write!(f, "{:?}", length), - LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage * 100.), - LengthOrPercentageOrNone::Calc(calc) => write!(f, "{:?}", calc), - LengthOrPercentageOrNone::None => write!(f, "none"), - } - } -} - -impl ToComputedValue for specified::LengthOrPercentageOrNone { - type ComputedValue = LengthOrPercentageOrNone; - - #[inline] - fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrNone { - match *self { - specified::LengthOrPercentageOrNone::Length(value) => { - LengthOrPercentageOrNone::Length(value.to_computed_value(context)) - } - specified::LengthOrPercentageOrNone::Percentage(value) => { - LengthOrPercentageOrNone::Percentage(value.0) - } - specified::LengthOrPercentageOrNone::Calc(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(specified::Percentage(value)) - } - LengthOrPercentageOrNone::Calc(calc) => { - specified::LengthOrPercentageOrNone::Calc( - ToComputedValue::from_computed_value(&calc) - ) - } - } - } -} - -impl ::cssparser::ToCss for LengthOrPercentageOrNone { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentageOrNone::Length(length) => length.to_css(dest), - LengthOrPercentageOrNone::Percentage(percentage) => - write!(dest, "{}%", percentage * 100.), - LengthOrPercentageOrNone::Calc(calc) => calc.to_css(dest), - LengthOrPercentageOrNone::None => dest.write_str("none"), - } - } -} - -#[derive(PartialEq, Clone, Copy)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrNone { - Length(Au), - None, -} - -impl fmt::Debug for LengthOrNone { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrNone::Length(length) => write!(f, "{:?}", length), - LengthOrNone::None => write!(f, "none"), - } - } -} - -impl ToComputedValue for specified::LengthOrNone { - type ComputedValue = LengthOrNone; - - #[inline] - fn to_computed_value(&self, context: &Context) -> LengthOrNone { - match *self { - specified::LengthOrNone::Length(specified::Length::Calc(calc, range)) => { - LengthOrNone::Length(range.clamp(calc.to_computed_value(context).length())) - } - specified::LengthOrNone::Length(value) => { - LengthOrNone::Length(value.to_computed_value(context)) - } - specified::LengthOrNone::None => { - LengthOrNone::None - } - } - } - - #[inline] - fn from_computed_value(computed: &LengthOrNone) -> Self { - match *computed { - LengthOrNone::Length(au) => { - specified::LengthOrNone::Length(ToComputedValue::from_computed_value(&au)) - } - LengthOrNone::None => { - specified::LengthOrNone::None - } - } - } -} - -impl ::cssparser::ToCss for LengthOrNone { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrNone::Length(length) => length.to_css(dest), - LengthOrNone::None => dest.write_str("none"), - } - } -} pub type Length = Au; pub type Number = CSSFloat; diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs new file mode 100644 index 00000000000..0eca7c16ac2 --- /dev/null +++ b/components/style/values/specified/length.rs @@ -0,0 +1,1004 @@ +/* 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/. */ + +use app_units::Au; +use cssparser::{Parser, ToCss, Token}; +use euclid::size::Size2D; +use parser::Parse; +use std::ascii::AsciiExt; +use std::cmp; +use std::fmt; +use std::ops::Mul; +use style_traits::values::specified::AllowedNumericType; +use super::{Angle, SimplifiedValueNode, SimplifiedSumNode, Time}; +use values::{CSSFloat, FONT_MEDIUM_PX, HasViewportPercentage, LocalToCss, computed}; + +pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; +pub use super::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword}; +pub use super::image::{SizeKeyword, VerticalDirection}; + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum FontRelativeLength { + Em(CSSFloat), + Ex(CSSFloat), + Ch(CSSFloat), + Rem(CSSFloat) +} + +impl ToCss for FontRelativeLength { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + FontRelativeLength::Em(length) => write!(dest, "{}em", length), + FontRelativeLength::Ex(length) => write!(dest, "{}ex", length), + FontRelativeLength::Ch(length) => write!(dest, "{}ch", length), + FontRelativeLength::Rem(length) => write!(dest, "{}rem", length) + } + } +} + +impl FontRelativeLength { + pub fn to_computed_value(&self, + reference_font_size: Au, + root_font_size: Au) + -> Au + { + match *self { + FontRelativeLength::Em(length) => reference_font_size.scale_by(length), + FontRelativeLength::Ex(length) | FontRelativeLength::Ch(length) => { + // https://github.com/servo/servo/issues/7462 + let em_factor = 0.5; + reference_font_size.scale_by(length * em_factor) + }, + FontRelativeLength::Rem(length) => root_font_size.scale_by(length) + } + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum ViewportPercentageLength { + Vw(CSSFloat), + Vh(CSSFloat), + Vmin(CSSFloat), + Vmax(CSSFloat) +} + +impl HasViewportPercentage for ViewportPercentageLength { + fn has_viewport_percentage(&self) -> bool { + true + } +} + +impl ToCss for ViewportPercentageLength { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + ViewportPercentageLength::Vw(length) => write!(dest, "{}vw", length), + ViewportPercentageLength::Vh(length) => write!(dest, "{}vh", length), + ViewportPercentageLength::Vmin(length) => write!(dest, "{}vmin", length), + ViewportPercentageLength::Vmax(length) => write!(dest, "{}vmax", length) + } + } +} + +impl ViewportPercentageLength { + pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> Au { + macro_rules! to_unit { + ($viewport_dimension:expr) => { + $viewport_dimension.to_f32_px() / 100.0 + } + } + + let value = match *self { + ViewportPercentageLength::Vw(length) => + length * to_unit!(viewport_size.width), + ViewportPercentageLength::Vh(length) => + length * to_unit!(viewport_size.height), + ViewportPercentageLength::Vmin(length) => + length * to_unit!(cmp::min(viewport_size.width, viewport_size.height)), + ViewportPercentageLength::Vmax(length) => + length * to_unit!(cmp::max(viewport_size.width, viewport_size.height)), + }; + Au::from_f32_px(value) + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct CharacterWidth(pub i32); + +impl CharacterWidth { + pub fn to_computed_value(&self, reference_font_size: Au) -> Au { + // 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.scale_by(0.5); + let max_advance = reference_font_size; + average_advance.scale_by(self.0 as CSSFloat - 1.0) + max_advance + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Length { + Absolute(Au), // application units + FontRelative(FontRelativeLength), + ViewportPercentage(ViewportPercentageLength), + + /// 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), + + Calc(CalcLengthOrPercentage, AllowedNumericType), +} + +impl HasViewportPercentage for Length { + fn has_viewport_percentage(&self) -> bool { + match *self { + Length::ViewportPercentage(_) => true, + Length::Calc(ref calc, _) => calc.has_viewport_percentage(), + _ => false + } + } +} + +impl ToCss for Length { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + Length::Absolute(length) => write!(dest, "{}px", length.to_f32_px()), + Length::FontRelative(length) => length.to_css(dest), + Length::ViewportPercentage(length) => length.to_css(dest), + Length::Calc(ref calc, _) => calc.to_css(dest), + /* This should only be reached from style dumping code */ + Length::ServoCharacterWidth(CharacterWidth(i)) => write!(dest, "CharWidth({})", i), + } + } +} + +impl Mul<CSSFloat> for Length { + type Output = Length; + + #[inline] + fn mul(self, scalar: CSSFloat) -> Length { + match self { + Length::Absolute(Au(v)) => Length::Absolute(Au(((v as f32) * scalar) as i32)), + Length::FontRelative(v) => Length::FontRelative(v * scalar), + Length::ViewportPercentage(v) => Length::ViewportPercentage(v * scalar), + Length::Calc(..) => panic!("Can't multiply Calc!"), + Length::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"), + } + } +} + +impl Mul<CSSFloat> for FontRelativeLength { + type Output = FontRelativeLength; + + #[inline] + fn mul(self, scalar: CSSFloat) -> FontRelativeLength { + match self { + FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar), + FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar), + FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar), + FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar), + } + } +} + +impl Mul<CSSFloat> for ViewportPercentageLength { + type Output = ViewportPercentageLength; + + #[inline] + fn mul(self, scalar: CSSFloat) -> ViewportPercentageLength { + match self { + ViewportPercentageLength::Vw(v) => ViewportPercentageLength::Vw(v * scalar), + ViewportPercentageLength::Vh(v) => ViewportPercentageLength::Vh(v * scalar), + ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar), + ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar), + } + } +} + +const AU_PER_PX: CSSFloat = 60.; +const AU_PER_IN: CSSFloat = AU_PER_PX * 96.; +const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54; +const AU_PER_MM: CSSFloat = AU_PER_IN / 25.4; +const AU_PER_Q: CSSFloat = AU_PER_MM / 4.; +const AU_PER_PT: CSSFloat = AU_PER_IN / 72.; +const AU_PER_PC: CSSFloat = AU_PER_PT * 12.; + +impl Length { + // https://drafts.csswg.org/css-fonts-3/#font-size-prop + pub fn from_str(s: &str) -> Option<Length> { + Some(match_ignore_ascii_case! { s, + "xx-small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 5), + "x-small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 4), + "small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 8 / 9), + "medium" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX)), + "large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 6 / 5), + "x-large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 2), + "xx-large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 2), + + // https://github.com/servo/servo/issues/3423#issuecomment-56321664 + "smaller" => Length::FontRelative(FontRelativeLength::Em(0.85)), + "larger" => Length::FontRelative(FontRelativeLength::Em(1.2)), + _ => return None + }) + } + + #[inline] + fn parse_internal(input: &mut Parser, context: AllowedNumericType) -> Result<Length, ()> { + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => + Length::parse_dimension(value.value, unit), + Token::Number(ref value) if value.value == 0. => + Ok(Length::Absolute(Au(0))), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => + input.parse_nested_block(|input| { + CalcLengthOrPercentage::parse_length(input, context) + }), + _ => Err(()) + } + } + pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> { + Length::parse_internal(input, AllowedNumericType::NonNegative) + } + pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> { + match_ignore_ascii_case! { unit, + "px" => Ok(Length::from_px(value)), + "in" => Ok(Length::Absolute(Au((value * AU_PER_IN) as i32))), + "cm" => Ok(Length::Absolute(Au((value * AU_PER_CM) as i32))), + "mm" => Ok(Length::Absolute(Au((value * AU_PER_MM) as i32))), + "q" => Ok(Length::Absolute(Au((value * AU_PER_Q) as i32))), + "pt" => Ok(Length::Absolute(Au((value * AU_PER_PT) as i32))), + "pc" => Ok(Length::Absolute(Au((value * AU_PER_PC) as i32))), + // font-relative + "em" => Ok(Length::FontRelative(FontRelativeLength::Em(value))), + "ex" => Ok(Length::FontRelative(FontRelativeLength::Ex(value))), + "ch" => Ok(Length::FontRelative(FontRelativeLength::Ch(value))), + "rem" => Ok(Length::FontRelative(FontRelativeLength::Rem(value))), + // viewport percentages + "vw" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vw(value))), + "vh" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vh(value))), + "vmin" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmin(value))), + "vmax" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmax(value))), + _ => Err(()) + } + } + #[inline] + pub fn from_px(px_value: CSSFloat) -> Length { + Length::Absolute(Au((px_value * AU_PER_PX) as i32)) + } +} + +impl Parse for Length { + fn parse(input: &mut Parser) -> Result<Self, ()> { + Length::parse_internal(input, AllowedNumericType::All) + } +} + +#[derive(Clone, Debug)] +pub struct CalcSumNode { + pub products: Vec<CalcProductNode>, +} + +#[derive(Clone, Debug)] +pub struct CalcProductNode { + values: Vec<CalcValueNode> +} + +#[derive(Clone, Debug)] +pub enum CalcValueNode { + Length(Length), + Angle(Angle), + Time(Time), + Percentage(CSSFloat), + Number(CSSFloat), + Sum(Box<CalcSumNode>), +} + +#[derive(Clone, Copy, PartialEq)] +pub enum CalcUnit { + Number, + Integer, + Length, + LengthOrPercentage, + Angle, + Time, +} + +#[derive(Clone, PartialEq, Copy, Debug, Default)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct CalcLengthOrPercentage { + pub absolute: Option<Au>, + pub vw: Option<ViewportPercentageLength>, + pub vh: Option<ViewportPercentageLength>, + pub vmin: Option<ViewportPercentageLength>, + pub vmax: Option<ViewportPercentageLength>, + pub em: Option<FontRelativeLength>, + pub ex: Option<FontRelativeLength>, + pub ch: Option<FontRelativeLength>, + pub rem: Option<FontRelativeLength>, + pub percentage: Option<Percentage>, +} + +impl CalcLengthOrPercentage { + pub fn parse_sum(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcSumNode, ()> { + let mut products = Vec::new(); + products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit))); + + while let Ok(token) = input.next() { + match token { + Token::Delim('+') => { + products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit))); + } + Token::Delim('-') => { + let mut right = try!(CalcLengthOrPercentage::parse_product(input, expected_unit)); + right.values.push(CalcValueNode::Number(-1.)); + products.push(right); + } + _ => return Err(()) + } + } + + Ok(CalcSumNode { products: products }) + } + + fn parse_product(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcProductNode, ()> { + let mut values = Vec::new(); + values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit))); + + loop { + let position = input.position(); + match input.next() { + Ok(Token::Delim('*')) => { + values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit))); + } + Ok(Token::Delim('/')) if expected_unit != CalcUnit::Integer => { + if let Ok(Token::Number(ref value)) = input.next() { + if value.value == 0. { + return Err(()); + } + values.push(CalcValueNode::Number(1. / value.value)); + } else { + return Err(()); + } + } + _ => { + input.reset(position); + break + } + } + } + + Ok(CalcProductNode { values: values }) + } + + fn parse_value(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcValueNode, ()> { + match (try!(input.next()), expected_unit) { + (Token::Number(ref value), _) => Ok(CalcValueNode::Number(value.value)), + (Token::Dimension(ref value, ref unit), CalcUnit::Length) | + (Token::Dimension(ref value, ref unit), CalcUnit::LengthOrPercentage) => { + Length::parse_dimension(value.value, unit).map(CalcValueNode::Length) + } + (Token::Dimension(ref value, ref unit), CalcUnit::Angle) => { + Angle::parse_dimension(value.value, unit).map(CalcValueNode::Angle) + } + (Token::Dimension(ref value, ref unit), CalcUnit::Time) => { + Time::parse_dimension(value.value, unit).map(CalcValueNode::Time) + } + (Token::Percentage(ref value), CalcUnit::LengthOrPercentage) => + Ok(CalcValueNode::Percentage(value.unit_value)), + (Token::ParenthesisBlock, _) => { + input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, expected_unit)) + .map(|result| CalcValueNode::Sum(Box::new(result))) + }, + _ => Err(()) + } + } + + fn simplify_value_to_number(node: &CalcValueNode) -> Option<CSSFloat> { + match *node { + CalcValueNode::Number(number) => Some(number), + CalcValueNode::Sum(ref sum) => CalcLengthOrPercentage::simplify_sum_to_number(sum), + _ => None + } + } + + fn simplify_sum_to_number(node: &CalcSumNode) -> Option<CSSFloat> { + let mut sum = 0.; + for ref product in &node.products { + match CalcLengthOrPercentage::simplify_product_to_number(product) { + Some(number) => sum += number, + _ => return None + } + } + Some(sum) + } + + fn simplify_product_to_number(node: &CalcProductNode) -> Option<CSSFloat> { + let mut product = 1.; + for ref value in &node.values { + match CalcLengthOrPercentage::simplify_value_to_number(value) { + Some(number) => product *= number, + _ => return None + } + } + Some(product) + } + + fn simplify_products_in_sum(node: &CalcSumNode) -> Result<SimplifiedValueNode, ()> { + let mut simplified = Vec::new(); + for product in &node.products { + match try!(CalcLengthOrPercentage::simplify_product(product)) { + SimplifiedValueNode::Sum(ref sum) => simplified.extend_from_slice(&sum.values), + val => simplified.push(val), + } + } + + if simplified.len() == 1 { + Ok(simplified[0].clone()) + } else { + Ok(SimplifiedValueNode::Sum(Box::new(SimplifiedSumNode { values: simplified }))) + } + } + + pub fn simplify_product(node: &CalcProductNode) -> Result<SimplifiedValueNode, ()> { + let mut multiplier = 1.; + let mut node_with_unit = None; + for node in &node.values { + match CalcLengthOrPercentage::simplify_value_to_number(&node) { + Some(number) => multiplier *= number, + _ if node_with_unit.is_none() => { + node_with_unit = Some(match *node { + CalcValueNode::Sum(ref sum) => + try!(CalcLengthOrPercentage::simplify_products_in_sum(sum)), + CalcValueNode::Length(l) => SimplifiedValueNode::Length(l), + CalcValueNode::Angle(a) => SimplifiedValueNode::Angle(a), + CalcValueNode::Time(t) => SimplifiedValueNode::Time(t), + CalcValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p), + _ => unreachable!("Numbers should have been handled by simplify_value_to_nubmer") + }) + }, + _ => return Err(()), + } + } + + match node_with_unit { + None => Ok(SimplifiedValueNode::Number(multiplier)), + Some(ref value) => Ok(value * multiplier) + } + } + + fn parse_length(input: &mut Parser, + context: AllowedNumericType) -> Result<Length, ()> { + CalcLengthOrPercentage::parse(input, CalcUnit::Length).map(|calc| { + Length::Calc(calc, context) + }) + } + + fn parse_length_or_percentage(input: &mut Parser) -> Result<CalcLengthOrPercentage, ()> { + CalcLengthOrPercentage::parse(input, CalcUnit::LengthOrPercentage) + } + + fn parse(input: &mut Parser, + expected_unit: CalcUnit) -> Result<CalcLengthOrPercentage, ()> { + let ast = try!(CalcLengthOrPercentage::parse_sum(input, expected_unit)); + + let mut simplified = Vec::new(); + for ref node in ast.products { + match try!(CalcLengthOrPercentage::simplify_product(node)) { + SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values), + value => simplified.push(value), + } + } + + let mut absolute = None; + let mut vw = None; + let mut vh = None; + let mut vmax = None; + let mut vmin = None; + let mut em = None; + let mut ex = None; + let mut ch = None; + let mut rem = None; + let mut percentage = None; + let mut number = None; + + for value in simplified { + match value { + SimplifiedValueNode::Percentage(p) => + percentage = Some(percentage.unwrap_or(0.) + p), + SimplifiedValueNode::Length(Length::Absolute(Au(au))) => + absolute = Some(absolute.unwrap_or(0) + au), + SimplifiedValueNode::Length(Length::ViewportPercentage(v)) => + match v { + ViewportPercentageLength::Vw(val) => + vw = Some(vw.unwrap_or(0.) + val), + ViewportPercentageLength::Vh(val) => + vh = Some(vh.unwrap_or(0.) + val), + ViewportPercentageLength::Vmin(val) => + vmin = Some(vmin.unwrap_or(0.) + val), + ViewportPercentageLength::Vmax(val) => + vmax = Some(vmax.unwrap_or(0.) + val), + }, + SimplifiedValueNode::Length(Length::FontRelative(f)) => + match f { + FontRelativeLength::Em(val) => + em = Some(em.unwrap_or(0.) + val), + FontRelativeLength::Ex(val) => + ex = Some(ex.unwrap_or(0.) + val), + FontRelativeLength::Ch(val) => + ch = Some(ch.unwrap_or(0.) + val), + FontRelativeLength::Rem(val) => + rem = Some(rem.unwrap_or(0.) + val), + }, + SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val), + _ => return Err(()), + } + } + + Ok(CalcLengthOrPercentage { + absolute: absolute.map(Au), + vw: vw.map(ViewportPercentageLength::Vw), + vh: vh.map(ViewportPercentageLength::Vh), + vmax: vmax.map(ViewportPercentageLength::Vmax), + vmin: vmin.map(ViewportPercentageLength::Vmin), + em: em.map(FontRelativeLength::Em), + ex: ex.map(FontRelativeLength::Ex), + ch: ch.map(FontRelativeLength::Ch), + rem: rem.map(FontRelativeLength::Rem), + percentage: percentage.map(Percentage), + }) + } + + pub fn parse_time(input: &mut Parser) -> Result<Time, ()> { + let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Time)); + + let mut simplified = Vec::new(); + for ref node in ast.products { + match try!(CalcLengthOrPercentage::simplify_product(node)) { + SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values), + value => simplified.push(value), + } + } + + let mut time = None; + + for value in simplified { + match value { + SimplifiedValueNode::Time(Time(val)) => + time = Some(time.unwrap_or(0.) + val), + _ => return Err(()), + } + } + + match time { + Some(time) => Ok(Time(time)), + _ => Err(()) + } + } + + pub fn parse_angle(input: &mut Parser) -> Result<Angle, ()> { + let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Angle)); + + let mut simplified = Vec::new(); + for ref node in ast.products { + match try!(CalcLengthOrPercentage::simplify_product(node)) { + SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values), + value => simplified.push(value), + } + } + + let mut angle = None; + let mut number = None; + + for value in simplified { + match value { + SimplifiedValueNode::Angle(Angle(val)) => + angle = Some(angle.unwrap_or(0.) + val), + SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val), + _ => unreachable!() + } + } + + match (angle, number) { + (Some(angle), None) => Ok(Angle(angle)), + (None, Some(value)) if value == 0. => Ok(Angle(0.)), + _ => Err(()) + } + } + + pub fn compute_from_viewport_and_font_size(&self, + viewport_size: Size2D<Au>, + font_size: Au, + root_font_size: Au) + -> computed::CalcLengthOrPercentage + { + let mut length = None; + + if let Some(absolute) = self.absolute { + length = Some(length.unwrap_or(Au(0)) + absolute); + } + + for val in &[self.vw, self.vh, self.vmin, self.vmax] { + if let Some(val) = *val { + length = Some(length.unwrap_or(Au(0)) + + val.to_computed_value(viewport_size)); + } + } + + for val in &[self.ch, self.em, self.ex, self.rem] { + if let Some(val) = *val { + length = Some(length.unwrap_or(Au(0)) + val.to_computed_value( + font_size, root_font_size)); + } + } + + computed::CalcLengthOrPercentage { + length: length, + percentage: self.percentage.map(|p| p.0), + } + } +} + +impl HasViewportPercentage for CalcLengthOrPercentage { + fn has_viewport_percentage(&self) -> bool { + self.vw.is_some() || self.vh.is_some() || + self.vmin.is_some() || self.vmax.is_some() + } +} + +impl ToCss for CalcLengthOrPercentage { + #[allow(unused_assignments)] + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + macro_rules! count { + ( $( $val:ident ),* ) => { + { + let mut count = 0; + $( + if let Some(_) = self.$val { + count += 1; + } + )* + count + } + }; + } + + macro_rules! serialize { + ( $( $val:ident ),* ) => { + { + let mut first_value = true; + $( + if let Some(val) = self.$val { + if !first_value { + try!(write!(dest, " + ")); + } else { + first_value = false; + } + try!(val.to_css(dest)); + } + )* + } + }; + } + + let count = count!(ch, em, ex, absolute, rem, vh, vmax, vmin, vw, percentage); + assert!(count > 0); + + if count > 1 { + try!(write!(dest, "calc(")); + } + + serialize!(ch, em, ex, absolute, rem, vh, vmax, vmin, vw, percentage); + + if count > 1 { + try!(write!(dest, ")")); + } + Ok(()) + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct Percentage(pub CSSFloat); // [0 .. 100%] maps to [0.0 .. 1.0] + +impl ToCss for Percentage { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + write!(dest, "{}%", self.0 * 100.) + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentage { + Length(Length), + Percentage(Percentage), + Calc(CalcLengthOrPercentage), +} + +impl HasViewportPercentage for LengthOrPercentage { + fn has_viewport_percentage(&self) -> bool { + match *self { + LengthOrPercentage::Length(ref length) => length.has_viewport_percentage(), + LengthOrPercentage::Calc(ref calc) => calc.has_viewport_percentage(), + _ => false + } + } +} + +impl ToCss for LengthOrPercentage { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentage::Length(length) => length.to_css(dest), + LengthOrPercentage::Percentage(percentage) => percentage.to_css(dest), + LengthOrPercentage::Calc(calc) => calc.to_css(dest), + } + } +} +impl LengthOrPercentage { + pub fn zero() -> LengthOrPercentage { + LengthOrPercentage::Length(Length::Absolute(Au(0))) + } + + fn parse_internal(input: &mut Parser, context: AllowedNumericType) + -> Result<LengthOrPercentage, ()> + { + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => + Length::parse_dimension(value.value, unit).map(LengthOrPercentage::Length), + Token::Percentage(ref value) if context.is_ok(value.unit_value) => + Ok(LengthOrPercentage::Percentage(Percentage(value.unit_value))), + Token::Number(ref value) if value.value == 0. => + Ok(LengthOrPercentage::Length(Length::Absolute(Au(0)))), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); + Ok(LengthOrPercentage::Calc(calc)) + }, + _ => Err(()) + } + } + + #[inline] + pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> { + LengthOrPercentage::parse_internal(input, AllowedNumericType::NonNegative) + } +} + +impl Parse for LengthOrPercentage { + #[inline] + fn parse(input: &mut Parser) -> Result<Self, ()> { + LengthOrPercentage::parse_internal(input, AllowedNumericType::All) + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentageOrAuto { + Length(Length), + Percentage(Percentage), + Auto, + Calc(CalcLengthOrPercentage), +} + +impl HasViewportPercentage for LengthOrPercentageOrAuto { + fn has_viewport_percentage(&self) -> bool { + match *self { + LengthOrPercentageOrAuto::Length(ref length) => length.has_viewport_percentage(), + LengthOrPercentageOrAuto::Calc(ref calc) => calc.has_viewport_percentage(), + _ => false + } + } +} + +impl ToCss for LengthOrPercentageOrAuto { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentageOrAuto::Length(length) => length.to_css(dest), + LengthOrPercentageOrAuto::Percentage(percentage) => percentage.to_css(dest), + LengthOrPercentageOrAuto::Auto => dest.write_str("auto"), + LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest), + } + } +} + +impl LengthOrPercentageOrAuto { + fn parse_internal(input: &mut Parser, context: AllowedNumericType) + -> Result<LengthOrPercentageOrAuto, ()> + { + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => + Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrAuto::Length), + Token::Percentage(ref value) if context.is_ok(value.unit_value) => + Ok(LengthOrPercentageOrAuto::Percentage(Percentage(value.unit_value))), + Token::Number(ref value) if value.value == 0. => + Ok(LengthOrPercentageOrAuto::Length(Length::Absolute(Au(0)))), + Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => + Ok(LengthOrPercentageOrAuto::Auto), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); + Ok(LengthOrPercentageOrAuto::Calc(calc)) + }, + _ => Err(()) + } + } + #[inline] + pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> { + LengthOrPercentageOrAuto::parse_internal(input, AllowedNumericType::All) + } + #[inline] + pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> { + LengthOrPercentageOrAuto::parse_internal(input, AllowedNumericType::NonNegative) + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentageOrNone { + Length(Length), + Percentage(Percentage), + Calc(CalcLengthOrPercentage), + None, +} + +impl HasViewportPercentage for LengthOrPercentageOrNone { + fn has_viewport_percentage(&self) -> bool { + match *self { + LengthOrPercentageOrNone::Length(ref length) => length.has_viewport_percentage(), + LengthOrPercentageOrNone::Calc(ref calc) => calc.has_viewport_percentage(), + _ => false + } + } +} + +impl ToCss for LengthOrPercentageOrNone { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentageOrNone::Length(ref length) => length.to_css(dest), + LengthOrPercentageOrNone::Percentage(ref percentage) => percentage.to_css(dest), + LengthOrPercentageOrNone::Calc(ref calc) => calc.to_css(dest), + LengthOrPercentageOrNone::None => dest.write_str("none"), + } + } +} +impl LengthOrPercentageOrNone { + fn parse_internal(input: &mut Parser, context: AllowedNumericType) + -> Result<LengthOrPercentageOrNone, ()> + { + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => + Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrNone::Length), + Token::Percentage(ref value) if context.is_ok(value.unit_value) => + Ok(LengthOrPercentageOrNone::Percentage(Percentage(value.unit_value))), + Token::Number(ref value) if value.value == 0. => + Ok(LengthOrPercentageOrNone::Length(Length::Absolute(Au(0)))), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); + Ok(LengthOrPercentageOrNone::Calc(calc)) + }, + Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => + Ok(LengthOrPercentageOrNone::None), + _ => Err(()) + } + } + #[inline] + pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> { + LengthOrPercentageOrNone::parse_internal(input, AllowedNumericType::All) + } + #[inline] + pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> { + LengthOrPercentageOrNone::parse_internal(input, AllowedNumericType::NonNegative) + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrNone { + Length(Length), + None, +} + +impl HasViewportPercentage for LengthOrNone { + fn has_viewport_percentage(&self) -> bool { + match *self { + LengthOrNone::Length(ref length) => length.has_viewport_percentage(), + _ => false + } + } +} + +impl ToCss for LengthOrNone { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrNone::Length(length) => length.to_css(dest), + LengthOrNone::None => dest.write_str("none"), + } + } +} +impl LengthOrNone { + fn parse_internal(input: &mut Parser, context: AllowedNumericType) + -> Result<LengthOrNone, ()> + { + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => + Length::parse_dimension(value.value, unit).map(LengthOrNone::Length), + Token::Number(ref value) if value.value == 0. => + Ok(LengthOrNone::Length(Length::Absolute(Au(0)))), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => + input.parse_nested_block(|input| { + CalcLengthOrPercentage::parse_length(input, context) + }).map(LengthOrNone::Length), + Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => + Ok(LengthOrNone::None), + _ => Err(()) + } + } + #[inline] + pub fn parse(input: &mut Parser) -> Result<LengthOrNone, ()> { + LengthOrNone::parse_internal(input, AllowedNumericType::All) + } + #[inline] + pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrNone, ()> { + LengthOrNone::parse_internal(input, AllowedNumericType::NonNegative) + } +} + +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentageOrAutoOrContent { + Length(Length), + Percentage(Percentage), + Calc(CalcLengthOrPercentage), + Auto, + Content +} + +impl HasViewportPercentage for LengthOrPercentageOrAutoOrContent { + fn has_viewport_percentage(&self) -> bool { + match *self { + LengthOrPercentageOrAutoOrContent::Length(length) => length.has_viewport_percentage(), + LengthOrPercentageOrAutoOrContent::Calc(ref calc) => calc.has_viewport_percentage(), + _ => false + } + } +} + +impl ToCss for LengthOrPercentageOrAutoOrContent { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentageOrAutoOrContent::Length(len) => len.to_css(dest), + LengthOrPercentageOrAutoOrContent::Percentage(perc) => perc.to_css(dest), + LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"), + LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content"), + LengthOrPercentageOrAutoOrContent::Calc(calc) => calc.to_css(dest), + } + } +} + +impl LengthOrPercentageOrAutoOrContent { + pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAutoOrContent, ()> { + let context = AllowedNumericType::NonNegative; + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => + Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrAutoOrContent::Length), + Token::Percentage(ref value) if context.is_ok(value.unit_value) => + Ok(LengthOrPercentageOrAutoOrContent::Percentage(Percentage(value.unit_value))), + Token::Number(ref value) if value.value == 0. => + Ok(LengthOrPercentageOrAutoOrContent::Length(Length::Absolute(Au(0)))), + Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => + Ok(LengthOrPercentageOrAutoOrContent::Auto), + Token::Ident(ref value) if value.eq_ignore_ascii_case("content") => + Ok(LengthOrPercentageOrAutoOrContent::Content), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); + Ok(LengthOrPercentageOrAutoOrContent::Calc(calc)) + }, + _ => Err(()) + } + } +} + diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 124acadad5a..30ca7266185 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -7,25 +7,27 @@ use cssparser::{self, Parser, ToCss, Token}; use euclid::size::Size2D; #[cfg(feature = "gecko")] use gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI}; -use parser::{Parse, ParserContext}; +use parser::ParserContext; #[cfg(feature = "gecko")] use parser::ParserContextExtraData; use std::ascii::AsciiExt; -use std::cmp; use std::f32::consts::PI; use std::fmt; use std::ops::Mul; -use style_traits::values::specified::AllowedNumericType; -use super::{CSSFloat, FONT_MEDIUM_PX, HasViewportPercentage, LocalToCss, NoViewportPercentage}; -use super::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue}; +use super::{CSSFloat, HasViewportPercentage, NoViewportPercentage}; +use super::computed::{ComputedValueAsSpecified, Context, ToComputedValue}; use url::Url; pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; pub use self::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword}; pub use self::image::{SizeKeyword, VerticalDirection}; +pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage}; +pub use self::length::{Percentage, LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; +pub use self::length::{LengthOrNone, LengthOrPercentageOrAutoOrContent, CalcUnit}; pub mod basic_shape; pub mod image; +pub mod length; pub mod position; impl NoViewportPercentage for i32 {} // For PropertyDeclaration::Order @@ -80,289 +82,9 @@ impl ToCss for CSSRGBA { } } -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum FontRelativeLength { - Em(CSSFloat), - Ex(CSSFloat), - Ch(CSSFloat), - Rem(CSSFloat) -} - -impl ToCss for FontRelativeLength { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - FontRelativeLength::Em(length) => write!(dest, "{}em", length), - FontRelativeLength::Ex(length) => write!(dest, "{}ex", length), - FontRelativeLength::Ch(length) => write!(dest, "{}ch", length), - FontRelativeLength::Rem(length) => write!(dest, "{}rem", length) - } - } -} - -impl FontRelativeLength { - pub fn to_computed_value(&self, - reference_font_size: Au, - root_font_size: Au) - -> Au - { - match *self { - FontRelativeLength::Em(length) => reference_font_size.scale_by(length), - FontRelativeLength::Ex(length) | FontRelativeLength::Ch(length) => { - // https://github.com/servo/servo/issues/7462 - let em_factor = 0.5; - reference_font_size.scale_by(length * em_factor) - }, - FontRelativeLength::Rem(length) => root_font_size.scale_by(length) - } - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum ViewportPercentageLength { - Vw(CSSFloat), - Vh(CSSFloat), - Vmin(CSSFloat), - Vmax(CSSFloat) -} - -impl HasViewportPercentage for ViewportPercentageLength { - fn has_viewport_percentage(&self) -> bool { - true - } -} - -impl ToCss for ViewportPercentageLength { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - ViewportPercentageLength::Vw(length) => write!(dest, "{}vw", length), - ViewportPercentageLength::Vh(length) => write!(dest, "{}vh", length), - ViewportPercentageLength::Vmin(length) => write!(dest, "{}vmin", length), - ViewportPercentageLength::Vmax(length) => write!(dest, "{}vmax", length) - } - } -} - -impl ViewportPercentageLength { - pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> Au { - macro_rules! to_unit { - ($viewport_dimension:expr) => { - $viewport_dimension.to_f32_px() / 100.0 - } - } - - let value = match *self { - ViewportPercentageLength::Vw(length) => - length * to_unit!(viewport_size.width), - ViewportPercentageLength::Vh(length) => - length * to_unit!(viewport_size.height), - ViewportPercentageLength::Vmin(length) => - length * to_unit!(cmp::min(viewport_size.width, viewport_size.height)), - ViewportPercentageLength::Vmax(length) => - length * to_unit!(cmp::max(viewport_size.width, viewport_size.height)), - }; - Au::from_f32_px(value) - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct CharacterWidth(pub i32); - -impl CharacterWidth { - pub fn to_computed_value(&self, reference_font_size: Au) -> Au { - // 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.scale_by(0.5); - let max_advance = reference_font_size; - average_advance.scale_by(self.0 as CSSFloat - 1.0) + max_advance - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum Length { - Absolute(Au), // application units - FontRelative(FontRelativeLength), - ViewportPercentage(ViewportPercentageLength), - - /// 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), - - Calc(CalcLengthOrPercentage, AllowedNumericType), -} - -impl HasViewportPercentage for Length { - fn has_viewport_percentage(&self) -> bool { - match *self { - Length::ViewportPercentage(_) => true, - Length::Calc(ref calc, _) => calc.has_viewport_percentage(), - _ => false - } - } -} - -impl ToCss for Length { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - Length::Absolute(length) => write!(dest, "{}px", length.to_f32_px()), - Length::FontRelative(length) => length.to_css(dest), - Length::ViewportPercentage(length) => length.to_css(dest), - Length::Calc(ref calc, _) => calc.to_css(dest), - /* This should only be reached from style dumping code */ - Length::ServoCharacterWidth(CharacterWidth(i)) => write!(dest, "CharWidth({})", i), - } - } -} - -impl Mul<CSSFloat> for Length { - type Output = Length; - - #[inline] - fn mul(self, scalar: CSSFloat) -> Length { - match self { - Length::Absolute(Au(v)) => Length::Absolute(Au(((v as f32) * scalar) as i32)), - Length::FontRelative(v) => Length::FontRelative(v * scalar), - Length::ViewportPercentage(v) => Length::ViewportPercentage(v * scalar), - Length::Calc(..) => panic!("Can't multiply Calc!"), - Length::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"), - } - } -} - -impl Mul<CSSFloat> for FontRelativeLength { - type Output = FontRelativeLength; - - #[inline] - fn mul(self, scalar: CSSFloat) -> FontRelativeLength { - match self { - FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar), - FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar), - FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar), - FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar), - } - } -} - -impl Mul<CSSFloat> for ViewportPercentageLength { - type Output = ViewportPercentageLength; - - #[inline] - fn mul(self, scalar: CSSFloat) -> ViewportPercentageLength { - match self { - ViewportPercentageLength::Vw(v) => ViewportPercentageLength::Vw(v * scalar), - ViewportPercentageLength::Vh(v) => ViewportPercentageLength::Vh(v * scalar), - ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar), - ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar), - } - } -} - -const AU_PER_PX: CSSFloat = 60.; -const AU_PER_IN: CSSFloat = AU_PER_PX * 96.; -const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54; -const AU_PER_MM: CSSFloat = AU_PER_IN / 25.4; -const AU_PER_Q: CSSFloat = AU_PER_MM / 4.; -const AU_PER_PT: CSSFloat = AU_PER_IN / 72.; -const AU_PER_PC: CSSFloat = AU_PER_PT * 12.; -impl Length { - // https://drafts.csswg.org/css-fonts-3/#font-size-prop - pub fn from_str(s: &str) -> Option<Length> { - Some(match_ignore_ascii_case! { s, - "xx-small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 5), - "x-small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 4), - "small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 8 / 9), - "medium" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX)), - "large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 6 / 5), - "x-large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 2), - "xx-large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 2), - - // https://github.com/servo/servo/issues/3423#issuecomment-56321664 - "smaller" => Length::FontRelative(FontRelativeLength::Em(0.85)), - "larger" => Length::FontRelative(FontRelativeLength::Em(1.2)), - _ => return None - }) - } - - #[inline] - fn parse_internal(input: &mut Parser, context: AllowedNumericType) -> Result<Length, ()> { - match try!(input.next()) { - Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => - Length::parse_dimension(value.value, unit), - Token::Number(ref value) if value.value == 0. => - Ok(Length::Absolute(Au(0))), - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => - input.parse_nested_block(|input| { - CalcLengthOrPercentage::parse_length(input, context) - }), - _ => Err(()) - } - } - pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> { - Length::parse_internal(input, AllowedNumericType::NonNegative) - } - pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> { - match_ignore_ascii_case! { unit, - "px" => Ok(Length::from_px(value)), - "in" => Ok(Length::Absolute(Au((value * AU_PER_IN) as i32))), - "cm" => Ok(Length::Absolute(Au((value * AU_PER_CM) as i32))), - "mm" => Ok(Length::Absolute(Au((value * AU_PER_MM) as i32))), - "q" => Ok(Length::Absolute(Au((value * AU_PER_Q) as i32))), - "pt" => Ok(Length::Absolute(Au((value * AU_PER_PT) as i32))), - "pc" => Ok(Length::Absolute(Au((value * AU_PER_PC) as i32))), - // font-relative - "em" => Ok(Length::FontRelative(FontRelativeLength::Em(value))), - "ex" => Ok(Length::FontRelative(FontRelativeLength::Ex(value))), - "ch" => Ok(Length::FontRelative(FontRelativeLength::Ch(value))), - "rem" => Ok(Length::FontRelative(FontRelativeLength::Rem(value))), - // viewport percentages - "vw" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vw(value))), - "vh" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vh(value))), - "vmin" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmin(value))), - "vmax" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmax(value))), - _ => Err(()) - } - } - #[inline] - pub fn from_px(px_value: CSSFloat) -> Length { - Length::Absolute(Au((px_value * AU_PER_PX) as i32)) - } -} - -impl Parse for Length { - fn parse(input: &mut Parser) -> Result<Self, ()> { - Length::parse_internal(input, AllowedNumericType::All) - } -} #[derive(Clone, Debug)] -struct CalcSumNode { - products: Vec<CalcProductNode>, -} - -#[derive(Clone, Debug)] -struct CalcProductNode { - values: Vec<CalcValueNode> -} - -#[derive(Clone, Debug)] -enum CalcValueNode { - Length(Length), - Angle(Angle), - Time(Time), - Percentage(CSSFloat), - Number(CSSFloat), - Sum(Box<CalcSumNode>), -} - -#[derive(Clone, Debug)] -struct SimplifiedSumNode { +pub struct SimplifiedSumNode { values: Vec<SimplifiedValueNode>, } impl<'a> Mul<CSSFloat> for &'a SimplifiedSumNode { @@ -377,7 +99,7 @@ impl<'a> Mul<CSSFloat> for &'a SimplifiedSumNode { } #[derive(Clone, Debug)] -enum SimplifiedValueNode { +pub enum SimplifiedValueNode { Length(Length), Angle(Angle), Time(Time), @@ -454,708 +176,6 @@ pub fn parse_number(input: &mut Parser) -> Result<f32, ()> { } } -#[derive(Clone, Copy, PartialEq)] -enum CalcUnit { - Number, - Integer, - Length, - LengthOrPercentage, - Angle, - Time, -} - -#[derive(Clone, PartialEq, Copy, Debug, Default)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct CalcLengthOrPercentage { - pub absolute: Option<Au>, - pub vw: Option<ViewportPercentageLength>, - pub vh: Option<ViewportPercentageLength>, - pub vmin: Option<ViewportPercentageLength>, - pub vmax: Option<ViewportPercentageLength>, - pub em: Option<FontRelativeLength>, - pub ex: Option<FontRelativeLength>, - pub ch: Option<FontRelativeLength>, - pub rem: Option<FontRelativeLength>, - pub percentage: Option<Percentage>, -} - -impl CalcLengthOrPercentage { - fn parse_sum(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcSumNode, ()> { - let mut products = Vec::new(); - products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit))); - - while let Ok(token) = input.next() { - match token { - Token::Delim('+') => { - products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit))); - } - Token::Delim('-') => { - let mut right = try!(CalcLengthOrPercentage::parse_product(input, expected_unit)); - right.values.push(CalcValueNode::Number(-1.)); - products.push(right); - } - _ => return Err(()) - } - } - - Ok(CalcSumNode { products: products }) - } - - fn parse_product(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcProductNode, ()> { - let mut values = Vec::new(); - values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit))); - - loop { - let position = input.position(); - match input.next() { - Ok(Token::Delim('*')) => { - values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit))); - } - Ok(Token::Delim('/')) if expected_unit != CalcUnit::Integer => { - if let Ok(Token::Number(ref value)) = input.next() { - if value.value == 0. { - return Err(()); - } - values.push(CalcValueNode::Number(1. / value.value)); - } else { - return Err(()); - } - } - _ => { - input.reset(position); - break - } - } - } - - Ok(CalcProductNode { values: values }) - } - - fn parse_value(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcValueNode, ()> { - match (try!(input.next()), expected_unit) { - (Token::Number(ref value), _) => Ok(CalcValueNode::Number(value.value)), - (Token::Dimension(ref value, ref unit), CalcUnit::Length) | - (Token::Dimension(ref value, ref unit), CalcUnit::LengthOrPercentage) => { - Length::parse_dimension(value.value, unit).map(CalcValueNode::Length) - } - (Token::Dimension(ref value, ref unit), CalcUnit::Angle) => { - Angle::parse_dimension(value.value, unit).map(CalcValueNode::Angle) - } - (Token::Dimension(ref value, ref unit), CalcUnit::Time) => { - Time::parse_dimension(value.value, unit).map(CalcValueNode::Time) - } - (Token::Percentage(ref value), CalcUnit::LengthOrPercentage) => - Ok(CalcValueNode::Percentage(value.unit_value)), - (Token::ParenthesisBlock, _) => { - input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, expected_unit)) - .map(|result| CalcValueNode::Sum(Box::new(result))) - }, - _ => Err(()) - } - } - - fn simplify_value_to_number(node: &CalcValueNode) -> Option<CSSFloat> { - match *node { - CalcValueNode::Number(number) => Some(number), - CalcValueNode::Sum(ref sum) => CalcLengthOrPercentage::simplify_sum_to_number(sum), - _ => None - } - } - - fn simplify_sum_to_number(node: &CalcSumNode) -> Option<CSSFloat> { - let mut sum = 0.; - for ref product in &node.products { - match CalcLengthOrPercentage::simplify_product_to_number(product) { - Some(number) => sum += number, - _ => return None - } - } - Some(sum) - } - - fn simplify_product_to_number(node: &CalcProductNode) -> Option<CSSFloat> { - let mut product = 1.; - for ref value in &node.values { - match CalcLengthOrPercentage::simplify_value_to_number(value) { - Some(number) => product *= number, - _ => return None - } - } - Some(product) - } - - fn simplify_products_in_sum(node: &CalcSumNode) -> Result<SimplifiedValueNode, ()> { - let mut simplified = Vec::new(); - for product in &node.products { - match try!(CalcLengthOrPercentage::simplify_product(product)) { - SimplifiedValueNode::Sum(ref sum) => simplified.extend_from_slice(&sum.values), - val => simplified.push(val), - } - } - - if simplified.len() == 1 { - Ok(simplified[0].clone()) - } else { - Ok(SimplifiedValueNode::Sum(Box::new(SimplifiedSumNode { values: simplified }))) - } - } - - fn simplify_product(node: &CalcProductNode) -> Result<SimplifiedValueNode, ()> { - let mut multiplier = 1.; - let mut node_with_unit = None; - for node in &node.values { - match CalcLengthOrPercentage::simplify_value_to_number(&node) { - Some(number) => multiplier *= number, - _ if node_with_unit.is_none() => { - node_with_unit = Some(match *node { - CalcValueNode::Sum(ref sum) => - try!(CalcLengthOrPercentage::simplify_products_in_sum(sum)), - CalcValueNode::Length(l) => SimplifiedValueNode::Length(l), - CalcValueNode::Angle(a) => SimplifiedValueNode::Angle(a), - CalcValueNode::Time(t) => SimplifiedValueNode::Time(t), - CalcValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p), - _ => unreachable!("Numbers should have been handled by simplify_value_to_nubmer") - }) - }, - _ => return Err(()), - } - } - - match node_with_unit { - None => Ok(SimplifiedValueNode::Number(multiplier)), - Some(ref value) => Ok(value * multiplier) - } - } - - fn parse_length(input: &mut Parser, - context: AllowedNumericType) -> Result<Length, ()> { - CalcLengthOrPercentage::parse(input, CalcUnit::Length).map(|calc| { - Length::Calc(calc, context) - }) - } - - fn parse_length_or_percentage(input: &mut Parser) -> Result<CalcLengthOrPercentage, ()> { - CalcLengthOrPercentage::parse(input, CalcUnit::LengthOrPercentage) - } - - fn parse(input: &mut Parser, - expected_unit: CalcUnit) -> Result<CalcLengthOrPercentage, ()> { - let ast = try!(CalcLengthOrPercentage::parse_sum(input, expected_unit)); - - let mut simplified = Vec::new(); - for ref node in ast.products { - match try!(CalcLengthOrPercentage::simplify_product(node)) { - SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values), - value => simplified.push(value), - } - } - - let mut absolute = None; - let mut vw = None; - let mut vh = None; - let mut vmax = None; - let mut vmin = None; - let mut em = None; - let mut ex = None; - let mut ch = None; - let mut rem = None; - let mut percentage = None; - let mut number = None; - - for value in simplified { - match value { - SimplifiedValueNode::Percentage(p) => - percentage = Some(percentage.unwrap_or(0.) + p), - SimplifiedValueNode::Length(Length::Absolute(Au(au))) => - absolute = Some(absolute.unwrap_or(0) + au), - SimplifiedValueNode::Length(Length::ViewportPercentage(v)) => - match v { - ViewportPercentageLength::Vw(val) => - vw = Some(vw.unwrap_or(0.) + val), - ViewportPercentageLength::Vh(val) => - vh = Some(vh.unwrap_or(0.) + val), - ViewportPercentageLength::Vmin(val) => - vmin = Some(vmin.unwrap_or(0.) + val), - ViewportPercentageLength::Vmax(val) => - vmax = Some(vmax.unwrap_or(0.) + val), - }, - SimplifiedValueNode::Length(Length::FontRelative(f)) => - match f { - FontRelativeLength::Em(val) => - em = Some(em.unwrap_or(0.) + val), - FontRelativeLength::Ex(val) => - ex = Some(ex.unwrap_or(0.) + val), - FontRelativeLength::Ch(val) => - ch = Some(ch.unwrap_or(0.) + val), - FontRelativeLength::Rem(val) => - rem = Some(rem.unwrap_or(0.) + val), - }, - SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val), - _ => return Err(()), - } - } - - Ok(CalcLengthOrPercentage { - absolute: absolute.map(Au), - vw: vw.map(ViewportPercentageLength::Vw), - vh: vh.map(ViewportPercentageLength::Vh), - vmax: vmax.map(ViewportPercentageLength::Vmax), - vmin: vmin.map(ViewportPercentageLength::Vmin), - em: em.map(FontRelativeLength::Em), - ex: ex.map(FontRelativeLength::Ex), - ch: ch.map(FontRelativeLength::Ch), - rem: rem.map(FontRelativeLength::Rem), - percentage: percentage.map(Percentage), - }) - } - - pub fn parse_time(input: &mut Parser) -> Result<Time, ()> { - let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Time)); - - let mut simplified = Vec::new(); - for ref node in ast.products { - match try!(CalcLengthOrPercentage::simplify_product(node)) { - SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values), - value => simplified.push(value), - } - } - - let mut time = None; - - for value in simplified { - match value { - SimplifiedValueNode::Time(Time(val)) => - time = Some(time.unwrap_or(0.) + val), - _ => return Err(()), - } - } - - match time { - Some(time) => Ok(Time(time)), - _ => Err(()) - } - } - - pub fn parse_angle(input: &mut Parser) -> Result<Angle, ()> { - let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Angle)); - - let mut simplified = Vec::new(); - for ref node in ast.products { - match try!(CalcLengthOrPercentage::simplify_product(node)) { - SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values), - value => simplified.push(value), - } - } - - let mut angle = None; - let mut number = None; - - for value in simplified { - match value { - SimplifiedValueNode::Angle(Angle(val)) => - angle = Some(angle.unwrap_or(0.) + val), - SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val), - _ => unreachable!() - } - } - - match (angle, number) { - (Some(angle), None) => Ok(Angle(angle)), - (None, Some(value)) if value == 0. => Ok(Angle(0.)), - _ => Err(()) - } - } - - pub fn compute_from_viewport_and_font_size(&self, - viewport_size: Size2D<Au>, - font_size: Au, - root_font_size: Au) - -> computed::CalcLengthOrPercentage - { - let mut length = None; - - if let Some(absolute) = self.absolute { - length = Some(length.unwrap_or(Au(0)) + absolute); - } - - for val in &[self.vw, self.vh, self.vmin, self.vmax] { - if let Some(val) = *val { - length = Some(length.unwrap_or(Au(0)) + - val.to_computed_value(viewport_size)); - } - } - - for val in &[self.ch, self.em, self.ex, self.rem] { - if let Some(val) = *val { - length = Some(length.unwrap_or(Au(0)) + val.to_computed_value( - font_size, root_font_size)); - } - } - - computed::CalcLengthOrPercentage { - length: length, - percentage: self.percentage.map(|p| p.0), - } - } -} - -impl HasViewportPercentage for CalcLengthOrPercentage { - fn has_viewport_percentage(&self) -> bool { - self.vw.is_some() || self.vh.is_some() || - self.vmin.is_some() || self.vmax.is_some() - } -} - -impl ToCss for CalcLengthOrPercentage { - #[allow(unused_assignments)] - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - macro_rules! count { - ( $( $val:ident ),* ) => { - { - let mut count = 0; - $( - if let Some(_) = self.$val { - count += 1; - } - )* - count - } - }; - } - - macro_rules! serialize { - ( $( $val:ident ),* ) => { - { - let mut first_value = true; - $( - if let Some(val) = self.$val { - if !first_value { - try!(write!(dest, " + ")); - } else { - first_value = false; - } - try!(val.to_css(dest)); - } - )* - } - }; - } - - let count = count!(ch, em, ex, absolute, rem, vh, vmax, vmin, vw, percentage); - assert!(count > 0); - - if count > 1 { - try!(write!(dest, "calc(")); - } - - serialize!(ch, em, ex, absolute, rem, vh, vmax, vmin, vw, percentage); - - if count > 1 { - try!(write!(dest, ")")); - } - Ok(()) - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct Percentage(pub CSSFloat); // [0 .. 100%] maps to [0.0 .. 1.0] - -impl ToCss for Percentage { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - write!(dest, "{}%", self.0 * 100.) - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrPercentage { - Length(Length), - Percentage(Percentage), - Calc(CalcLengthOrPercentage), -} - -impl HasViewportPercentage for LengthOrPercentage { - fn has_viewport_percentage(&self) -> bool { - match *self { - LengthOrPercentage::Length(ref length) => length.has_viewport_percentage(), - LengthOrPercentage::Calc(ref calc) => calc.has_viewport_percentage(), - _ => false - } - } -} - -impl ToCss for LengthOrPercentage { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentage::Length(length) => length.to_css(dest), - LengthOrPercentage::Percentage(percentage) => percentage.to_css(dest), - LengthOrPercentage::Calc(calc) => calc.to_css(dest), - } - } -} -impl LengthOrPercentage { - pub fn zero() -> LengthOrPercentage { - LengthOrPercentage::Length(Length::Absolute(Au(0))) - } - - fn parse_internal(input: &mut Parser, context: AllowedNumericType) - -> Result<LengthOrPercentage, ()> - { - match try!(input.next()) { - Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => - Length::parse_dimension(value.value, unit).map(LengthOrPercentage::Length), - Token::Percentage(ref value) if context.is_ok(value.unit_value) => - Ok(LengthOrPercentage::Percentage(Percentage(value.unit_value))), - Token::Number(ref value) if value.value == 0. => - Ok(LengthOrPercentage::Length(Length::Absolute(Au(0)))), - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { - let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); - Ok(LengthOrPercentage::Calc(calc)) - }, - _ => Err(()) - } - } - - #[inline] - pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> { - LengthOrPercentage::parse_internal(input, AllowedNumericType::NonNegative) - } -} - -impl Parse for LengthOrPercentage { - #[inline] - fn parse(input: &mut Parser) -> Result<Self, ()> { - LengthOrPercentage::parse_internal(input, AllowedNumericType::All) - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrPercentageOrAuto { - Length(Length), - Percentage(Percentage), - Auto, - Calc(CalcLengthOrPercentage), -} - -impl HasViewportPercentage for LengthOrPercentageOrAuto { - fn has_viewport_percentage(&self) -> bool { - match *self { - LengthOrPercentageOrAuto::Length(ref length) => length.has_viewport_percentage(), - LengthOrPercentageOrAuto::Calc(ref calc) => calc.has_viewport_percentage(), - _ => false - } - } -} - -impl ToCss for LengthOrPercentageOrAuto { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentageOrAuto::Length(length) => length.to_css(dest), - LengthOrPercentageOrAuto::Percentage(percentage) => percentage.to_css(dest), - LengthOrPercentageOrAuto::Auto => dest.write_str("auto"), - LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest), - } - } -} - -impl LengthOrPercentageOrAuto { - fn parse_internal(input: &mut Parser, context: AllowedNumericType) - -> Result<LengthOrPercentageOrAuto, ()> - { - match try!(input.next()) { - Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => - Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrAuto::Length), - Token::Percentage(ref value) if context.is_ok(value.unit_value) => - Ok(LengthOrPercentageOrAuto::Percentage(Percentage(value.unit_value))), - Token::Number(ref value) if value.value == 0. => - Ok(LengthOrPercentageOrAuto::Length(Length::Absolute(Au(0)))), - Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => - Ok(LengthOrPercentageOrAuto::Auto), - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { - let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); - Ok(LengthOrPercentageOrAuto::Calc(calc)) - }, - _ => Err(()) - } - } - #[inline] - pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> { - LengthOrPercentageOrAuto::parse_internal(input, AllowedNumericType::All) - } - #[inline] - pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> { - LengthOrPercentageOrAuto::parse_internal(input, AllowedNumericType::NonNegative) - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrPercentageOrNone { - Length(Length), - Percentage(Percentage), - Calc(CalcLengthOrPercentage), - None, -} - -impl HasViewportPercentage for LengthOrPercentageOrNone { - fn has_viewport_percentage(&self) -> bool { - match *self { - LengthOrPercentageOrNone::Length(ref length) => length.has_viewport_percentage(), - LengthOrPercentageOrNone::Calc(ref calc) => calc.has_viewport_percentage(), - _ => false - } - } -} - -impl ToCss for LengthOrPercentageOrNone { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentageOrNone::Length(ref length) => length.to_css(dest), - LengthOrPercentageOrNone::Percentage(ref percentage) => percentage.to_css(dest), - LengthOrPercentageOrNone::Calc(ref calc) => calc.to_css(dest), - LengthOrPercentageOrNone::None => dest.write_str("none"), - } - } -} -impl LengthOrPercentageOrNone { - fn parse_internal(input: &mut Parser, context: AllowedNumericType) - -> Result<LengthOrPercentageOrNone, ()> - { - match try!(input.next()) { - Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => - Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrNone::Length), - Token::Percentage(ref value) if context.is_ok(value.unit_value) => - Ok(LengthOrPercentageOrNone::Percentage(Percentage(value.unit_value))), - Token::Number(ref value) if value.value == 0. => - Ok(LengthOrPercentageOrNone::Length(Length::Absolute(Au(0)))), - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { - let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); - Ok(LengthOrPercentageOrNone::Calc(calc)) - }, - Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => - Ok(LengthOrPercentageOrNone::None), - _ => Err(()) - } - } - #[inline] - pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> { - LengthOrPercentageOrNone::parse_internal(input, AllowedNumericType::All) - } - #[inline] - pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> { - LengthOrPercentageOrNone::parse_internal(input, AllowedNumericType::NonNegative) - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrNone { - Length(Length), - None, -} - -impl HasViewportPercentage for LengthOrNone { - fn has_viewport_percentage(&self) -> bool { - match *self { - LengthOrNone::Length(ref length) => length.has_viewport_percentage(), - _ => false - } - } -} - -impl ToCss for LengthOrNone { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrNone::Length(length) => length.to_css(dest), - LengthOrNone::None => dest.write_str("none"), - } - } -} -impl LengthOrNone { - fn parse_internal(input: &mut Parser, context: AllowedNumericType) - -> Result<LengthOrNone, ()> - { - match try!(input.next()) { - Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => - Length::parse_dimension(value.value, unit).map(LengthOrNone::Length), - Token::Number(ref value) if value.value == 0. => - Ok(LengthOrNone::Length(Length::Absolute(Au(0)))), - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => - input.parse_nested_block(|input| { - CalcLengthOrPercentage::parse_length(input, context) - }).map(LengthOrNone::Length), - Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => - Ok(LengthOrNone::None), - _ => Err(()) - } - } - #[inline] - pub fn parse(input: &mut Parser) -> Result<LengthOrNone, ()> { - LengthOrNone::parse_internal(input, AllowedNumericType::All) - } - #[inline] - pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrNone, ()> { - LengthOrNone::parse_internal(input, AllowedNumericType::NonNegative) - } -} - -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum LengthOrPercentageOrAutoOrContent { - Length(Length), - Percentage(Percentage), - Calc(CalcLengthOrPercentage), - Auto, - Content -} - -impl HasViewportPercentage for LengthOrPercentageOrAutoOrContent { - fn has_viewport_percentage(&self) -> bool { - match *self { - LengthOrPercentageOrAutoOrContent::Length(length) => length.has_viewport_percentage(), - LengthOrPercentageOrAutoOrContent::Calc(ref calc) => calc.has_viewport_percentage(), - _ => false - } - } -} - -impl ToCss for LengthOrPercentageOrAutoOrContent { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - LengthOrPercentageOrAutoOrContent::Length(len) => len.to_css(dest), - LengthOrPercentageOrAutoOrContent::Percentage(perc) => perc.to_css(dest), - LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"), - LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content"), - LengthOrPercentageOrAutoOrContent::Calc(calc) => calc.to_css(dest), - } - } -} - -impl LengthOrPercentageOrAutoOrContent { - pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAutoOrContent, ()> { - let context = AllowedNumericType::NonNegative; - match try!(input.next()) { - Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => - Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrAutoOrContent::Length), - Token::Percentage(ref value) if context.is_ok(value.unit_value) => - Ok(LengthOrPercentageOrAutoOrContent::Percentage(Percentage(value.unit_value))), - Token::Number(ref value) if value.value == 0. => - Ok(LengthOrPercentageOrAutoOrContent::Length(Length::Absolute(Au(0)))), - Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => - Ok(LengthOrPercentageOrAutoOrContent::Auto), - Token::Ident(ref value) if value.eq_ignore_ascii_case("content") => - Ok(LengthOrPercentageOrAutoOrContent::Content), - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { - let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); - Ok(LengthOrPercentageOrAutoOrContent::Calc(calc)) - }, - _ => Err(()) - } - } -} - #[derive(Clone, PartialEq, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>); |