From e2f94d137adddcd44e04ba33d4eb10e2a22176a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Fri, 6 Jan 2017 23:26:17 +0300 Subject: [PATCH] Fix serialization of transition-timing-function --- .../style/properties/longhand/box.mako.rs | 286 +++++++++++++----- .../style/properties/shorthand/box.mako.rs | 4 +- 2 files changed, 210 insertions(+), 80 deletions(-) diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index df82244faebd..c9f926f05a39 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -495,60 +495,60 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_index="True" animatable="False" spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function"> - use self::computed_value::{StartEnd, TransitionTimingFunction}; + use self::computed_value::StartEnd; + use self::computed_value::TransitionTimingFunction as ComputedTransitionTimingFunction; use euclid::point::{Point2D, TypedPoint2D}; + use std::fmt; use std::marker::PhantomData; - use values::computed::ComputedValueAsSpecified; + use style_traits::ToCss; - pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue; - pub use self::computed_value::T as SpecifiedValue; + pub use self::TransitionTimingFunction as SingleSpecifiedValue; // FIXME: This could use static variables and const functions when they are available. #[inline(always)] - fn ease() -> TransitionTimingFunction { - TransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.25, 0.1), + fn ease() -> ComputedTransitionTimingFunction { + ComputedTransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.25, 0.1), TypedPoint2D::new(0.25, 1.0)) } #[inline(always)] - fn linear() -> TransitionTimingFunction { - TransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.0, 0.0), + fn linear() -> ComputedTransitionTimingFunction { + ComputedTransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.0, 0.0), TypedPoint2D::new(1.0, 1.0)) } #[inline(always)] - fn ease_in() -> TransitionTimingFunction { - TransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.42, 0.0), + fn ease_in() -> ComputedTransitionTimingFunction { + ComputedTransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.42, 0.0), TypedPoint2D::new(1.0, 1.0)) } #[inline(always)] - fn ease_out() -> TransitionTimingFunction { - TransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.0, 0.0), + fn ease_out() -> ComputedTransitionTimingFunction { + ComputedTransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.0, 0.0), TypedPoint2D::new(0.58, 1.0)) } #[inline(always)] - fn ease_in_out() -> TransitionTimingFunction { - TransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.42, 0.0), + fn ease_in_out() -> ComputedTransitionTimingFunction { + ComputedTransitionTimingFunction::CubicBezier(TypedPoint2D::new(0.42, 0.0), TypedPoint2D::new(0.58, 1.0)) } - static STEP_START: TransitionTimingFunction = - TransitionTimingFunction::Steps(1, StartEnd::Start); - static STEP_END: TransitionTimingFunction = - TransitionTimingFunction::Steps(1, StartEnd::End); + static STEP_START: ComputedTransitionTimingFunction = + ComputedTransitionTimingFunction::Steps(1, StartEnd::Start); + static STEP_END: ComputedTransitionTimingFunction = + ComputedTransitionTimingFunction::Steps(1, StartEnd::End); pub mod computed_value { use euclid::point::Point2D; use parser::{Parse, ParserContext}; use std::fmt; use style_traits::ToCss; - use values::specified; - use values::computed::ComputedValueAsSpecified; pub use self::TransitionTimingFunction as SingleComputedValue; + pub use super::parse; #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -557,55 +557,10 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", Steps(u32, StartEnd), } - impl Parse for TransitionTimingFunction { - fn parse(_context: &ParserContext, input: &mut ::cssparser::Parser) -> Result { - 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); - try!(input.parse_nested_block(|input| { - p1x = try!(specified::parse_number(input)); - try!(input.expect_comma()); - p1y = try!(specified::parse_number(input)); - try!(input.expect_comma()); - p2x = try!(specified::parse_number(input)); - try!(input.expect_comma()); - p2y = try!(specified::parse_number(input)); - Ok(()) - })); - let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y)); - Ok(TransitionTimingFunction::CubicBezier(p1, p2)) - }, - "steps" => { - let (mut step_count, mut start_end) = (0, StartEnd::End); - try!(input.parse_nested_block(|input| { - step_count = try!(specified::parse_integer(input)); - if input.try(|input| input.expect_comma()).is_ok() { - start_end = try!(match_ignore_ascii_case! { - try!(input.expect_ident()), - "start" => Ok(StartEnd::Start), - "end" => Ok(StartEnd::End), - _ => Err(()) - }); - } - Ok(()) - })); - Ok(TransitionTimingFunction::Steps(step_count as u32, start_end)) - }, - _ => Err(()) - } - } - match_ignore_ascii_case! { - try!(input.expect_ident()), - "ease" => Ok(super::ease()), - "linear" => Ok(super::linear()), - "ease-in" => Ok(super::ease_in()), - "ease-out" => Ok(super::ease_out()), - "ease-in-out" => Ok(super::ease_in_out()), - "step-start" => Ok(super::STEP_START), - "step-end" => Ok(super::STEP_END), - _ => Err(()) - } + impl TransitionTimingFunction { + pub fn parse(context: &ParserContext, input: &mut ::cssparser::Parser) -> + Result { + super::TransitionTimingFunction::parse(context, input) } } @@ -624,11 +579,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", dest.write_str(")") } TransitionTimingFunction::Steps(steps, start_end) => { - try!(dest.write_str("steps(")); - try!(steps.to_css(dest)); - try!(dest.write_str(", ")); - try!(start_end.to_css(dest)); - dest.write_str(")") + super::serialize_steps(dest, steps, start_end) } } } @@ -670,16 +621,194 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", } } + define_css_keyword_enum!(FunctionKeyword: + "ease" => Ease, + "linear" => Linear, + "ease-in" => EaseIn, + "ease-out" => EaseOut, + "ease-in-out" => EaseInOut, + "step-start" => StepStart, + "step-end" => StepEnd); + + #[derive(Copy, Clone, Debug, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub enum TransitionTimingFunction { + CubicBezier(Point2D, Point2D), + Steps(u32, StartEnd), + Keyword(FunctionKeyword), + } + + #[derive(Clone, Debug, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub struct SpecifiedValue(pub Vec); + + impl Parse for TransitionTimingFunction { + fn parse(_context: &ParserContext, input: &mut ::cssparser::Parser) -> Result { + 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); + try!(input.parse_nested_block(|input| { + p1x = try!(specified::parse_number(input)); + try!(input.expect_comma()); + p1y = try!(specified::parse_number(input)); + try!(input.expect_comma()); + p2x = try!(specified::parse_number(input)); + try!(input.expect_comma()); + p2y = try!(specified::parse_number(input)); + Ok(()) + })); + let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y)); + Ok(TransitionTimingFunction::CubicBezier(p1, p2)) + }, + "steps" => { + let (mut step_count, mut start_end) = (0, StartEnd::End); + try!(input.parse_nested_block(|input| { + step_count = try!(specified::parse_integer(input)); + if input.try(|input| input.expect_comma()).is_ok() { + start_end = try!(match_ignore_ascii_case! { + try!(input.expect_ident()), + "start" => Ok(StartEnd::Start), + "end" => Ok(StartEnd::End), + _ => Err(()) + }); + } + Ok(()) + })); + Ok(TransitionTimingFunction::Steps(step_count as u32, start_end)) + }, + _ => Err(()) + } + } + Ok(TransitionTimingFunction::Keyword(try!(FunctionKeyword::parse(input)))) + } + } + + fn serialize_steps(dest: &mut W, steps: u32, + start_end: StartEnd) -> fmt::Result where W: fmt::Write { + try!(dest.write_str("steps(")); + try!(steps.to_css(dest)); + if let StartEnd::Start = start_end { + try!(dest.write_str(", start")); + } + dest.write_str(")") + } + + // https://drafts.csswg.org/css-transitions/#serializing-a-timing-function + impl ToCss for TransitionTimingFunction { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + TransitionTimingFunction::CubicBezier(p1, p2) => { + try!(dest.write_str("cubic-bezier(")); + try!(p1.x.to_css(dest)); + try!(dest.write_str(", ")); + try!(p1.y.to_css(dest)); + try!(dest.write_str(", ")); + try!(p2.x.to_css(dest)); + try!(dest.write_str(", ")); + try!(p2.y.to_css(dest)); + dest.write_str(")") + }, + TransitionTimingFunction::Steps(steps, start_end) => { + serialize_steps(dest, steps, start_end) + }, + TransitionTimingFunction::Keyword(keyword) => { + match keyword { + FunctionKeyword::StepStart => { + serialize_steps(dest, 1, StartEnd::Start) + }, + FunctionKeyword::StepEnd => { + serialize_steps(dest, 1, StartEnd::End) + }, + _ => { + keyword.to_css(dest) + }, + } + }, + } + } + } + + impl ToComputedValue for TransitionTimingFunction { + type ComputedValue = ComputedTransitionTimingFunction; + + #[inline] + fn to_computed_value(&self, _context: &Context) -> ComputedTransitionTimingFunction { + match *self { + TransitionTimingFunction::CubicBezier(p1, p2) => { + ComputedTransitionTimingFunction::CubicBezier(p1, p2) + }, + TransitionTimingFunction::Steps(count, start_end) => { + ComputedTransitionTimingFunction::Steps(count, start_end) + }, + TransitionTimingFunction::Keyword(keyword) => { + match keyword { + FunctionKeyword::Ease => ease(), + FunctionKeyword::Linear => linear(), + FunctionKeyword::EaseIn => ease_in(), + FunctionKeyword::EaseOut => ease_out(), + FunctionKeyword::EaseInOut => ease_in_out(), + FunctionKeyword::StepStart => STEP_START, + FunctionKeyword::StepEnd => STEP_END, + } + }, + } + } + #[inline] + fn from_computed_value(computed: &ComputedTransitionTimingFunction) -> Self { + match *computed { + computed_value::TransitionTimingFunction::CubicBezier(p1, p2) => { + TransitionTimingFunction::CubicBezier(p1, p2) + }, + computed_value::TransitionTimingFunction::Steps(count, start_end) => { + TransitionTimingFunction::Steps(count, start_end) + }, + } + } + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.0.is_empty() { + return dest.write_str("none") + } + for (i, value) in self.0.iter().enumerate() { + if i != 0 { + try!(dest.write_str(", ")) + } + try!(value.to_css(dest)) + } + Ok(()) + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + computed_value::T(self.0.iter().map(|f| f.to_computed_value(context)).collect()) + } + #[inline] + fn from_computed_value(computed: &computed_value::T) -> Self { + SpecifiedValue(computed.0.iter().map(|f| ToComputedValue::from_computed_value(f)) + .collect()) + } + } + use values::NoViewportPercentage; impl NoViewportPercentage for SpecifiedValue {} - impl ComputedValueAsSpecified for SpecifiedValue {} - #[inline] - pub fn get_initial_single_value() -> TransitionTimingFunction { + pub fn get_initial_single_value() -> ComputedTransitionTimingFunction { ease() } + #[inline] + pub fn get_initial_specified_single_value() -> TransitionTimingFunction { + ToComputedValue::from_computed_value(&ease()) + } + #[inline] pub fn get_initial_value() -> computed_value::T { computed_value::T(vec![get_initial_single_value()]) @@ -848,7 +977,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", spec="https://drafts.csswg.org/css-animations/#propdef-animation-timing-function", allowed_in_keyframe_block="False"> pub use super::transition_timing_function::computed_value; - pub use super::transition_timing_function::{get_initial_value, get_initial_single_value, parse}; + pub use super::transition_timing_function::{get_initial_value, get_initial_single_value}; + pub use super::transition_timing_function::{get_initial_specified_single_value, parse}; pub use super::transition_timing_function::SpecifiedValue; pub use super::transition_timing_function::SingleSpecifiedValue; diff --git a/components/style/properties/shorthand/box.mako.rs b/components/style/properties/shorthand/box.mako.rs index c836fe3a01bb..cd5eb1ba4ce9 100644 --- a/components/style/properties/shorthand/box.mako.rs +++ b/components/style/properties/shorthand/box.mako.rs @@ -129,7 +129,7 @@ macro_rules! try_parse_one { transition_duration: duration.unwrap_or_else(transition_duration::get_initial_single_value), transition_timing_function: - timing_function.unwrap_or_else(transition_timing_function::get_initial_single_value), + timing_function.unwrap_or_else(transition_timing_function::get_initial_specified_single_value), transition_delay: delay.unwrap_or_else(transition_delay::get_initial_single_value), }) @@ -240,7 +240,7 @@ macro_rules! try_parse_one { animation_duration: duration.unwrap_or_else(animation_duration::get_initial_single_value), animation_timing_function: - timing_function.unwrap_or_else(animation_timing_function::get_initial_single_value), + timing_function.unwrap_or_else(animation_timing_function::get_initial_specified_single_value), animation_delay: delay.unwrap_or_else(animation_delay::get_initial_single_value), animation_iteration_count: