From c7ce2ff4835f5d08eb3bbab8516356baf7537f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 26 Mar 2017 21:43:05 +0200 Subject: [PATCH] style: Make numbers keep track of whether they were specified as calc(). --- .../sugar/ns_timing_function.rs | 3 +- components/style/properties/gecko.mako.rs | 5 +- .../style/properties/longhand/border.mako.rs | 4 +- .../style/properties/longhand/box.mako.rs | 187 +++++++++++++----- .../style/properties/longhand/font.mako.rs | 58 ++++-- .../longhand/inherited_text.mako.rs | 42 ++-- .../properties/shorthand/position.mako.rs | 8 +- components/style/values/specified/mod.rs | 74 ++++--- tests/unit/style/properties/serialization.rs | 9 +- 9 files changed, 271 insertions(+), 119 deletions(-) diff --git a/components/style/gecko_bindings/sugar/ns_timing_function.rs b/components/style/gecko_bindings/sugar/ns_timing_function.rs index f624e460f51..35419821e08 100644 --- a/components/style/gecko_bindings/sugar/ns_timing_function.rs +++ b/components/style/gecko_bindings/sugar/ns_timing_function.rs @@ -65,7 +65,8 @@ impl From for nsTimingFunction { tf.set_as_step(nsTimingFunction_Type::StepEnd, steps); }, SpecifiedTimingFunction::CubicBezier(p1, p2) => { - tf.set_as_cubic_bezier(p1, p2); + tf.set_as_cubic_bezier(Point2D::new(p1.x.value, p1.y.value), + Point2D::new(p2.x.value, p2.y.value)); }, SpecifiedTimingFunction::Keyword(keyword) => { match keyword { diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index cc890e4764a..892c4471632 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1283,7 +1283,7 @@ fn static_assert() { use properties::longhands::font_size_adjust::computed_value::T; match v { T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32, - T::Number(n) => self.gecko.mFont.sizeAdjust = n.0 as f32, + T::Number(n) => self.gecko.mFont.sizeAdjust = n, } } @@ -1293,11 +1293,10 @@ fn static_assert() { pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T { use properties::longhands::font_size_adjust::computed_value::T; - use values::specified::Number; match self.gecko.mFont.sizeAdjust { -1.0 => T::None, - _ => T::Number(Number(self.gecko.mFont.sizeAdjust)), + _ => T::Number(self.gecko.mFont.sizeAdjust), } } diff --git a/components/style/properties/longhand/border.mako.rs b/components/style/properties/longhand/border.mako.rs index 79f2bfc246b..2aa6f0fe4b7 100644 --- a/components/style/properties/longhand/border.mako.rs +++ b/components/style/properties/longhand/border.mako.rs @@ -223,7 +223,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box", #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { - SpecifiedValue(vec![Either::Second(Number(0.0))]) + SpecifiedValue(vec![Either::Second(Number::new(0.0))]) } impl ToComputedValue for SpecifiedValue { @@ -486,7 +486,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box", #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { - SpecifiedValue(vec![SingleSpecifiedValue::Number(Number(1.0))]) + SpecifiedValue(vec![SingleSpecifiedValue::Number(Number::new(1.0))]) } impl ToComputedValue for SpecifiedValue { diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index 9545df6c636..aed3ccea7ee 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -440,7 +440,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", extra_prefixes="moz webkit" spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function"> use self::computed_value::StartEnd; - + use values::specified::Number; use euclid::point::{Point2D, TypedPoint2D}; use std::fmt; use std::marker::PhantomData; @@ -450,31 +450,31 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", #[inline(always)] fn ease() -> computed_value::T { computed_value::T::CubicBezier(TypedPoint2D::new(0.25, 0.1), - TypedPoint2D::new(0.25, 1.0)) + TypedPoint2D::new(0.25, 1.0)) } #[inline(always)] fn linear() -> computed_value::T { computed_value::T::CubicBezier(TypedPoint2D::new(0.0, 0.0), - TypedPoint2D::new(1.0, 1.0)) + TypedPoint2D::new(1.0, 1.0)) } #[inline(always)] fn ease_in() -> computed_value::T { computed_value::T::CubicBezier(TypedPoint2D::new(0.42, 0.0), - TypedPoint2D::new(1.0, 1.0)) + TypedPoint2D::new(1.0, 1.0)) } #[inline(always)] fn ease_out() -> computed_value::T { computed_value::T::CubicBezier(TypedPoint2D::new(0.0, 0.0), - TypedPoint2D::new(0.58, 1.0)) + TypedPoint2D::new(0.58, 1.0)) } #[inline(always)] fn ease_in_out() -> computed_value::T { computed_value::T::CubicBezier(TypedPoint2D::new(0.42, 0.0), - TypedPoint2D::new(0.58, 1.0)) + TypedPoint2D::new(0.58, 1.0)) } static STEP_START: computed_value::T = @@ -547,7 +547,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum SpecifiedValue { - CubicBezier(Point2D, Point2D), + CubicBezier(Point2D, Point2D), Steps(u32, StartEnd), Keyword(FunctionKeyword), } @@ -557,7 +557,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", if let Ok(function_name) = input.try(|input| input.expect_function()) { return match_ignore_ascii_case! { &function_name, "cubic-bezier" => { - let (mut p1x, mut p1y, mut p2x, mut p2y) = (0.0, 0.0, 0.0, 0.0); + let (mut p1x, mut p1y, mut p2x, mut p2y) = + (Number::new(0.0), Number::new(0.0), Number::new(0.0), Number::new(0.0)); try!(input.parse_nested_block(|input| { p1x = try!(specified::parse_number(input)); try!(input.expect_comma()); @@ -568,7 +569,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", p2y = try!(specified::parse_number(input)); Ok(()) })); - if p1x < 0.0 || p1x > 1.0 || p2x < 0.0 || p2x > 1.0 { + if p1x.value < 0.0 || p1x.value > 1.0 || + p2x.value < 0.0 || p2x.value > 1.0 { return Err(()) } @@ -651,10 +653,12 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", type ComputedValue = computed_value::T; #[inline] - fn to_computed_value(&self, _context: &Context) -> computed_value::T { + fn to_computed_value(&self, context: &Context) -> computed_value::T { match *self { SpecifiedValue::CubicBezier(p1, p2) => { - computed_value::T::CubicBezier(p1, p2) + computed_value::T::CubicBezier( + Point2D::new(p1.x.to_computed_value(context), p1.y.to_computed_value(context)), + Point2D::new(p2.x.to_computed_value(context), p2.y.to_computed_value(context))) }, SpecifiedValue::Steps(count, start_end) => { computed_value::T::Steps(count, start_end) @@ -676,7 +680,11 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", fn from_computed_value(computed: &computed_value::T) -> Self { match *computed { computed_value::T::CubicBezier(p1, p2) => { - SpecifiedValue::CubicBezier(p1, p2) + SpecifiedValue::CubicBezier( + Point2D::new(Number::from_computed_value(&p1.x), + Number::from_computed_value(&p1.y)), + Point2D::new(Number::from_computed_value(&p2.x), + Number::from_computed_value(&p2.y))) }, computed_value::T::Steps(count, start_end) => { SpecifiedValue::Steps(count, start_end) @@ -1083,6 +1091,7 @@ ${helpers.predefined_type("scroll-snap-coordinate", fixpos_cb="True" spec="https://drafts.csswg.org/css-transforms/#propdef-transform"> use app_units::Au; + use values::specified::Number; use style_traits::ToCss; use values::CSSFloat; use values::HasViewportPercentage; @@ -1131,7 +1140,60 @@ ${helpers.predefined_type("scroll-snap-coordinate", pub struct T(pub Option>); } - pub use self::computed_value::ComputedMatrix as SpecifiedMatrix; + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + #[derive(Copy, Clone, Debug, PartialEq)] + pub struct SpecifiedMatrix { + pub m11: Number, pub m12: Number, pub m13: Number, pub m14: Number, + pub m21: Number, pub m22: Number, pub m23: Number, pub m24: Number, + pub m31: Number, pub m32: Number, pub m33: Number, pub m34: Number, + pub m41: Number, pub m42: Number, pub m43: Number, pub m44: Number, + } + + impl ToComputedValue for SpecifiedMatrix { + type ComputedValue = computed_value::ComputedMatrix; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + computed_value::ComputedMatrix { + m11: self.m11.to_computed_value(context), + m12: self.m12.to_computed_value(context), + m13: self.m13.to_computed_value(context), + m14: self.m14.to_computed_value(context), + m21: self.m21.to_computed_value(context), + m22: self.m22.to_computed_value(context), + m23: self.m23.to_computed_value(context), + m24: self.m24.to_computed_value(context), + m31: self.m31.to_computed_value(context), + m32: self.m32.to_computed_value(context), + m33: self.m33.to_computed_value(context), + m34: self.m34.to_computed_value(context), + m41: self.m41.to_computed_value(context), + m42: self.m42.to_computed_value(context), + m43: self.m43.to_computed_value(context), + m44: self.m44.to_computed_value(context), + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + SpecifiedMatrix { + m11: Number::from_computed_value(&computed.m11), + m12: Number::from_computed_value(&computed.m12), + m13: Number::from_computed_value(&computed.m13), + m14: Number::from_computed_value(&computed.m14), + m21: Number::from_computed_value(&computed.m21), + m22: Number::from_computed_value(&computed.m22), + m23: Number::from_computed_value(&computed.m23), + m24: Number::from_computed_value(&computed.m24), + m31: Number::from_computed_value(&computed.m31), + m32: Number::from_computed_value(&computed.m32), + m33: Number::from_computed_value(&computed.m33), + m34: Number::from_computed_value(&computed.m34), + m41: Number::from_computed_value(&computed.m41), + m42: Number::from_computed_value(&computed.m42), + m43: Number::from_computed_value(&computed.m43), + m44: Number::from_computed_value(&computed.m44), + } + } + } fn parse_two_lengths_or_percentages(context: &ParserContext, input: &mut Parser) -> Result<(specified::LengthOrPercentage, @@ -1144,7 +1206,7 @@ ${helpers.predefined_type("scroll-snap-coordinate", Ok((first, second)) } - fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> { + fn parse_two_numbers(input: &mut Parser) -> Result<(Number, Number), ()> { let first = try!(specified::parse_number(input)); let second = input.try(|input| { try!(input.expect_comma()); @@ -1182,8 +1244,8 @@ ${helpers.predefined_type("scroll-snap-coordinate", specified::LengthOrPercentage, specified::LengthOrPercentage, specified::Length), - Scale(CSSFloat, CSSFloat, CSSFloat), - Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle), + Scale(Number, Number, Number), + Rotate(Number, Number, Number, specified::Angle), Perspective(specified::Length), } @@ -1323,31 +1385,29 @@ ${helpers.predefined_type("scroll-snap-coordinate", if values.len() != 6 { return Err(()) } - result.push(SpecifiedOperation::Matrix( - SpecifiedMatrix { - m11: values[0], m12: values[1], m13: 0.0, m14: 0.0, - m21: values[2], m22: values[3], m23: 0.0, m24: 0.0, - m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0, - m41: values[4], m42: values[5], m43: 0.0, m44: 1.0 - })); + let matrix = SpecifiedMatrix { + m11: values[0], m12: values[1], m13: Number::new(0.0), m14: Number::new(0.0), + m21: values[2], m22: values[3], m23: Number::new(0.0), m24: Number::new(0.0), + m31: Number::new(0.0), m32: Number::new(0.0), m33: Number::new(1.0), m34: Number::new(0.0), + m41: values[4], m42: values[5], m43: Number::new(0.0), m44: Number::new(1.0), + }; + result.push(SpecifiedOperation::Matrix(matrix)); Ok(()) })) }, "matrix3d" => { try!(input.parse_nested_block(|input| { - let values = try!(input.parse_comma_separated(|input| { - specified::parse_number(input) - })); + let values = try!(input.parse_comma_separated(specified::parse_number)); if values.len() != 16 { return Err(()) } result.push(SpecifiedOperation::Matrix( - SpecifiedMatrix { - m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3], - m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7], - m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11], - m41: values[12], m42: values[13], m43: values[14], m44: values[15] - })); + SpecifiedMatrix { + m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3], + m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7], + m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11], + m41: values[12], m42: values[13], m43: values[14], m44: values[15] + })); Ok(()) })) }, @@ -1412,29 +1472,37 @@ ${helpers.predefined_type("scroll-snap-coordinate", }, "scale" => { try!(input.parse_nested_block(|input| { - let (sx, sy) = try!(parse_two_floats(input)); - result.push(SpecifiedOperation::Scale(sx, sy, 1.0)); + let (sx, sy) = try!(parse_two_numbers(input)); + result.push(SpecifiedOperation::Scale(sx, + sy, + Number::new(1.0))); Ok(()) })) }, "scalex" => { try!(input.parse_nested_block(|input| { let sx = try!(specified::parse_number(input)); - result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0)); + result.push(SpecifiedOperation::Scale(sx, + Number::new(1.0), + Number::new(1.0))); Ok(()) })) }, "scaley" => { try!(input.parse_nested_block(|input| { let sy = try!(specified::parse_number(input)); - result.push(SpecifiedOperation::Scale(1.0, sy, 1.0)); + result.push(SpecifiedOperation::Scale(Number::new(1.0), + sy, + Number::new(1.0))); Ok(()) })) }, "scalez" => { try!(input.parse_nested_block(|input| { let sz = try!(specified::parse_number(input)); - result.push(SpecifiedOperation::Scale(1.0, 1.0, sz)); + result.push(SpecifiedOperation::Scale(Number::new(1.0), + Number::new(1.0), + sz)); Ok(()) })) }, @@ -1452,28 +1520,40 @@ ${helpers.predefined_type("scroll-snap-coordinate", "rotate" => { try!(input.parse_nested_block(|input| { let theta = try!(specified::Angle::parse(context,input)); - result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta)); + result.push(SpecifiedOperation::Rotate(Number::new(0.0), + Number::new(0.0), + Number::new(1.0), + theta)); Ok(()) })) }, "rotatex" => { try!(input.parse_nested_block(|input| { let theta = try!(specified::Angle::parse(context,input)); - result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta)); + result.push(SpecifiedOperation::Rotate(Number::new(1.0), + Number::new(0.0), + Number::new(0.0), + theta)); Ok(()) })) }, "rotatey" => { try!(input.parse_nested_block(|input| { let theta = try!(specified::Angle::parse(context,input)); - result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta)); + result.push(SpecifiedOperation::Rotate(Number::new(0.0), + Number::new(1.0), + Number::new(0.0), + theta)); Ok(()) })) }, "rotatez" => { try!(input.parse_nested_block(|input| { let theta = try!(specified::Angle::parse(context,input)); - result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta)); + result.push(SpecifiedOperation::Rotate(Number::new(0.0), + Number::new(0.0), + Number::new(1.0), + theta)); Ok(()) })) }, @@ -1543,7 +1623,7 @@ ${helpers.predefined_type("scroll-snap-coordinate", for operation in &self.0 { match *operation { SpecifiedOperation::Matrix(ref matrix) => { - result.push(computed_value::ComputedOperation::Matrix(*matrix)); + result.push(computed_value::ComputedOperation::Matrix(matrix.to_computed_value(context))); } SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => { result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context), @@ -1551,9 +1631,15 @@ ${helpers.predefined_type("scroll-snap-coordinate", tz.to_computed_value(context))); } SpecifiedOperation::Scale(sx, sy, sz) => { + let sx = sx.to_computed_value(context); + let sy = sy.to_computed_value(context); + let sz = sz.to_computed_value(context); result.push(computed_value::ComputedOperation::Scale(sx, sy, sz)); } SpecifiedOperation::Rotate(ax, ay, az, theta) => { + let ax = ax.to_computed_value(context); + let ay = ay.to_computed_value(context); + let az = az.to_computed_value(context); let len = (ax * ax + ay * ay + az * az).sqrt(); result.push(computed_value::ComputedOperation::Rotate(ax / len, ay / len, az / len, theta)); } @@ -1576,7 +1662,7 @@ ${helpers.predefined_type("scroll-snap-coordinate", for operation in computed { match *operation { computed_value::ComputedOperation::Matrix(ref matrix) => { - result.push(SpecifiedOperation::Matrix(*matrix)); + result.push(SpecifiedOperation::Matrix(SpecifiedMatrix::from_computed_value(matrix))); } computed_value::ComputedOperation::Translate(ref tx, ref ty, ref tz) => { // XXXManishearth we lose information here; perhaps we should try to @@ -1586,11 +1672,18 @@ ${helpers.predefined_type("scroll-snap-coordinate", ToComputedValue::from_computed_value(ty), ToComputedValue::from_computed_value(tz))); } - computed_value::ComputedOperation::Scale(sx, sy, sz) => { - result.push(SpecifiedOperation::Scale(sx, sy, sz)); + computed_value::ComputedOperation::Scale(ref sx, ref sy, ref sz) => { + result.push(SpecifiedOperation::Scale( + Number::from_computed_value(sx), + Number::from_computed_value(sy), + Number::from_computed_value(sz))); } - computed_value::ComputedOperation::Rotate(ax, ay, az, theta) => { - result.push(SpecifiedOperation::Rotate(ax, ay, az, theta)); + computed_value::ComputedOperation::Rotate(ref ax, ref ay, ref az, theta) => { + result.push(SpecifiedOperation::Rotate( + Number::from_computed_value(ax), + Number::from_computed_value(ay), + Number::from_computed_value(az), + theta)); } computed_value::ComputedOperation::Skew(theta_x, theta_y) => { result.push(SpecifiedOperation::Skew(theta_x, theta_y)); diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 55550f2d7d8..4d9d97fc868 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -655,30 +655,65 @@ ${helpers.single_keyword("font-variant-caps", <%helpers:longhand products="gecko" name="font-size-adjust" animatable="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust"> + use std::fmt; + use style_traits::ToCss; use values::HasViewportPercentage; - use values::computed::ComputedValueAsSpecified; - use values::specified::Number; - impl ComputedValueAsSpecified for SpecifiedValue {} no_viewport_percentage!(SpecifiedValue); #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum SpecifiedValue { None, - Number(Number), + Number(specified::Number), + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result + where W: fmt::Write, + { + match *self { + SpecifiedValue::None => dest.write_str("none"), + SpecifiedValue::Number(number) => number.to_css(dest), + } + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + SpecifiedValue::None => computed_value::T::None, + SpecifiedValue::Number(ref n) => computed_value::T::Number(n.to_computed_value(context)), + } + } + + fn from_computed_value(computed: &computed_value::T) -> Self { + match *computed { + computed_value::T::None => SpecifiedValue::None, + computed_value::T::Number(ref v) => SpecifiedValue::Number(specified::Number::from_computed_value(v)), + } + } } pub mod computed_value { - use style_traits::ToCss; - use std::fmt; use properties::animated_properties::Interpolate; - use values::specified::Number; + use std::fmt; + use style_traits::ToCss; + use values::CSSFloat; - pub use super::SpecifiedValue as T; + #[derive(Copy, Clone, Debug, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub enum T { + None, + Number(CSSFloat), + } impl ToCss for T { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + fn to_css(&self, dest: &mut W) -> fmt::Result + where W: fmt::Write, + { match *self { T::None => dest.write_str("none"), T::Number(number) => number.to_css(dest), @@ -690,14 +725,15 @@ ${helpers.single_keyword("font-variant-caps", fn interpolate(&self, other: &Self, time: f64) -> Result { match (*self, *other) { (T::Number(ref number), T::Number(ref other)) => - Ok(T::Number(Number(try!(number.0.interpolate(&other.0, time))))), + Ok(T::Number(try!(number.interpolate(other, time)))), _ => Err(()), } } } } - #[inline] pub fn get_initial_value() -> computed_value::T { + #[inline] + pub fn get_initial_value() -> computed_value::T { computed_value::T::None } diff --git a/components/style/properties/longhand/inherited_text.mako.rs b/components/style/properties/longhand/inherited_text.mako.rs index 3739b6f3921..1ff10a50662 100644 --- a/components/style/properties/longhand/inherited_text.mako.rs +++ b/components/style/properties/longhand/inherited_text.mako.rs @@ -10,7 +10,7 @@ spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height"> use std::fmt; use style_traits::ToCss; - use values::{CSSFloat, HasViewportPercentage}; + use values::HasViewportPercentage; impl HasViewportPercentage for SpecifiedValue { fn has_viewport_percentage(&self) -> bool { @@ -28,7 +28,7 @@ % if product == "gecko": MozBlockHeight, % endif - Number(CSSFloat), + Number(specified::Number), LengthOrPercentage(specified::LengthOrPercentage), } @@ -40,7 +40,7 @@ SpecifiedValue::MozBlockHeight => dest.write_str("-moz-block-height"), % endif SpecifiedValue::LengthOrPercentage(ref value) => value.to_css(dest), - SpecifiedValue::Number(number) => write!(dest, "{}", number), + SpecifiedValue::Number(number) => number.to_css(dest), } } } @@ -52,24 +52,24 @@ // parsed as a plain Number rather than a Length (0px); this matches the behaviour // of all major browsers input.try(specified::Number::parse_non_negative) - .map(|n| SpecifiedValue::Number(n.0)) - .or_else(|()| { - input.try(specified::LengthOrPercentage::parse_non_negative) - .map(SpecifiedValue::LengthOrPercentage) + .map(SpecifiedValue::Number) .or_else(|()| { - match try!(input.next()) { - 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::Ident(ref value) if value.eq_ignore_ascii_case("normal") => { + Ok(SpecifiedValue::Normal) + } + % if product == "gecko": + Token::Ident(ref value) if value.eq_ignore_ascii_case("-moz-block-height") => { + Ok(SpecifiedValue::MozBlockHeight) + } + % endif + _ => Err(()), } - % if product == "gecko": - Token::Ident(ref value) if value.eq_ignore_ascii_case("-moz-block-height") => { - Ok(SpecifiedValue::MozBlockHeight) - } - % endif - _ => Err(()), - } + }) }) - }) } pub mod computed_value { use app_units::Au; @@ -116,7 +116,7 @@ % if product == "gecko": SpecifiedValue::MozBlockHeight => computed_value::T::MozBlockHeight, % endif - SpecifiedValue::Number(value) => computed_value::T::Number(value), + SpecifiedValue::Number(value) => computed_value::T::Number(value.to_computed_value(context)), SpecifiedValue::LengthOrPercentage(ref value) => { match *value { specified::LengthOrPercentage::Length(ref value) => @@ -144,7 +144,9 @@ % if product == "gecko": computed_value::T::MozBlockHeight => SpecifiedValue::MozBlockHeight, % endif - computed_value::T::Number(value) => SpecifiedValue::Number(value), + computed_value::T::Number(ref value) => { + SpecifiedValue::Number(specified::Number::from_computed_value(value)) + }, computed_value::T::Length(au) => { SpecifiedValue::LengthOrPercentage(specified::LengthOrPercentage::Length( ToComputedValue::from_computed_value(&au) diff --git a/components/style/properties/shorthand/position.mako.rs b/components/style/properties/shorthand/position.mako.rs index bf9c74cfcbb..825a63d3478 100644 --- a/components/style/properties/shorthand/position.mako.rs +++ b/components/style/properties/shorthand/position.mako.rs @@ -70,8 +70,8 @@ if input.try(|input| input.expect_ident_matching("none")).is_ok() { return Ok(Longhands { - flex_grow: Number(0.0), - flex_shrink: Number(0.0), + flex_grow: Number::new(0.0), + flex_shrink: Number::new(0.0), % if product == "gecko": flex_basis: LengthOrPercentageOrAuto::Auto % else: @@ -105,8 +105,8 @@ return Err(()) } Ok(Longhands { - flex_grow: grow.unwrap_or(Number(1.0)), - flex_shrink: shrink.unwrap_or(Number(1.0)), + flex_grow: grow.unwrap_or(Number::new(1.0)), + flex_shrink: shrink.unwrap_or(Number::new(1.0)), % if product == "gecko": flex_basis: basis.unwrap_or(LengthOrPercentageOrAuto::Length(NoCalcLength::zero())) % else: diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 5e3fb5c5982..36650a9e2ea 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -219,17 +219,15 @@ pub fn parse_integer(input: &mut Parser) -> Result { } #[allow(missing_docs)] -pub fn parse_number(input: &mut Parser) -> Result { +pub fn parse_number(input: &mut Parser) -> Result { + use std::f32; + match try!(input.next()) { Token::Number(ref value) => { - use std::f32; - if value.value.is_finite() { - Ok(value.value) - } else if value.value.is_sign_positive() { - Ok(f32::MAX) - } else { - Ok(f32::MIN) - } + Ok(Number { + value: value.value.min(f32::MAX).max(f32::MIN), + was_calc: false, + }) }, 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))); @@ -245,7 +243,12 @@ pub fn parse_number(input: &mut Parser) -> Result { } match result { - Some(result) => Ok(result), + Some(result) => { + Ok(Number { + value: result.min(f32::MAX).max(f32::MIN), + was_calc: true, + }) + }, _ => Err(()) } } @@ -525,24 +528,39 @@ impl ToCss for Time { #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] -pub struct Number(pub CSSFloat); +pub struct Number { + /// The numeric value itself. + pub value: CSSFloat, + /// Whether this came from a `calc()` expression. This is needed for + /// serialization purposes, since `calc(1)` should still serialize to + /// `calc(1)`, not just `1`. + was_calc: bool, +} no_viewport_percentage!(Number); impl Parse for Number { fn parse(_context: &ParserContext, input: &mut Parser) -> Result { - parse_number(input).map(Number) + parse_number(input) } } impl Number { fn parse_with_minimum(input: &mut Parser, min: CSSFloat) -> Result { match parse_number(input) { - Ok(value) if value >= min => Ok(Number(value)), + Ok(value) if value.value >= min => Ok(value), _ => Err(()), } } + /// Returns a new number with the value `val`. + pub fn new(val: CSSFloat) -> Self { + Number { + value: val, + was_calc: false, + } + } + #[allow(missing_docs)] pub fn parse_non_negative(input: &mut Parser) -> Result { Number::parse_with_minimum(input, 0.0) @@ -558,17 +576,27 @@ impl ToComputedValue for Number { type ComputedValue = CSSFloat; #[inline] - fn to_computed_value(&self, _: &Context) -> CSSFloat { self.0 } + fn to_computed_value(&self, _: &Context) -> CSSFloat { self.value } #[inline] fn from_computed_value(computed: &CSSFloat) -> Self { - Number(*computed) + Number { + value: *computed, + was_calc: false, + } } } impl ToCss for Number { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - self.0.to_css(dest) + if self.was_calc { + dest.write_str("calc(")?; + } + self.value.to_css(dest)?; + if self.was_calc { + dest.write_str(")")?; + } + Ok(()) } } @@ -606,7 +634,7 @@ impl ToCss for NumberOrPercentage { #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] -pub struct Opacity(pub CSSFloat); +pub struct Opacity(Number); no_viewport_percentage!(Opacity); @@ -620,19 +648,13 @@ impl ToComputedValue for Opacity { type ComputedValue = CSSFloat; #[inline] - fn to_computed_value(&self, _: &Context) -> CSSFloat { - if self.0 < 0.0 { - 0.0 - } else if self.0 > 1.0 { - 1.0 - } else { - self.0 - } + fn to_computed_value(&self, context: &Context) -> CSSFloat { + self.0.to_computed_value(context).min(1.0).max(0.0) } #[inline] fn from_computed_value(computed: &CSSFloat) -> Self { - Opacity(*computed) + Opacity(Number::from_computed_value(computed)) } } diff --git a/tests/unit/style/properties/serialization.rs b/tests/unit/style/properties/serialization.rs index 73fdf184584..44cf18d8cd3 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -573,15 +573,14 @@ mod shorthand_serialization { #[test] fn flex_should_serialize_all_available_properties() { - use style::values::specified::Number as NumberContainer; - use style::values::specified::Percentage as PercentageContainer; + use style::values::specified::{Number, Percentage}; let mut properties = Vec::new(); - let grow = NumberContainer(2f32); - let shrink = NumberContainer(3f32); + let grow = Number::new(2f32); + let shrink = Number::new(3f32); let basis = - LengthOrPercentageOrAutoOrContent::Percentage(PercentageContainer(0.5f32)); + LengthOrPercentageOrAutoOrContent::Percentage(Percentage(0.5f32)); properties.push(PropertyDeclaration::FlexGrow(grow)); properties.push(PropertyDeclaration::FlexShrink(shrink));