diff options
author | David Zbarsky <dzbarsky@gmail.com> | 2015-08-20 12:31:19 -0400 |
---|---|---|
committer | David Zbarsky <dzbarsky@gmail.com> | 2015-11-01 23:16:14 -0800 |
commit | 00980ea595dd54643eee59c1a0e2ddef39286e7f (patch) | |
tree | 75e43edd9ead2fc0e9c484ae57c9bef8bccf2fca | |
parent | 35b452660bf0759d222e8f2ac4b8c57f75529443 (diff) | |
download | servo-00980ea595dd54643eee59c1a0e2ddef39286e7f.tar.gz servo-00980ea595dd54643eee59c1a0e2ddef39286e7f.zip |
Implement calc expressions for more value types
-rw-r--r-- | components/layout/block.rs | 4 | ||||
-rw-r--r-- | components/layout/inline.rs | 7 | ||||
-rw-r--r-- | components/layout/model.rs | 2 | ||||
-rw-r--r-- | components/script/dom/element.rs | 5 | ||||
-rw-r--r-- | components/style/animation.rs | 24 | ||||
-rw-r--r-- | components/style/properties.mako.rs | 157 | ||||
-rw-r--r-- | components/style/values.rs | 346 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/mozilla/calc.html.ini | 110 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/calc.html | 40 |
9 files changed, 383 insertions, 312 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index adb48e3248f..399b3e5a9f4 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -340,6 +340,10 @@ impl CandidateBSizeIterator { (LengthOrPercentageOrNone::Percentage(percent), Some(block_container_block_size)) => { Some(block_container_block_size.scale_by(percent)) } + (LengthOrPercentageOrNone::Calc(calc), Some(block_container_block_size)) => { + Some(block_container_block_size.scale_by(calc.percentage()) + calc.length()) + } + (LengthOrPercentageOrNone::Calc(_), _) | (LengthOrPercentageOrNone::Percentage(_), None) | (LengthOrPercentageOrNone::None, _) => None, (LengthOrPercentageOrNone::Length(length), _) => Some(length), diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 5bd622871e1..8711881ffaf 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -27,6 +27,7 @@ use std::{fmt, isize, mem}; use style::computed_values::{display, overflow_x, position, text_align, text_justify}; use style::computed_values::{text_overflow, vertical_align, white_space}; use style::properties::ComputedValues; +use style::values::computed::LengthOrPercentage; use text; use unicode_bidi; use util; @@ -953,15 +954,15 @@ impl InlineFlow { offset_from_baseline = offset_from_baseline - *depth_below_baseline } }, - vertical_align::T::Length(length) => { + vertical_align::T::LengthOrPercentage(LengthOrPercentage::Length(length)) => { offset_from_baseline = offset_from_baseline - length } - vertical_align::T::Percentage(p) => { + vertical_align::T::LengthOrPercentage(LengthOrPercentage::Percentage(p)) => { let line_height = fragment.calculate_line_height(layout_context); let percent_offset = line_height.scale_by(p); offset_from_baseline = offset_from_baseline - percent_offset } - vertical_align::T::Calc(calc) => { + vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(calc)) => { let line_height = fragment.calculate_line_height(layout_context); let percent_offset = line_height.scale_by(calc.percentage()); offset_from_baseline = offset_from_baseline - percent_offset - calc.length() diff --git a/components/layout/model.rs b/components/layout/model.rs index 8ee98a39678..f49ac51ad21 100644 --- a/components/layout/model.rs +++ b/components/layout/model.rs @@ -411,6 +411,8 @@ pub fn specified_or_none(length: LengthOrPercentageOrNone, containing_length: Au match length { LengthOrPercentageOrNone::None => None, LengthOrPercentageOrNone::Percentage(percent) => Some(containing_length.scale_by(percent)), + LengthOrPercentageOrNone::Calc(calc) => + Some(containing_length.scale_by(calc.percentage()) + calc.length()), LengthOrPercentageOrNone::Length(length) => Some(length), } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 4fc714ed1fe..d07d2566371 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -80,7 +80,7 @@ use style::properties::DeclaredValue; use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size}; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute}; use style::values::CSSFloat; -use style::values::specified::{self, CSSColor, CSSRGBA}; +use style::values::specified::{self, CSSColor, CSSRGBA, LengthOrPercentage}; use url::UrlParser; use util::mem::HeapSizeOf; use util::str::{DOMString, LengthOrPercentageOrAuto}; @@ -347,7 +347,8 @@ impl LayoutElementHelpers for LayoutJS<Element> { hints.push(from_declaration( PropertyDeclaration::FontSize( DeclaredValue::Value( - font_size::SpecifiedValue(font_size))))) + font_size::SpecifiedValue( + LengthOrPercentage::Length(font_size)))))) } let cellspacing = if let Some(this) = self.downcast::<HTMLTableElement>() { diff --git a/components/style/animation.rs b/components/style/animation.rs index 30c0b658fec..a073c39ae73 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -28,7 +28,7 @@ use std::iter::repeat; use util::bezier::Bezier; use values::CSSFloat; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; -use values::computed::{Calc, Length, LengthOrPercentage, Time}; +use values::computed::{CalcLengthOrPercentage, Length, LengthOrPercentage, Time}; #[derive(Clone, Debug)] pub struct PropertyAnimation { @@ -461,10 +461,10 @@ impl Interpolate for VerticalAlign { fn interpolate(&self, other: &VerticalAlign, time: f64) -> Option<VerticalAlign> { match (*self, *other) { - (VerticalAlign::Length(ref this), - VerticalAlign::Length(ref other)) => { + (VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)), + VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => { this.interpolate(other, time).and_then(|value| { - Some(VerticalAlign::Length(value)) + Some(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value))) }) } (_, _) => None, @@ -513,11 +513,11 @@ impl Interpolate for Color { } } -impl Interpolate for Calc { +impl Interpolate for CalcLengthOrPercentage { #[inline] - fn interpolate(&self, other: &Calc, time: f64) - -> Option<Calc> { - Some(Calc { + fn interpolate(&self, other: &CalcLengthOrPercentage, time: f64) + -> Option<CalcLengthOrPercentage> { + Some(CalcLengthOrPercentage { length: self.length().interpolate(&other.length(), time), percentage: self.percentage().interpolate(&other.percentage(), time), }) @@ -542,8 +542,8 @@ impl Interpolate for LengthOrPercentage { }) } (this, other) => { - let this: Calc = From::from(this); - let other: Calc = From::from(other); + let this: CalcLengthOrPercentage = From::from(this); + let other: CalcLengthOrPercentage = From::from(other); this.interpolate(&other, time).and_then(|value| { Some(LengthOrPercentage::Calc(value)) }) @@ -573,8 +573,8 @@ impl Interpolate for LengthOrPercentageOrAuto { Some(LengthOrPercentageOrAuto::Auto) } (this, other) => { - let this: Option<Calc> = From::from(this); - let other: Option<Calc> = From::from(other); + let this: Option<CalcLengthOrPercentage> = From::from(this); + let other: Option<CalcLengthOrPercentage> = From::from(other); this.interpolate(&other, time).unwrap_or(None).and_then(|value| { Some(LengthOrPercentageOrAuto::Calc(value)) }) diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index c8f1834b1ac..2912d4ea871 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -611,7 +611,7 @@ pub mod longhands { if input.try(|input| input.expect_ident_matching("auto")).is_ok() { Ok(computed_value::T::Auto) } else { - Ok(computed_value::T::Number(try!(input.expect_integer()) as i32)) + specified::parse_integer(input).map(computed_value::T::Number) } } </%self:longhand> @@ -658,18 +658,16 @@ pub mod longhands { #[derive(Clone, PartialEq, Copy)] pub enum SpecifiedValue { Normal, - Length(specified::Length), Number(CSSFloat), - Percentage(CSSFloat), + LengthOrPercentage(specified::LengthOrPercentage), } impl ToCss for SpecifiedValue { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { SpecifiedValue::Normal => dest.write_str("normal"), - SpecifiedValue::Length(length) => length.to_css(dest), + SpecifiedValue::LengthOrPercentage(value) => value.to_css(dest), SpecifiedValue::Number(number) => write!(dest, "{}", number), - SpecifiedValue::Percentage(number) => write!(dest, "{}%", number * 100.), } } } @@ -677,22 +675,19 @@ pub mod longhands { pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { use cssparser::Token; use std::ascii::AsciiExt; - match try!(input.next()) { - Token::Number(ref value) if value.value >= 0. => { - Ok(SpecifiedValue::Number(value.value)) - } - Token::Percentage(ref value) if value.unit_value >= 0. => { - Ok(SpecifiedValue::Percentage(value.unit_value)) - } - Token::Dimension(ref value, ref unit) if value.value >= 0. => { - specified::Length::parse_dimension(value.value, unit) - .map(SpecifiedValue::Length) - } - Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => { - Ok(SpecifiedValue::Normal) + input.try(specified::LengthOrPercentage::parse_non_negative) + .map(SpecifiedValue::LengthOrPercentage) + .or_else(|()| { + match try!(input.next()) { + Token::Number(ref value) if value.value >= 0. => { + Ok(SpecifiedValue::Number(value.value)) + } + Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => { + Ok(SpecifiedValue::Normal) + } + _ => Err(()), } - _ => Err(()), - } + }) } pub mod computed_value { use app_units::Au; @@ -733,13 +728,22 @@ pub mod longhands { fn to_computed_value(&self, context: &Context) -> computed_value::T { match *self { SpecifiedValue::Normal => computed_value::T::Normal, - SpecifiedValue::Length(value) => { - computed_value::T::Length(value.to_computed_value(context)) - } SpecifiedValue::Number(value) => computed_value::T::Number(value), - SpecifiedValue::Percentage(value) => { - let fr = specified::Length::FontRelative(specified::FontRelativeLength::Em(value)); - computed_value::T::Length(fr.to_computed_value(context)) + SpecifiedValue::LengthOrPercentage(value) => { + match value { + specified::LengthOrPercentage::Length(value) => + computed_value::T::Length(value.to_computed_value(context)), + specified::LengthOrPercentage::Percentage(specified::Percentage(value)) => { + let fr = specified::Length::FontRelative(specified::FontRelativeLength::Em(value)); + computed_value::T::Length(fr.to_computed_value(context)) + }, + specified::LengthOrPercentage::Calc(calc) => { + let calc = calc.to_computed_value(context); + let fr = specified::FontRelativeLength::Em(calc.percentage()); + let fr = specified::Length::FontRelative(fr); + computed_value::T::Length(calc.length() + fr.to_computed_value(context)) + } + } } } } @@ -804,9 +808,7 @@ pub mod longhands { % for keyword in vertical_align_keywords: ${to_rust_ident(keyword)}, % endfor - Length(Au), - Percentage(CSSFloat), - Calc(computed::Calc), + LengthOrPercentage(computed::LengthOrPercentage), } impl fmt::Debug for T { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -814,9 +816,7 @@ pub mod longhands { % for keyword in vertical_align_keywords: T::${to_rust_ident(keyword)} => write!(f, "${keyword}"), % endfor - T::Length(length) => write!(f, "{:?}", length), - T::Percentage(number) => write!(f, "{}%", number), - T::Calc(calc) => write!(f, "{:?}", calc) + T::LengthOrPercentage(value) => write!(f, "{:?}", value), } } } @@ -826,9 +826,7 @@ pub mod longhands { % for keyword in vertical_align_keywords: T::${to_rust_ident(keyword)} => dest.write_str("${keyword}"), % endfor - T::Length(value) => value.to_css(dest), - T::Percentage(percentage) => write!(dest, "{}%", percentage * 100.), - T::Calc(calc) => calc.to_css(dest), + T::LengthOrPercentage(value) => value.to_css(dest), } } } @@ -847,16 +845,8 @@ pub mod longhands { computed_value::T::${to_rust_ident(keyword)} } % endfor - SpecifiedValue::LengthOrPercentage(value) => { - match value.to_computed_value(context) { - computed::LengthOrPercentage::Length(value) => - computed_value::T::Length(value), - computed::LengthOrPercentage::Percentage(value) => - computed_value::T::Percentage(value), - computed::LengthOrPercentage::Calc(value) => - computed_value::T::Calc(value), - } - } + SpecifiedValue::LengthOrPercentage(value) => + computed_value::T::LengthOrPercentage(value.to_computed_value(context)), } } } @@ -1311,7 +1301,8 @@ pub mod longhands { if content::counter_name_is_illegal(&counter_name) { return Err(()) } - let counter_delta = input.try(|input| input.expect_integer()).unwrap_or(1) as i32; + let counter_delta = + input.try(|input| specified::parse_integer(input)).unwrap_or(1); counters.push((counter_name, counter_delta)) } @@ -1478,8 +1469,7 @@ pub mod longhands { PositionCategory::VerticalKeyword, specified::PositionComponent::Center => PositionCategory::OtherKeyword, - specified::PositionComponent::Length(_) | - specified::PositionComponent::Percentage(_) => + specified::PositionComponent::LengthOrPercentage(_) => PositionCategory::LengthOrPercentage, } } @@ -1929,7 +1919,7 @@ pub mod longhands { } #[derive(Clone, PartialEq)] - pub struct SpecifiedValue(pub specified::Length); // Percentages are the same as em. + pub struct SpecifiedValue(pub specified::LengthOrPercentage); pub mod computed_value { use app_units::Au; pub type T = Au; @@ -1949,17 +1939,14 @@ pub mod longhands { } /// <length> | <percentage> | <absolute-size> | <relative-size> pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + use values::specified::{Length, LengthOrPercentage}; + input.try(specified::LengthOrPercentage::parse_non_negative) - .and_then(|value| match value { - specified::LengthOrPercentage::Length(value) => Ok(value), - specified::LengthOrPercentage::Percentage(value) => - Ok(specified::Length::FontRelative(specified::FontRelativeLength::Em(value.0))), - // FIXME(dzbarsky) handle calc for font-size - specified::LengthOrPercentage::Calc(_) => Err(()) - }) .or_else(|()| { let ident = try!(input.expect_ident()); - specified::Length::from_str(&ident as &str).ok_or(()) + specified::Length::from_str(&ident as &str) + .ok_or(()) + .map(specified::LengthOrPercentage::Length) }) .map(SpecifiedValue) } @@ -2649,7 +2636,7 @@ pub mod longhands { if input.try(|input| input.expect_ident_matching("auto")).is_ok() { Ok(SpecifiedValue::Auto) } else { - let count = try!(input.expect_integer()); + let count = try!(specified::parse_integer(input)); // Zero is invalid if count <= 0 { return Err(()) @@ -2763,7 +2750,7 @@ pub mod longhands { } } fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { - input.expect_number().map(SpecifiedValue) + specified::parse_number(input).map(SpecifiedValue) } </%self:longhand> @@ -3628,10 +3615,10 @@ pub mod longhands { } fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> { - let first = try!(input.expect_number()); + let first = try!(specified::parse_number(input)); let second = input.try(|input| { try!(input.expect_comma()); - input.expect_number() + specified::parse_number(input) }).unwrap_or(first); Ok((first, second)) } @@ -3771,7 +3758,7 @@ pub mod longhands { "matrix" => { try!(input.parse_nested_block(|input| { let values = try!(input.parse_comma_separated(|input| { - input.expect_number() + specified::parse_number(input) })); if values.len() != 6 { return Err(()) @@ -3789,7 +3776,7 @@ pub mod longhands { "matrix3d" => { try!(input.parse_nested_block(|input| { let values = try!(input.parse_comma_separated(|input| { - input.expect_number() + specified::parse_number(input) })); if values.len() != 16 { return Err(()) @@ -3872,32 +3859,32 @@ pub mod longhands { }, "scalex" => { try!(input.parse_nested_block(|input| { - let sx = try!(input.expect_number()); + let sx = try!(specified::parse_number(input)); result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0)); Ok(()) })) }, "scaley" => { try!(input.parse_nested_block(|input| { - let sy = try!(input.expect_number()); + let sy = try!(specified::parse_number(input)); result.push(SpecifiedOperation::Scale(1.0, sy, 1.0)); Ok(()) })) }, "scalez" => { try!(input.parse_nested_block(|input| { - let sz = try!(input.expect_number()); + let sz = try!(specified::parse_number(input)); result.push(SpecifiedOperation::Scale(1.0, 1.0, sz)); Ok(()) })) }, "scale3d" => { try!(input.parse_nested_block(|input| { - let sx = try!(input.expect_number()); + let sx = try!(specified::parse_number(input)); try!(input.expect_comma()); - let sy = try!(input.expect_number()); + let sy = try!(specified::parse_number(input)); try!(input.expect_comma()); - let sz = try!(input.expect_number()); + let sz = try!(specified::parse_number(input)); result.push(SpecifiedOperation::Scale(sx, sy, sz)); Ok(()) })) @@ -3932,11 +3919,11 @@ pub mod longhands { }, "rotate3d" => { try!(input.parse_nested_block(|input| { - let ax = try!(input.expect_number()); + let ax = try!(specified::parse_number(input)); try!(input.expect_comma()); - let ay = try!(input.expect_number()); + let ay = try!(specified::parse_number(input)); try!(input.expect_comma()); - let az = try!(input.expect_number()); + let az = try!(specified::parse_number(input)); try!(input.expect_comma()); let theta = try!(specified::Angle::parse(input)); // TODO(gw): Check the axis can be normalized!! @@ -4539,13 +4526,13 @@ pub mod longhands { "cubic-bezier" => { let (mut p1x, mut p1y, mut p2x, mut p2y) = (0.0, 0.0, 0.0, 0.0); try!(input.parse_nested_block(|input| { - p1x = try!(input.expect_number()); + p1x = try!(specified::parse_number(input)); try!(input.expect_comma()); - p1y = try!(input.expect_number()); + p1y = try!(specified::parse_number(input)); try!(input.expect_comma()); - p2x = try!(input.expect_number()); + p2x = try!(specified::parse_number(input)); try!(input.expect_comma()); - p2y = try!(input.expect_number()); + p2y = try!(specified::parse_number(input)); Ok(()) })); let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y)); @@ -4554,7 +4541,7 @@ pub mod longhands { "steps" => { let (mut step_count, mut start_end) = (0, computed_value::StartEnd::Start); try!(input.parse_nested_block(|input| { - step_count = try!(input.expect_integer()); + step_count = try!(specified::parse_integer(input)); try!(input.expect_comma()); start_end = try!(match_ignore_ascii_case! { try!(input.expect_ident()), @@ -6520,6 +6507,7 @@ pub fn cascade(viewport_size: Size2D<Au>, // Initialize `context` // Declarations blocks are already stored in increasing precedence order. for sub_list in applicable_declarations { + use values::specified::{LengthOrPercentage, Percentage}; // Declarations are stored in reverse source order, we want them in forward order here. for declaration in sub_list.declarations.iter().rev() { match *declaration { @@ -6528,14 +6516,23 @@ pub fn cascade(viewport_size: Size2D<Au>, value, &custom_properties, |value| match *value { DeclaredValue::Value(ref specified_value) => { match specified_value.0 { - Length::FontRelative(value) => { + LengthOrPercentage::Length(Length::FontRelative(value)) => { value.to_computed_value(context.inherited_font_size, context.root_font_size) } - Length::ServoCharacterWidth(value) => { + LengthOrPercentage::Length(Length::ServoCharacterWidth(value)) => { value.to_computed_value(context.inherited_font_size) } - _ => specified_value.0.to_computed_value(&context) + LengthOrPercentage::Length(l) => { + l.to_computed_value(&context) + } + LengthOrPercentage::Percentage(Percentage(value)) => { + context.inherited_font_size.scale_by(value) + } + LengthOrPercentage::Calc(calc) => { + let calc = calc.to_computed_value(&context); + calc.length() + context.inherited_font_size.scale_by(calc.percentage()) + } } } DeclaredValue::Initial => longhands::font_size::get_initial_value(), diff --git a/components/style/values.rs b/components/style/values.rs index d8850dc31cf..a32b668fa9c 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -232,6 +232,8 @@ pub mod specified { /// This cannot be specified by the user directly and is only generated by /// `Stylist::synthesize_rules_for_legacy_attributes()`. ServoCharacterWidth(CharacterWidth), + + Calc(CalcLengthOrPercentage), } impl ToCss for Length { @@ -240,6 +242,7 @@ pub mod specified { 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(calc) => calc.to_css(dest), Length::ServoCharacterWidth(_) => panic!("internal CSS values should never be serialized"), } @@ -255,6 +258,7 @@ pub mod specified { 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!"), } } @@ -292,6 +296,7 @@ pub mod specified { 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 { @@ -320,6 +325,8 @@ pub mod specified { 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(CalcLengthOrPercentage::parse_length), _ => Err(()) } } @@ -335,6 +342,7 @@ pub mod specified { "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 @@ -369,6 +377,8 @@ pub mod specified { #[derive(Clone, Debug)] enum CalcValueNode { Length(Length), + Angle(Angle), + Time(Time), Percentage(CSSFloat), Number(CSSFloat), Sum(Box<CalcSumNode>), @@ -392,6 +402,8 @@ pub mod specified { #[derive(Clone, Debug)] enum SimplifiedValueNode { Length(Length), + Angle(Angle), + Time(Time), Percentage(CSSFloat), Number(CSSFloat), Sum(Box<SimplifiedSumNode>), @@ -404,6 +416,8 @@ pub mod specified { match *self { SimplifiedValueNode::Length(l) => SimplifiedValueNode::Length(l * scalar), SimplifiedValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p * scalar), + SimplifiedValueNode::Angle(Angle(a)) => SimplifiedValueNode::Angle(Angle(a * scalar)), + SimplifiedValueNode::Time(Time(t)) => SimplifiedValueNode::Time(Time(t * scalar)), SimplifiedValueNode::Number(n) => SimplifiedValueNode::Number(n * scalar), SimplifiedValueNode::Sum(box ref s) => { let sum = s * scalar; @@ -413,8 +427,68 @@ pub mod specified { } } + pub fn parse_integer(input: &mut Parser) -> Result<i32, ()> { + match try!(input.next()) { + Token::Number(ref value) => value.int_value.ok_or(()), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Integer))); + + let mut result = None; + + for ref node in ast.products { + match try!(CalcLengthOrPercentage::simplify_product(node)) { + SimplifiedValueNode::Number(val) => + result = Some(result.unwrap_or(0) + val as i32), + _ => unreachable!() + } + } + + match result { + Some(result) => Ok(result), + _ => Err(()) + } + } + _ => Err(()) + } + } + + pub fn parse_number(input: &mut Parser) -> Result<f32, ()> { + match try!(input.next()) { + Token::Number(ref value) => Ok(value.value), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Number))); + + let mut result = None; + + for ref node in ast.products { + match try!(CalcLengthOrPercentage::simplify_product(node)) { + SimplifiedValueNode::Number(val) => + result = Some(result.unwrap_or(0.) + val), + _ => unreachable!() + } + } + + match result { + Some(result) => Ok(result), + _ => Err(()) + } + } + _ => Err(()) + } + } + + #[derive(Clone, Copy, PartialEq)] + enum CalcUnit { + Number, + Integer, + Length, + LengthOrPercentage, + Angle, + Time, + } + #[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)] - pub struct Calc { + pub struct CalcLengthOrPercentage { pub absolute: Option<Au>, pub vw: Option<ViewportPercentageLength>, pub vh: Option<ViewportPercentageLength>, @@ -426,18 +500,18 @@ pub mod specified { pub rem: Option<FontRelativeLength>, pub percentage: Option<Percentage>, } - impl Calc { - fn parse_sum(input: &mut Parser) -> Result<CalcSumNode, ()> { + impl CalcLengthOrPercentage { + fn parse_sum(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcSumNode, ()> { let mut products = Vec::new(); - products.push(try!(Calc::parse_product(input))); + products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit))); loop { match input.next() { Ok(Token::Delim('+')) => { - products.push(try!(Calc::parse_product(input))); + products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit))); } Ok(Token::Delim('-')) => { - let mut right = try!(Calc::parse_product(input)); + let mut right = try!(CalcLengthOrPercentage::parse_product(input, expected_unit)); right.values.push(CalcValueNode::Number(-1.)); products.push(right); } @@ -449,17 +523,17 @@ pub mod specified { Ok(CalcSumNode { products: products }) } - fn parse_product(input: &mut Parser) -> Result<CalcProductNode, ()> { + fn parse_product(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcProductNode, ()> { let mut values = Vec::new(); - values.push(try!(Calc::parse_value(input))); + values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit))); loop { let position = input.position(); match input.next() { Ok(Token::Delim('*')) => { - values.push(try!(Calc::parse_value(input))); + values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit))); } - Ok(Token::Delim('/')) => { + Ok(Token::Delim('/')) if expected_unit != CalcUnit::Integer => { if let Ok(Token::Number(ref value)) = input.next() { if value.value == 0. { return Err(()); @@ -479,16 +553,24 @@ pub mod specified { Ok(CalcProductNode { values: values }) } - fn parse_value(input: &mut Parser) -> Result<CalcValueNode, ()> { - match input.next() { - Ok(Token::Number(ref value)) => Ok(CalcValueNode::Number(value.value)), - Ok(Token::Dimension(ref value, ref unit)) => - Length::parse_dimension(value.value, unit).map(CalcValueNode::Length), - Ok(Token::Percentage(ref value)) => + 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)), - Ok(Token::ParenthesisBlock) => { - let result = try!(input.parse_nested_block(Calc::parse_sum)); - Ok(CalcValueNode::Sum(box result)) + (Token::ParenthesisBlock, _) => { + input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, expected_unit)) + .map(|result| CalcValueNode::Sum(box result)) }, _ => Err(()) } @@ -497,7 +579,7 @@ pub mod specified { fn simplify_value_to_number(node: &CalcValueNode) -> Option<CSSFloat> { match *node { CalcValueNode::Number(number) => Some(number), - CalcValueNode::Sum(box ref sum) => Calc::simplify_sum_to_number(sum), + CalcValueNode::Sum(box ref sum) => CalcLengthOrPercentage::simplify_sum_to_number(sum), _ => None } } @@ -505,7 +587,7 @@ pub mod specified { fn simplify_sum_to_number(node: &CalcSumNode) -> Option<CSSFloat> { let mut sum = 0.; for ref product in &node.products { - match Calc::simplify_product_to_number(product) { + match CalcLengthOrPercentage::simplify_product_to_number(product) { Some(number) => sum += number, _ => return None } @@ -516,7 +598,7 @@ pub mod specified { fn simplify_product_to_number(node: &CalcProductNode) -> Option<CSSFloat> { let mut product = 1.; for ref value in &node.values { - match Calc::simplify_value_to_number(value) { + match CalcLengthOrPercentage::simplify_value_to_number(value) { Some(number) => product *= number, _ => return None } @@ -527,7 +609,7 @@ pub mod specified { fn simplify_products_in_sum(node: &CalcSumNode) -> Result<SimplifiedValueNode, ()> { let mut simplified = Vec::new(); for product in &node.products { - match try!(Calc::simplify_product(product)) { + match try!(CalcLengthOrPercentage::simplify_product(product)) { SimplifiedValueNode::Sum(box sum) => simplified.push_all(&sum.values), val => simplified.push(val), } @@ -544,13 +626,15 @@ pub mod specified { let mut multiplier = 1.; let mut node_with_unit = None; for node in &node.values { - match Calc::simplify_value_to_number(&node) { + 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(box ref sum) => - try!(Calc::simplify_products_in_sum(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") }) @@ -565,12 +649,20 @@ pub mod specified { } } - pub fn parse(input: &mut Parser) -> Result<Calc, ()> { - let ast = try!(Calc::parse_sum(input)); + fn parse_length(input: &mut Parser) -> Result<Length, ()> { + CalcLengthOrPercentage::parse(input, CalcUnit::Length).map(Length::Calc) + } + + 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!(Calc::simplify_product(node)) { + match try!(CalcLengthOrPercentage::simplify_product(node)) { SimplifiedValueNode::Sum(sum) => simplified.push_all(&sum.values), value => simplified.push(value), } @@ -617,11 +709,11 @@ pub mod specified { rem = Some(rem.unwrap_or(0.) + val), }, SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val), - _ => unreachable!() + _ => return Err(()), } } - Ok(Calc { + Ok(CalcLengthOrPercentage { absolute: absolute.map(Au), vw: vw.map(ViewportPercentageLength::Vw), vh: vh.map(ViewportPercentageLength::Vh), @@ -634,9 +726,66 @@ pub mod specified { 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.push_all(&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.push_all(&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(()) + } + } } - impl ToCss for Calc { + impl ToCss for CalcLengthOrPercentage { #[allow(unused_assignments)] fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { @@ -701,7 +850,7 @@ pub mod specified { pub enum LengthOrPercentage { Length(Length), Percentage(Percentage), - Calc(Calc), + Calc(CalcLengthOrPercentage), } impl ToCss for LengthOrPercentage { @@ -729,7 +878,7 @@ pub mod specified { 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(Calc::parse)); + let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); Ok(LengthOrPercentage::Calc(calc)) }, _ => Err(()) @@ -750,7 +899,7 @@ pub mod specified { Length(Length), Percentage(Percentage), Auto, - Calc(Calc), + Calc(CalcLengthOrPercentage), } impl ToCss for LengthOrPercentageOrAuto { @@ -778,7 +927,7 @@ pub mod specified { 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(Calc::parse)); + let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); Ok(LengthOrPercentageOrAuto::Calc(calc)) }, _ => Err(()) @@ -798,6 +947,7 @@ pub mod specified { pub enum LengthOrPercentageOrNone { Length(Length), Percentage(Percentage), + Calc(CalcLengthOrPercentage), None, } @@ -806,6 +956,7 @@ pub mod specified { match *self { LengthOrPercentageOrNone::Length(length) => length.to_css(dest), LengthOrPercentageOrNone::Percentage(percentage) => percentage.to_css(dest), + LengthOrPercentageOrNone::Calc(calc) => calc.to_css(dest), LengthOrPercentageOrNone::None => dest.write_str("none"), } } @@ -821,6 +972,10 @@ pub mod specified { 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(()) @@ -859,6 +1014,8 @@ pub mod specified { 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(CalcLengthOrPercentage::parse_length).map(LengthOrNone::Length), Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => Ok(LengthOrNone::None), _ => Err(()) @@ -911,8 +1068,7 @@ pub mod specified { // http://dev.w3.org/csswg/css2/colors.html#propdef-background-position #[derive(Clone, PartialEq, Copy)] pub enum PositionComponent { - Length(Length), - Percentage(Percentage), + LengthOrPercentage(LengthOrPercentage), Center, Left, Right, @@ -921,35 +1077,29 @@ pub mod specified { } impl PositionComponent { pub fn parse(input: &mut Parser) -> Result<PositionComponent, ()> { - match try!(input.next()) { - Token::Dimension(ref value, ref unit) => { - Length::parse_dimension(value.value, unit) - .map(PositionComponent::Length) - } - Token::Percentage(ref value) => { - Ok(PositionComponent::Percentage(Percentage(value.unit_value))) - } - Token::Number(ref value) if value.value == 0. => { - Ok(PositionComponent::Length(Length::Absolute(Au(0)))) - } - Token::Ident(value) => { - match_ignore_ascii_case! { value, - "center" => Ok(PositionComponent::Center), - "left" => Ok(PositionComponent::Left), - "right" => Ok(PositionComponent::Right), - "top" => Ok(PositionComponent::Top), - "bottom" => Ok(PositionComponent::Bottom) - _ => Err(()) - } + + input.try(LengthOrPercentage::parse) + .map(PositionComponent::LengthOrPercentage) + .or_else(|()| { + match try!(input.next()) { + Token::Ident(value) => { + match_ignore_ascii_case! { value, + "center" => Ok(PositionComponent::Center), + "left" => Ok(PositionComponent::Left), + "right" => Ok(PositionComponent::Right), + "top" => Ok(PositionComponent::Top), + "bottom" => Ok(PositionComponent::Bottom) + _ => Err(()) + } + }, + _ => Err(()) } - _ => Err(()) - } + }) } #[inline] pub fn to_length_or_percentage(self) -> LengthOrPercentage { match self { - PositionComponent::Length(x) => LengthOrPercentage::Length(x), - PositionComponent::Percentage(x) => LengthOrPercentage::Percentage(x), + PositionComponent::LengthOrPercentage(value) => value, PositionComponent::Center => LengthOrPercentage::Percentage(Percentage(0.5)), PositionComponent::Left | PositionComponent::Top => LengthOrPercentage::Percentage(Percentage(0.0)), @@ -984,19 +1134,24 @@ pub mod specified { /// Parses an angle according to CSS-VALUES § 6.1. pub fn parse(input: &mut Parser) -> Result<Angle, ()> { match try!(input.next()) { - Token::Dimension(value, unit) => { - match_ignore_ascii_case! { unit, - "deg" => Ok(Angle(value.value * RAD_PER_DEG)), - "grad" => Ok(Angle(value.value * RAD_PER_GRAD)), - "turn" => Ok(Angle(value.value * RAD_PER_TURN)), - "rad" => Ok(Angle(value.value)) - _ => Err(()) - } - } + Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit), Token::Number(ref value) if value.value == 0. => Ok(Angle(0.)), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + input.parse_nested_block(CalcLengthOrPercentage::parse_angle) + }, _ => Err(()) } } + + pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle, ()> { + match_ignore_ascii_case! { unit, + "deg" => Ok(Angle(value * RAD_PER_DEG)), + "grad" => Ok(Angle(value * RAD_PER_GRAD)), + "turn" => Ok(Angle(value * RAD_PER_TURN)), + "rad" => Ok(Angle(value)) + _ => Err(()) + } + } } /// Specified values for an image according to CSS-IMAGES. @@ -1235,7 +1390,10 @@ pub mod specified { Ok(Token::Dimension(ref value, ref unit)) => { Time::parse_dimension(value.value, &unit) } - _ => Err(()), + Ok(Token::Function(ref name)) if name.eq_ignore_ascii_case("calc") => { + input.parse_nested_block(CalcLengthOrPercentage::parse_time) + } + _ => Err(()) } } } @@ -1251,7 +1409,7 @@ pub mod specified { impl ToCss for Time { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - write!(dest, "{}ms", self.0) + write!(dest, "{}s", self.0) } } } @@ -1328,6 +1486,7 @@ pub mod computed { fn to_computed_value(&self, context: &Context) -> Au { match *self { specified::Length::Absolute(length) => length, + specified::Length::Calc(calc) => calc.to_computed_value(context).length(), specified::Length::FontRelative(length) => length.to_computed_value(context.font_size, context.root_font_size), specified::Length::ViewportPercentage(length) => @@ -1339,12 +1498,12 @@ pub mod computed { } #[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)] - pub struct Calc { + pub struct CalcLengthOrPercentage { pub length: Option<Au>, pub percentage: Option<CSSFloat>, } - impl Calc { + impl CalcLengthOrPercentage { pub fn length(&self) -> Au { self.length.unwrap_or(Au(0)) } @@ -1354,17 +1513,17 @@ pub mod computed { } } - impl From<LengthOrPercentage> for Calc { - fn from(len: LengthOrPercentage) -> Calc { + impl From<LengthOrPercentage> for CalcLengthOrPercentage { + fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage { match len { LengthOrPercentage::Percentage(this) => { - Calc { + CalcLengthOrPercentage { length: None, percentage: Some(this), } } LengthOrPercentage::Length(this) => { - Calc { + CalcLengthOrPercentage { length: Some(this), percentage: None, } @@ -1376,17 +1535,17 @@ pub mod computed { } } - impl From<LengthOrPercentageOrAuto> for Option<Calc> { - fn from(len: LengthOrPercentageOrAuto) -> Option<Calc> { + impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> { + fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> { match len { LengthOrPercentageOrAuto::Percentage(this) => { - Some(Calc { + Some(CalcLengthOrPercentage { length: None, percentage: Some(this), }) } LengthOrPercentageOrAuto::Length(this) => { - Some(Calc { + Some(CalcLengthOrPercentage { length: Some(this), percentage: None, }) @@ -1401,7 +1560,7 @@ pub mod computed { } } - impl ::cssparser::ToCss for Calc { + 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.), @@ -1412,10 +1571,10 @@ pub mod computed { } } - impl ToComputedValue for specified::Calc { - type ComputedValue = Calc; + impl ToComputedValue for specified::CalcLengthOrPercentage { + type ComputedValue = CalcLengthOrPercentage; - fn to_computed_value(&self, context: &Context) -> Calc { + fn to_computed_value(&self, context: &Context) -> CalcLengthOrPercentage { let mut length = None; if let Some(absolute) = self.absolute { @@ -1435,7 +1594,7 @@ pub mod computed { } } - Calc { length: length, percentage: self.percentage.map(|p| p.0) } + CalcLengthOrPercentage { length: length, percentage: self.percentage.map(|p| p.0) } } } @@ -1474,7 +1633,7 @@ pub mod computed { pub enum LengthOrPercentage { Length(Au), Percentage(CSSFloat), - Calc(Calc), + Calc(CalcLengthOrPercentage), } impl LengthOrPercentage { @@ -1527,7 +1686,7 @@ pub mod computed { Length(Au), Percentage(CSSFloat), Auto, - Calc(Calc), + Calc(CalcLengthOrPercentage), } impl fmt::Debug for LengthOrPercentageOrAuto { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -1578,6 +1737,7 @@ pub mod computed { pub enum LengthOrPercentageOrNone { Length(Au), Percentage(CSSFloat), + Calc(CalcLengthOrPercentage), None, } impl fmt::Debug for LengthOrPercentageOrNone { @@ -1585,6 +1745,7 @@ pub mod computed { 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"), } } @@ -1602,6 +1763,9 @@ pub mod computed { specified::LengthOrPercentageOrNone::Percentage(value) => { LengthOrPercentageOrNone::Percentage(value.0) } + specified::LengthOrPercentageOrNone::Calc(calc) => { + LengthOrPercentageOrNone::Calc(calc.to_computed_value(context)) + } specified::LengthOrPercentageOrNone::None => { LengthOrPercentageOrNone::None } @@ -1615,6 +1779,7 @@ pub mod computed { 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"), } } @@ -1640,6 +1805,9 @@ pub mod computed { #[inline] fn to_computed_value(&self, context: &Context) -> LengthOrNone { match *self { + specified::LengthOrNone::Length(specified::Length::Calc(calc)) => { + LengthOrNone::Length(calc.to_computed_value(context).length()) + } specified::LengthOrNone::Length(value) => { LengthOrNone::Length(value.to_computed_value(context)) } diff --git a/tests/wpt/mozilla/meta/mozilla/calc.html.ini b/tests/wpt/mozilla/meta/mozilla/calc.html.ini index ad1ebaf3617..e365575ac70 100644 --- a/tests/wpt/mozilla/meta/mozilla/calc.html.ini +++ b/tests/wpt/mozilla/meta/mozilla/calc.html.ini @@ -6,123 +6,15 @@ [calc(0px + 0pt + 0pc + 0in + 0cm + 0mm + 0rem + 0em + 0ex + 0% + 0vw + 0vh + 0vmin + 0vmax)] expected: FAIL - [calc for border-width] - expected: FAIL - - [calc for border-top-width] - expected: FAIL - - [calc for border-left-width] - expected: FAIL - - [calc for border-right-width] - expected: FAIL - - [calc for border-bottom-width] - expected: FAIL - - [calc for outline-width] - expected: FAIL - - [calc for outline-offset] - expected: FAIL - - [calc for letter-spacing] - expected: FAIL - - [calc for word-spacing] - expected: FAIL - - [calc for border-spacing] - expected: FAIL - [calc for column-width] expected: FAIL [calc for column-gap] expected: FAIL - [calc for perspective] - expected: FAIL - - [calc for border-top-left-radius] - expected: FAIL - - [calc for border-bottom-left-radius] - expected: FAIL - - [calc for border-top-right-radius] - expected: FAIL - - [calc for border-bottom-right-radius] - expected: FAIL - - [calc for top] - expected: FAIL - - [calc for left] - expected: FAIL - - [calc for bottom] - expected: FAIL - - [calc for right] - expected: FAIL - - [calc for width] - expected: FAIL - - [calc for height] - expected: FAIL - - [calc for min-width] - expected: FAIL - - [calc for min-height] - expected: FAIL - - [calc for max-width] - expected: FAIL - - [calc for max-height] - expected: FAIL - - [calc for line-height] - expected: FAIL - - [calc for vertical-align] - expected: FAIL - - [calc for background-position] - expected: FAIL - - [calc for background-size] - expected: FAIL - - [calc for font-size] - expected: FAIL - - [calc for text-indent] - expected: FAIL - - [calc for transform-origin] - expected: FAIL - - [calc for perspective-origin] - expected: FAIL - - [calc for transition-delay] - expected: FAIL - - [calc for z-index] - expected: FAIL - [calc for column-count] expected: FAIL - [calc for opacity] - expected: FAIL - - [calc for transition-duration] + [calc(0ch + 0px + 0pt + 0pc + 0in + 0cm + 0mm + 0rem + 0em + 0ex + 0% + 0vw + 0vh + 0vmin + 0vmax)] expected: FAIL diff --git a/tests/wpt/mozilla/tests/mozilla/calc.html b/tests/wpt/mozilla/tests/mozilla/calc.html index b69c28e67d0..1db42bb6f25 100644 --- a/tests/wpt/mozilla/tests/mozilla/calc.html +++ b/tests/wpt/mozilla/tests/mozilla/calc.html @@ -39,8 +39,8 @@ var widthTests = [ ['calc(1px + 1pt + 1pc + 1in + 1cm + 1mm)', '155.88333333333333px', '155.88333333333333px'], // Alphabetical order - ['calc(0px + 0pt + 0pc + 0in + 0cm + 0mm + 0rem + 0em + 0ex + 0% + 0vw + 0vh + 0vmin + 0vmax)', - 'calc(0em + 0ex + 0px + 0rem + 0vh + 0vmax + 0vmin + 0vw + 0%)', + ['calc(0ch + 0px + 0pt + 0pc + 0in + 0cm + 0mm + 0rem + 0em + 0ex + 0% + 0vw + 0vh + 0vmin + 0vmax)', + 'calc(0ch + 0em + 0ex + 0px + 0rem + 0vh + 0vmax + 0vmin + 0vw + 0%)', '0px'], // Simplification @@ -59,7 +59,6 @@ widthTests.forEach(function(item) { }); var lengthProperties = [ - 'border-width', 'border-top-width', 'border-left-width', 'border-right-width', @@ -68,7 +67,6 @@ var lengthProperties = [ 'outline-offset', 'letter-spacing', 'word-spacing', - 'border-spacing', 'column-width', 'column-gap', 'perspective', @@ -78,15 +76,10 @@ lengthProperties.forEach(function(prop) { test(function() { div.style.setProperty(prop, 'calc(1px)'); assert_equals(div.style.getPropertyValue(prop), '1px'); - assert_equals(window.getComputedStyle(div).getPropertyValue(prop), '1px'); }, 'calc for ' + prop); }); var lengthOrPercentageProperties = [ - 'border-top-left-radius', - 'border-bottom-left-radius', - 'border-top-right-radius', - 'border-bottom-right-radius', 'top', 'left', 'bottom', @@ -99,31 +92,26 @@ var lengthOrPercentageProperties = [ 'max-height', 'line-height', 'vertical-align', - 'background-position', - 'background-size', 'font-size', 'text-indent', - 'transform-origin', - 'perspective-origin', ]; lengthOrPercentageProperties.forEach(function(prop) { test(function() { div.style.setProperty(prop, 'calc(1px + 0%)'); assert_equals(div.style.getPropertyValue(prop), 'calc(1px + 0%)'); - assert_equals(window.getComputedStyle(div).getPropertyValue(prop), '1px'); }, 'calc for ' + prop); }); var timeProperties = [ 'transition-delay', + 'transition-duration', ]; timeProperties.forEach(function(prop) { test(function() { div.style.setProperty(prop, 'calc(1s)'); assert_equals(div.style.getPropertyValue(prop), '1s'); - assert_equals(window.getComputedStyle(div).getPropertyValue(prop), '1s'); }, 'calc for ' + prop); }); @@ -131,22 +119,40 @@ var numberProperties = [ 'z-index', 'column-count', 'opacity', - 'transition-duration', ]; numberProperties.forEach(function(prop) { test(function() { div.style.setProperty(prop, 'calc(1)'); assert_equals(div.style.getPropertyValue(prop), '1'); - assert_equals(window.getComputedStyle(div).getPropertyValue(prop), '1'); }, 'calc for ' + prop); }); +var otherProperties = [ + ['border-width', 'calc(1px)', '1px 1px 1px 1px'], + ['border-spacing', 'calc(1px)', '1px 1px'], + ['transform-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50% 0px'], + ['perspective-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50%'], + ['background-size', 'calc(1px + 0%)', 'calc(1px + 0%) auto'], + ['background-position', 'calc(1px + 0%) calc(2px + 0%)', 'calc(1px + 0%) calc(2px + 0%)'], + ['border-top-left-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'], + ['border-bottom-left-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'], + ['border-top-right-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'], + ['border-bottom-right-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'], +]; + +otherProperties.forEach(function(testcase) { + test(function() { + div.style.setProperty(testcase[0], testcase[1]); + assert_equals(div.style.getPropertyValue(testcase[0]), testcase[2]); + }, 'calc for ' + testcase[0]); +}); /* TODO: test these: counter-increment, counter-reset, color, box-shadow, clip, text-shadow, transform transition-timing-function + angles */ </script> </head> |