style: Properly track whether <angle> or <time> values came from calc() expressions.

This commit is contained in:
Emilio Cobos Álvarez 2017-03-27 01:45:22 +02:00
parent c654c05884
commit fc72f096a0
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
10 changed files with 281 additions and 90 deletions

View file

@ -200,7 +200,7 @@ impl ToFilterOps for filter::T {
Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)), Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)),
Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)), Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)),
Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)), Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)),
Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.0)), Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.radians())),
Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)), Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)),
Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())), Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())),
Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)), Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)),

View file

@ -343,7 +343,7 @@ impl PropertyAnimation {
#[inline] #[inline]
fn does_animate(&self) -> bool { fn does_animate(&self) -> bool {
self.property.does_animate() && self.duration != Time(0.0) self.property.does_animate() && self.duration.seconds() != 0.0
} }
/// Whether this animation has the same end value as another one. /// Whether this animation has the same end value as another one.
@ -681,7 +681,7 @@ pub fn update_style_for_animation(context: &SharedStyleContext,
transition_property, name); transition_property, name);
match PropertyAnimation::from_transition_property(*transition_property, match PropertyAnimation::from_transition_property(*transition_property,
timing_function, timing_function,
Time(relative_duration as f32), Time::from_seconds(relative_duration as f32),
&from_style, &from_style,
&target_style) { &target_style) {
Some(property_animation) => { Some(property_animation) => {

View file

@ -36,7 +36,6 @@ use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage};
use values::computed::{MaxLength, MinLength}; use values::computed::{MaxLength, MinLength};
use values::computed::position::{HorizontalPosition, Position, VerticalPosition}; use values::computed::position::{HorizontalPosition, Position, VerticalPosition};
use values::computed::ToComputedValue; use values::computed::ToComputedValue;
use values::specified::Angle as SpecifiedAngle;
@ -455,7 +454,7 @@ impl Interpolate for i32 {
impl Interpolate for Angle { impl Interpolate for Angle {
#[inline] #[inline]
fn interpolate(&self, other: &Angle, progress: f64) -> Result<Self, ()> { fn interpolate(&self, other: &Angle, progress: f64) -> Result<Self, ()> {
self.radians().interpolate(&other.radians(), progress).map(Angle) self.radians().interpolate(&other.radians(), progress).map(Angle::from_radians)
} }
} }
@ -952,7 +951,7 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp
result.push(TransformOperation::Matrix(identity)); result.push(TransformOperation::Matrix(identity));
} }
TransformOperation::Skew(..) => { TransformOperation::Skew(..) => {
result.push(TransformOperation::Skew(Angle(0.0), Angle(0.0))); result.push(TransformOperation::Skew(Angle::zero(), Angle::zero()))
} }
TransformOperation::Translate(..) => { TransformOperation::Translate(..) => {
result.push(TransformOperation::Translate(LengthOrPercentage::zero(), result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
@ -963,7 +962,7 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp
result.push(TransformOperation::Scale(1.0, 1.0, 1.0)); result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
} }
TransformOperation::Rotate(..) => { TransformOperation::Rotate(..) => {
result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle(0.0))); result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle::zero()));
} }
TransformOperation::Perspective(..) => { TransformOperation::Perspective(..) => {
// http://dev.w3.org/csswg/css-transforms/#identity-transform-function // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
@ -1052,7 +1051,7 @@ fn interpolate_transform_list(from_list: &[TransformOperation],
} }
/// https://drafts.csswg.org/css-transforms/#Rotate3dDefined /// https://drafts.csswg.org/css-transforms/#Rotate3dDefined
fn rotate_to_matrix(x: f32, y: f32, z: f32, a: SpecifiedAngle) -> ComputedMatrix { fn rotate_to_matrix(x: f32, y: f32, z: f32, a: Angle) -> ComputedMatrix {
let half_rad = a.radians() / 2.0; let half_rad = a.radians() / 2.0;
let sc = (half_rad).sin() * (half_rad).cos(); let sc = (half_rad).sin() * (half_rad).cos();
let sq = (half_rad).sin().powi(2); let sq = (half_rad).sin().powi(2);

View file

@ -419,13 +419,13 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
} }
#[inline] #[inline]
pub fn get_initial_value() -> Time { pub fn get_initial_value() -> computed_value::T {
Time(0.0) computed_value::T::zero()
} }
#[inline] #[inline]
pub fn get_initial_specified_value() -> SpecifiedValue { pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue(0.0) Time::zero()
} }
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
@ -1221,7 +1221,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
let second = input.try(|input| { let second = input.try(|input| {
try!(input.expect_comma()); try!(input.expect_comma());
specified::Angle::parse(context, input) specified::Angle::parse(context, input)
}).unwrap_or(specified::Angle(0.0)); }).unwrap_or(specified::Angle::zero());
Ok((first, second)) Ok((first, second))
} }
@ -1581,14 +1581,14 @@ ${helpers.predefined_type("scroll-snap-coordinate",
"skewx" => { "skewx" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta_x = try!(specified::Angle::parse(context,input)); let theta_x = try!(specified::Angle::parse(context,input));
result.push(SpecifiedOperation::Skew(theta_x, specified::Angle(0.0))); result.push(SpecifiedOperation::Skew(theta_x, specified::Angle::zero()));
Ok(()) Ok(())
})) }))
}, },
"skewy" => { "skewy" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta_y = try!(specified::Angle::parse(context,input)); let theta_y = try!(specified::Angle::parse(context,input));
result.push(SpecifiedOperation::Skew(specified::Angle(0.0), theta_y)); result.push(SpecifiedOperation::Skew(specified::Angle::zero(), theta_y));
Ok(()) Ok(())
})) }))
}, },
@ -1640,11 +1640,13 @@ ${helpers.predefined_type("scroll-snap-coordinate",
let ax = ax.to_computed_value(context); let ax = ax.to_computed_value(context);
let ay = ay.to_computed_value(context); let ay = ay.to_computed_value(context);
let az = az.to_computed_value(context); let az = az.to_computed_value(context);
let theta = theta.to_computed_value(context);
let len = (ax * ax + ay * ay + az * az).sqrt(); let len = (ax * ax + ay * ay + az * az).sqrt();
result.push(computed_value::ComputedOperation::Rotate(ax / len, ay / len, az / len, theta)); result.push(computed_value::ComputedOperation::Rotate(ax / len, ay / len, az / len, theta));
} }
SpecifiedOperation::Skew(theta_x, theta_y) => { SpecifiedOperation::Skew(theta_x, theta_y) => {
result.push(computed_value::ComputedOperation::Skew(theta_x, theta_y)); result.push(computed_value::ComputedOperation::Skew(theta_x.to_computed_value(context),
theta_y.to_computed_value(context)));
} }
SpecifiedOperation::Perspective(ref d) => { SpecifiedOperation::Perspective(ref d) => {
result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context))); result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context)));
@ -1658,7 +1660,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
#[inline] #[inline]
fn from_computed_value(computed: &computed_value::T) -> Self { fn from_computed_value(computed: &computed_value::T) -> Self {
SpecifiedValue(computed.0.as_ref().map(|computed| { SpecifiedValue(computed.0.as_ref().map(|computed| {
let mut result = vec!(); let mut result = vec![];
for operation in computed { for operation in computed {
match *operation { match *operation {
computed_value::ComputedOperation::Matrix(ref matrix) => { computed_value::ComputedOperation::Matrix(ref matrix) => {
@ -1678,15 +1680,17 @@ ${helpers.predefined_type("scroll-snap-coordinate",
Number::from_computed_value(sy), Number::from_computed_value(sy),
Number::from_computed_value(sz))); Number::from_computed_value(sz)));
} }
computed_value::ComputedOperation::Rotate(ref ax, ref ay, ref az, theta) => { computed_value::ComputedOperation::Rotate(ref ax, ref ay, ref az, ref theta) => {
result.push(SpecifiedOperation::Rotate( result.push(SpecifiedOperation::Rotate(
Number::from_computed_value(ax), Number::from_computed_value(ax),
Number::from_computed_value(ay), Number::from_computed_value(ay),
Number::from_computed_value(az), Number::from_computed_value(az),
theta)); specified::Angle::from_computed_value(theta)));
} }
computed_value::ComputedOperation::Skew(theta_x, theta_y) => { computed_value::ComputedOperation::Skew(ref theta_x, ref theta_y) => {
result.push(SpecifiedOperation::Skew(theta_x, theta_y)); result.push(SpecifiedOperation::Skew(
specified::Angle::from_computed_value(theta_x),
specified::Angle::from_computed_value(theta_y)))
} }
computed_value::ComputedOperation::Perspective(ref d) => { computed_value::ComputedOperation::Perspective(ref d) => {
result.push(SpecifiedOperation::Perspective( result.push(SpecifiedOperation::Perspective(

View file

@ -96,7 +96,7 @@ ${helpers.single_keyword("image-rendering",
use std::f32::consts::PI; use std::f32::consts::PI;
use values::CSSFloat; use values::CSSFloat;
const TWO_PI: CSSFloat = 2.0*PI; const TWO_PI: CSSFloat = 2.0 * PI;
#[derive(Clone, PartialEq, Copy, Debug)] #[derive(Clone, PartialEq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -125,7 +125,7 @@ ${helpers.single_keyword("image-rendering",
} }
pub mod computed_value { pub mod computed_value {
use values::specified::Angle; use values::computed::Angle;
#[derive(Clone, PartialEq, Copy, Debug)] #[derive(Clone, PartialEq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -135,36 +135,35 @@ ${helpers.single_keyword("image-rendering",
} }
} }
const INITIAL_ANGLE: Angle = Angle(0.0);
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
computed_value::T::AngleWithFlipped(INITIAL_ANGLE, false) computed_value::T::AngleWithFlipped(computed::Angle::zero(), false)
} }
// According to CSS Content Module Level 3: // According to CSS Content Module Level 3:
// The computed value of the property is calculated by rounding the specified angle // The computed value of the property is calculated by rounding the specified angle
// to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn. // to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn.
#[inline] #[inline]
fn normalize_angle(angle: &Angle) -> Angle { fn normalize_angle(angle: &computed::Angle) -> computed::Angle {
let radians = angle.radians(); let radians = angle.radians();
let rounded_quarter_turns = (4.0 * radians / TWO_PI).round(); let rounded_quarter_turns = (4.0 * radians / TWO_PI).round();
let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0; let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0;
let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI; let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI;
Angle::from_radians(normalized_radians) computed::Angle::from_radians(normalized_radians)
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T; type ComputedValue = computed_value::T;
#[inline] #[inline]
fn to_computed_value(&self, _: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
if let Some(ref angle) = self.angle { if let Some(ref angle) = self.angle {
let normalized_angle = normalize_angle(angle); let angle = angle.to_computed_value(context);
let normalized_angle = normalize_angle(&angle);
computed_value::T::AngleWithFlipped(normalized_angle, self.flipped) computed_value::T::AngleWithFlipped(normalized_angle, self.flipped)
} else { } else {
if self.flipped { if self.flipped {
computed_value::T::AngleWithFlipped(INITIAL_ANGLE, true) computed_value::T::AngleWithFlipped(computed::Angle::zero(), true)
} else { } else {
computed_value::T::FromImage computed_value::T::FromImage
} }
@ -175,8 +174,12 @@ ${helpers.single_keyword("image-rendering",
fn from_computed_value(computed: &computed_value::T) -> Self { fn from_computed_value(computed: &computed_value::T) -> Self {
match *computed { match *computed {
computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false }, computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false },
computed_value::T::AngleWithFlipped(angle, flipped) => computed_value::T::AngleWithFlipped(ref angle, flipped) => {
SpecifiedValue { angle: Some(angle), flipped: flipped }, SpecifiedValue {
angle: Some(Angle::from_computed_value(angle)),
flipped: flipped,
}
}
} }
} }
} }
@ -205,7 +208,7 @@ ${helpers.single_keyword("image-rendering",
let angle = input.try(|input| Angle::parse(context, input)).ok(); let angle = input.try(|input| Angle::parse(context, input)).ok();
let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok(); let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok();
let explicit_angle = if angle.is_none() && !flipped { let explicit_angle = if angle.is_none() && !flipped {
Some(INITIAL_ANGLE) Some(Angle::zero())
} else { } else {
angle angle
}; };

View file

@ -537,27 +537,27 @@ impl ToComputedValue for specified::AngleOrCorner {
type ComputedValue = AngleOrCorner; type ComputedValue = AngleOrCorner;
#[inline] #[inline]
fn to_computed_value(&self, _: &Context) -> AngleOrCorner { fn to_computed_value(&self, context: &Context) -> AngleOrCorner {
match *self { match *self {
specified::AngleOrCorner::None => { specified::AngleOrCorner::None => {
AngleOrCorner::Angle(Angle(PI)) AngleOrCorner::Angle(Angle::from_radians(PI))
}, },
specified::AngleOrCorner::Angle(angle) => { specified::AngleOrCorner::Angle(angle) => {
AngleOrCorner::Angle(angle) AngleOrCorner::Angle(angle.to_computed_value(context))
}, },
specified::AngleOrCorner::Corner(horizontal, vertical) => { specified::AngleOrCorner::Corner(horizontal, vertical) => {
match (horizontal, vertical) { match (horizontal, vertical) {
(None, Some(VerticalDirection::Top)) => { (None, Some(VerticalDirection::Top)) => {
AngleOrCorner::Angle(Angle(0.0)) AngleOrCorner::Angle(Angle::from_radians(0.0))
}, },
(Some(HorizontalDirection::Right), None) => { (Some(HorizontalDirection::Right), None) => {
AngleOrCorner::Angle(Angle(PI * 0.5)) AngleOrCorner::Angle(Angle::from_radians(PI * 0.5))
}, },
(None, Some(VerticalDirection::Bottom)) => { (None, Some(VerticalDirection::Bottom)) => {
AngleOrCorner::Angle(Angle(PI)) AngleOrCorner::Angle(Angle::from_radians(PI))
}, },
(Some(HorizontalDirection::Left), None) => { (Some(HorizontalDirection::Left), None) => {
AngleOrCorner::Angle(Angle(PI * 1.5)) AngleOrCorner::Angle(Angle::from_radians(PI * 1.5))
}, },
(Some(horizontal), Some(vertical)) => { (Some(horizontal), Some(vertical)) => {
AngleOrCorner::Corner(horizontal, vertical) AngleOrCorner::Corner(horizontal, vertical)
@ -573,8 +573,8 @@ impl ToComputedValue for specified::AngleOrCorner {
#[inline] #[inline]
fn from_computed_value(computed: &AngleOrCorner) -> Self { fn from_computed_value(computed: &AngleOrCorner) -> Self {
match *computed { match *computed {
AngleOrCorner::Angle(angle) => { AngleOrCorner::Angle(ref angle) => {
specified::AngleOrCorner::Angle(angle) specified::AngleOrCorner::Angle(specified::Angle::from_computed_value(angle))
}, },
AngleOrCorner::Corner(horizontal, vertical) => { AngleOrCorner::Corner(horizontal, vertical) => {
specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical)) specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical))

View file

@ -20,7 +20,7 @@ pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword};
pub use super::{Auto, Either, None_}; pub use super::{Auto, Either, None_};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
pub use super::specified::{Angle, BorderStyle, GridLine, Percentage, Time, UrlOrNone}; pub use super::specified::{BorderStyle, GridLine, Percentage, UrlOrNone};
pub use super::specified::url::SpecifiedUrl; pub use super::specified::url::SpecifiedUrl;
pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone};
@ -114,6 +114,76 @@ impl<T> ToComputedValue for T
} }
} }
/// A computed `<angle>` value.
#[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
pub struct Angle {
radians: CSSFloat,
}
impl Angle {
/// Construct a computed `Angle` value from a radian amount.
pub fn from_radians(radians: CSSFloat) -> Self {
Angle {
radians: radians,
}
}
/// Return the amount of radians this angle represents.
#[inline]
pub fn radians(&self) -> CSSFloat {
self.radians
}
/// Returns an angle that represents a rotation of zero radians.
pub fn zero() -> Self {
Self::from_radians(0.0)
}
}
impl ToCss for Angle {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
write!(dest, "{}rad", self.radians())
}
}
/// A computed `<time>` value.
#[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
pub struct Time {
seconds: CSSFloat,
}
impl Time {
/// Construct a computed `Time` value from a seconds amount.
pub fn from_seconds(seconds: CSSFloat) -> Self {
Time {
seconds: seconds,
}
}
/// Construct a computed `Time` value that represents zero seconds.
pub fn zero() -> Self {
Self::from_seconds(0.0)
}
/// Return the amount of seconds this time represents.
#[inline]
pub fn seconds(&self) -> CSSFloat {
self.seconds
}
}
impl ToCss for Time {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
write!(dest, "{}s", self.seconds())
}
}
impl ToComputedValue for specified::Color { impl ToComputedValue for specified::Color {
type ComputedValue = RGBA; type ComputedValue = RGBA;

View file

@ -507,8 +507,8 @@ pub struct CalcProductNode {
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum CalcValueNode { pub enum CalcValueNode {
Length(NoCalcLength), Length(NoCalcLength),
Angle(Angle), Angle(CSSFloat),
Time(Time), Time(CSSFloat),
Percentage(CSSFloat), Percentage(CSSFloat),
Number(CSSFloat), Number(CSSFloat),
Sum(Box<CalcSumNode>), Sum(Box<CalcSumNode>),
@ -615,10 +615,14 @@ impl CalcLengthOrPercentage {
NoCalcLength::parse_dimension(value.value, unit).map(CalcValueNode::Length) NoCalcLength::parse_dimension(value.value, unit).map(CalcValueNode::Length)
} }
(Token::Dimension(ref value, ref unit), CalcUnit::Angle) => { (Token::Dimension(ref value, ref unit), CalcUnit::Angle) => {
Angle::parse_dimension(value.value, unit).map(CalcValueNode::Angle) Angle::parse_dimension(value.value, unit).map(|angle| {
CalcValueNode::Angle(angle.radians())
})
} }
(Token::Dimension(ref value, ref unit), CalcUnit::Time) => { (Token::Dimension(ref value, ref unit), CalcUnit::Time) => {
Time::parse_dimension(value.value, unit).map(CalcValueNode::Time) Time::parse_dimension(value.value, unit).map(|time| {
CalcValueNode::Time(time.seconds())
})
} }
(Token::Percentage(ref value), CalcUnit::LengthOrPercentage) => (Token::Percentage(ref value), CalcUnit::LengthOrPercentage) =>
Ok(CalcValueNode::Percentage(value.unit_value)), Ok(CalcValueNode::Percentage(value.unit_value)),
@ -802,14 +806,14 @@ impl CalcLengthOrPercentage {
for value in simplified { for value in simplified {
match value { match value {
SimplifiedValueNode::Time(Time(val)) => SimplifiedValueNode::Time(val) =>
time = Some(time.unwrap_or(0.) + val), time = Some(time.unwrap_or(0.) + val),
_ => return Err(()), _ => return Err(()),
} }
} }
match time { match time {
Some(time) => Ok(Time(time)), Some(time) => Ok(Time::from_calc(time)),
_ => Err(()) _ => Err(())
} }
} }
@ -831,16 +835,23 @@ impl CalcLengthOrPercentage {
for value in simplified { for value in simplified {
match value { match value {
SimplifiedValueNode::Angle(Angle(val)) => SimplifiedValueNode::Angle(val) => {
angle = Some(angle.unwrap_or(0.) + val), angle = Some(angle.unwrap_or(0.) + val)
SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val), }
// TODO(emilio): This `Number` logic looks fishy.
//
// In particular, this allows calc(2 - 2) to parse as an
// `Angle`, which doesn't seem desired to me.
SimplifiedValueNode::Number(val) => {
number = Some(number.unwrap_or(0.) + val)
}
_ => unreachable!() _ => unreachable!()
} }
} }
match (angle, number) { match (angle, number) {
(Some(angle), None) => Ok(Angle(angle)), (Some(angle), None) => Ok(Angle::from_calc(angle)),
(None, Some(value)) if value == 0. => Ok(Angle(0.)), (None, Some(value)) if value == 0. => Ok(Angle::from_calc(0.)),
_ => Err(()) _ => Err(())
} }
} }

View file

@ -18,7 +18,7 @@ use std::fmt;
use std::ops::Mul; use std::ops::Mul;
use style_traits::ToCss; use style_traits::ToCss;
use super::{Auto, CSSFloat, CSSInteger, HasViewportPercentage, Either, None_}; use super::{Auto, CSSFloat, CSSInteger, HasViewportPercentage, Either, None_};
use super::computed::{ComputedValueAsSpecified, Context}; use super::computed::{self, Context};
use super::computed::{Shadow as ComputedShadow, ToComputedValue}; use super::computed::{Shadow as ComputedShadow, ToComputedValue};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
@ -166,8 +166,8 @@ impl<'a> Mul<CSSFloat> for &'a SimplifiedSumNode {
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum SimplifiedValueNode { pub enum SimplifiedValueNode {
Length(NoCalcLength), Length(NoCalcLength),
Angle(Angle), Angle(CSSFloat),
Time(Time), Time(CSSFloat),
Percentage(CSSFloat), Percentage(CSSFloat),
Number(CSSFloat), Number(CSSFloat),
Sum(Box<SimplifiedSumNode>), Sum(Box<SimplifiedSumNode>),
@ -179,21 +179,31 @@ impl<'a> Mul<CSSFloat> for &'a SimplifiedValueNode {
#[inline] #[inline]
fn mul(self, scalar: CSSFloat) -> SimplifiedValueNode { fn mul(self, scalar: CSSFloat) -> SimplifiedValueNode {
match *self { match *self {
SimplifiedValueNode::Length(ref l) => SimplifiedValueNode::Length(l.clone() * scalar), SimplifiedValueNode::Length(ref l) => {
SimplifiedValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p * scalar), SimplifiedValueNode::Length(l.clone() * scalar)
SimplifiedValueNode::Angle(Angle(a)) => SimplifiedValueNode::Angle(Angle(a * scalar)), },
SimplifiedValueNode::Time(Time(t)) => SimplifiedValueNode::Time(Time(t * scalar)), SimplifiedValueNode::Percentage(p) => {
SimplifiedValueNode::Number(n) => SimplifiedValueNode::Number(n * scalar), SimplifiedValueNode::Percentage(p * scalar)
},
SimplifiedValueNode::Angle(a) => {
SimplifiedValueNode::Angle(a * scalar)
},
SimplifiedValueNode::Time(t) => {
SimplifiedValueNode::Time(t * scalar)
},
SimplifiedValueNode::Number(n) => {
SimplifiedValueNode::Number(n * scalar)
},
SimplifiedValueNode::Sum(ref s) => { SimplifiedValueNode::Sum(ref s) => {
let sum = &**s * scalar; let sum = &**s * scalar;
SimplifiedValueNode::Sum(Box::new(sum)) SimplifiedValueNode::Sum(Box::new(sum))
} },
} }
} }
} }
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn parse_integer(input: &mut Parser) -> Result<CSSInteger, ()> { pub fn parse_integer(input: &mut Parser) -> Result<i32, ()> {
match try!(input.next()) { match try!(input.next()) {
Token::Number(ref value) => value.int_value.ok_or(()), Token::Number(ref value) => value.int_value.ok_or(()),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
@ -302,11 +312,36 @@ impl ToCss for BorderRadiusSize {
#[derive(Clone, PartialEq, PartialOrd, Copy, Debug)] #[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
/// An angle, normalized to radians. /// An angle, normalized to radians.
pub struct Angle(pub CSSFloat); pub struct Angle {
radians: CSSFloat,
was_calc: bool,
}
impl ToCss for Angle { impl ToCss for Angle {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
write!(dest, "{}rad", self.0) if self.was_calc {
dest.write_str("calc(")?;
}
write!(dest, "{}rad", self.radians)?;
if self.was_calc {
dest.write_str(")")?;
}
Ok(())
}
}
impl ToComputedValue for Angle {
type ComputedValue = computed::Angle;
fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
computed::Angle::from_radians(self.radians())
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Angle {
radians: computed.radians(),
was_calc: false,
}
} }
} }
@ -314,13 +349,29 @@ impl Angle {
#[inline] #[inline]
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn radians(self) -> f32 { pub fn radians(self) -> f32 {
self.0 self.radians
}
/// Returns an angle value that represents zero radians.
pub fn zero() -> Self {
Self::from_radians(0.0)
} }
#[inline] #[inline]
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn from_radians(r: f32) -> Self { pub fn from_radians(r: f32) -> Self {
Angle(r) Angle {
radians: r,
was_calc: false,
}
}
/// Returns an `Angle` parsed from a `calc()` expression.
pub fn from_calc(radians: CSSFloat) -> Self {
Angle {
radians: radians,
was_calc: true,
}
} }
} }
@ -333,7 +384,7 @@ impl Parse for Angle {
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
match try!(input.next()) { match try!(input.next()) {
Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit), Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit),
Token::Number(ref value) if value.value == 0. => Ok(Angle(0.)), Token::Number(ref value) if value.value == 0. => Ok(Angle::zero()),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
input.parse_nested_block(CalcLengthOrPercentage::parse_angle) input.parse_nested_block(CalcLengthOrPercentage::parse_angle)
}, },
@ -345,13 +396,18 @@ impl Parse for Angle {
impl Angle { impl Angle {
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle, ()> { pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle, ()> {
match_ignore_ascii_case! { unit, let radians = match_ignore_ascii_case! { unit,
"deg" => Ok(Angle(value * RAD_PER_DEG)), "deg" => value * RAD_PER_DEG,
"grad" => Ok(Angle(value * RAD_PER_GRAD)), "grad" => value * RAD_PER_GRAD,
"turn" => Ok(Angle(value * RAD_PER_TURN)), "turn" => value * RAD_PER_TURN,
"rad" => Ok(Angle(value)), "rad" => value,
_ => Err(()) _ => return Err(())
} };
Ok(Angle {
radians: radians,
was_calc: false,
})
} }
} }
@ -482,28 +538,67 @@ impl BorderStyle {
/// A time in seconds according to CSS-VALUES § 6.2. /// A time in seconds according to CSS-VALUES § 6.2.
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Time(pub CSSFloat); pub struct Time {
seconds: CSSFloat,
was_calc: bool,
}
impl Time { impl Time {
/// Return a `<time>` value that represents `seconds` seconds.
pub fn from_seconds(seconds: CSSFloat) -> Self {
Time {
seconds: seconds,
was_calc: false,
}
}
/// Returns a time that represents a duration of zero.
pub fn zero() -> Self {
Self::from_seconds(0.0)
}
/// Returns the time in fractional seconds. /// Returns the time in fractional seconds.
pub fn seconds(self) -> f32 { pub fn seconds(self) -> CSSFloat {
let Time(seconds) = self; self.seconds
seconds
} }
/// Parses a time according to CSS-VALUES § 6.2. /// Parses a time according to CSS-VALUES § 6.2.
fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> { fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> {
if unit.eq_ignore_ascii_case("s") { let seconds = match_ignore_ascii_case! { unit,
Ok(Time(value)) "s" => value,
} else if unit.eq_ignore_ascii_case("ms") { "ms" => value / 1000.0,
Ok(Time(value / 1000.0)) _ => return Err(()),
} else { };
Err(())
Ok(Time {
seconds: seconds,
was_calc: false,
})
}
/// Returns a `Time` value from a CSS `calc()` expression.
pub fn from_calc(seconds: CSSFloat) -> Self {
Time {
seconds: seconds,
was_calc: true,
} }
} }
} }
impl ComputedValueAsSpecified for Time {} impl ToComputedValue for Time {
type ComputedValue = computed::Time;
fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
computed::Time::from_seconds(self.seconds())
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Time {
seconds: computed.seconds(),
was_calc: false,
}
}
}
impl Parse for Time { impl Parse for Time {
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
@ -521,7 +616,14 @@ impl Parse for Time {
impl ToCss for Time { impl ToCss for Time {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
write!(dest, "{}s", self.0) if self.was_calc {
dest.write_str("calc(")?;
}
write!(dest, "{}s", self.seconds)?;
if self.was_calc {
dest.write_str(")")?;
}
Ok(())
} }
} }
@ -588,7 +690,9 @@ impl ToComputedValue for Number {
} }
impl ToCss for Number { impl ToCss for Number {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
if self.was_calc { if self.was_calc {
dest.write_str("calc(")?; dest.write_str("calc(")?;
} }

View file

@ -55,7 +55,7 @@ fn test_linear_gradient() {
font_metrics_provider: None, font_metrics_provider: None,
}; };
assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context), assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context),
computed::AngleOrCorner::Angle(Angle(PI))); computed::AngleOrCorner::Angle(Angle::from_radians(PI)));
} }
#[test] #[test]