author | Anthony Ramine <n.oxyde@gmail.com> |
Mon, 05 Jun 2017 13:38:01 -0700 | |
changeset 362421 | 0cf5121dfb27b707912e80851c93091dd00418ac |
parent 362420 | 975ca096c5d96728a6b35886c35c3dd87e3c7da1 |
child 362422 | a7e36fe5ac723903f95532b93a1af63c4a1efc3b |
push id | 31977 |
push user | archaeopteryx@coole-files.de |
push date | Tue, 06 Jun 2017 09:17:27 +0000 |
treeherder | mozilla-central@d3b8e8571020 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | emilio |
milestone | 55.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/servo/components/style/animation.rs +++ b/servo/components/style/animation.rs @@ -11,24 +11,24 @@ use context::SharedStyleContext; use dom::OpaqueNode; use euclid::point::Point2D; use font_metrics::FontMetricsProvider; use properties::{self, CascadeFlags, ComputedValues, Importance}; use properties::animated_properties::{AnimatedProperty, TransitionProperty}; use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; use properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount; use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState; -use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd; -use properties::longhands::transition_timing_function::single_value::computed_value::T as TransitionTimingFunction; use rule_tree::CascadeLevel; use std::sync::mpsc::Sender; use stylearc::Arc; use stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue}; use timer::Timer; use values::computed::Time; +use values::computed::transform::TimingFunction; +use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction}; /// This structure represents a keyframes animation current iteration state. /// /// If the iteration count is infinite, there's no other state, otherwise we /// have to keep track the current iteration and the max iteration count. #[derive(Debug, Clone)] pub enum KeyframesIterationState { /// Infinite iterations, so no need to track a state. @@ -253,17 +253,17 @@ pub struct AnimationFrame { /// case (a number between 0 and 1), or absolute in the transition case. pub duration: f64, } /// Represents an animation for a given property. #[derive(Debug, Clone)] pub struct PropertyAnimation { property: AnimatedProperty, - timing_function: TransitionTimingFunction, + timing_function: TimingFunction, duration: Time, // TODO: isn't this just repeated? } impl PropertyAnimation { /// Returns the given property name. pub fn property_name(&self) -> &'static str { self.property.name() } @@ -318,17 +318,17 @@ impl PropertyAnimation { result.push(property_animation) } }); result } fn from_transition_property(transition_property: &TransitionProperty, - timing_function: TransitionTimingFunction, + timing_function: TimingFunction, duration: Time, old_style: &ComputedValues, new_style: &ComputedValues) -> Option<PropertyAnimation> { debug_assert!(!transition_property.is_shorthand() && transition_property != &TransitionProperty::All); let animated_property = AnimatedProperty::from_transition_property(transition_property, old_style, @@ -344,52 +344,53 @@ impl PropertyAnimation { Some(property_animation) } else { None } } /// Update the given animation at a given point of progress. pub fn update(&self, style: &mut ComputedValues, time: f64) { - let timing_function = match self.timing_function { - TransitionTimingFunction::Keyword(keyword) => - keyword.to_non_keyword_value(), - other => other, + let solve_bezier = |(p1, p2): (Point2D<_>, Point2D<_>)| { + let epsilon = 1. / (200. * (self.duration.seconds() as f64)); + let bezier = Bezier::new( + Point2D::new(p1.x as f64, p1.y as f64), + Point2D::new(p2.x as f64, p2.y as f64), + ); + bezier.solve(time, epsilon) }; - let progress = match timing_function { - TransitionTimingFunction::CubicBezier(p1, p2) => { - // See `WebCore::AnimationBase::solveEpsilon(double)` in WebKit. - let epsilon = 1.0 / (200.0 * (self.duration.seconds() as f64)); - Bezier::new(Point2D::new(p1.x as f64, p1.y as f64), - Point2D::new(p2.x as f64, p2.y as f64)).solve(time, epsilon) + + let progress = match self.timing_function { + GenericTimingFunction::CubicBezier(p1, p2) => { + solve_bezier((p1, p2)) }, - TransitionTimingFunction::Steps(steps, StartEnd::Start) => { + GenericTimingFunction::Steps(steps, StepPosition::Start) => { (time * (steps as f64)).ceil() / (steps as f64) }, - TransitionTimingFunction::Steps(steps, StartEnd::End) => { + GenericTimingFunction::Steps(steps, StepPosition::End) => { (time * (steps as f64)).floor() / (steps as f64) }, - TransitionTimingFunction::Frames(frames) => { + GenericTimingFunction::Frames(frames) => { // https://drafts.csswg.org/css-timing/#frames-timing-functions let mut out = (time * (frames as f64)).floor() / ((frames - 1) as f64); if out > 1.0 { // FIXME: Basically, during the animation sampling process, the input progress // should be in the range of [0, 1]. However, |time| is not accurate enough // here, which means |time| could be larger than 1.0 in the last animation // frame. (It should be equal to 1.0 exactly.) This makes the output of frames // timing function jumps to the next frame/level. // However, this solution is still not correct because |time| is possible // outside the range of [0, 1] after introducing Web Animations. We should fix // this problem when implementing web animations. out = 1.0; } out }, - TransitionTimingFunction::Keyword(_) => { - panic!("Keyword function should not appear") + GenericTimingFunction::Keyword(keyword) => { + solve_bezier(keyword.to_bezier_points()) }, }; self.property.update(style, progress); } #[inline] fn does_animate(&self) -> bool {
--- a/servo/components/style/gecko_bindings/sugar/ns_timing_function.rs +++ b/servo/components/style/gecko_bindings/sugar/ns_timing_function.rs @@ -1,20 +1,19 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use euclid::point::{Point2D, TypedPoint2D}; use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type}; -use properties::longhands::transition_timing_function::single_value::FunctionKeyword; -use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction; -use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd; -use properties::longhands::transition_timing_function::single_value::computed_value::T as ComputedTimingFunction; use std::mem; use values::computed::ToComputedValue; +use values::computed::transform::TimingFunction as ComputedTimingFunction; +use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction, TimingKeyword}; +use values::specified::transform::TimingFunction; impl nsTimingFunction { fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: u32) { debug_assert!(function_type == nsTimingFunction_Type::StepStart || function_type == nsTimingFunction_Type::StepEnd, "function_type should be step-start or step-end"); self.mType = function_type; unsafe { @@ -42,119 +41,97 @@ impl nsTimingFunction { gecko_cubic_bezier.mX2 = p2.x; gecko_cubic_bezier.mY2 = p2.y; } } } impl From<ComputedTimingFunction> for nsTimingFunction { fn from(function: ComputedTimingFunction) -> nsTimingFunction { - SpecifiedTimingFunction::from_computed_value(&function).into() + TimingFunction::from_computed_value(&function).into() } } -impl From<SpecifiedTimingFunction> for nsTimingFunction { - fn from(function: SpecifiedTimingFunction) -> nsTimingFunction { +impl From<TimingFunction> for nsTimingFunction { + fn from(function: TimingFunction) -> nsTimingFunction { let mut tf: nsTimingFunction = unsafe { mem::zeroed() }; match function { - SpecifiedTimingFunction::Steps(steps, StartEnd::Start) => { + GenericTimingFunction::Steps(steps, StepPosition::Start) => { debug_assert!(steps.value() >= 0); tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value() as u32); }, - SpecifiedTimingFunction::Steps(steps, StartEnd::End) => { + GenericTimingFunction::Steps(steps, StepPosition::End) => { debug_assert!(steps.value() >= 0); tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value() as u32); }, - SpecifiedTimingFunction::Frames(frames) => { + GenericTimingFunction::Frames(frames) => { debug_assert!(frames.value() >= 2); tf.set_as_frames(frames.value() as u32); }, - SpecifiedTimingFunction::CubicBezier(p1, p2) => { + GenericTimingFunction::CubicBezier(p1, p2) => { tf.set_as_bezier(nsTimingFunction_Type::CubicBezier, Point2D::new(p1.x.get(), p1.y.get()), Point2D::new(p2.x.get(), p2.y.get())); }, - SpecifiedTimingFunction::Keyword(keyword) => { - match keyword.to_non_keyword_value() { - ComputedTimingFunction::CubicBezier(p1, p2) => { - match keyword { - FunctionKeyword::Ease => { - tf.set_as_bezier(nsTimingFunction_Type::Ease, p1, p2); - }, - FunctionKeyword::Linear => { - tf.set_as_bezier(nsTimingFunction_Type::Linear, p1, p2); - }, - FunctionKeyword::EaseIn => { - tf.set_as_bezier(nsTimingFunction_Type::EaseIn, p1, p2); - }, - FunctionKeyword::EaseOut => { - tf.set_as_bezier(nsTimingFunction_Type::EaseOut, p1, p2); - }, - FunctionKeyword::EaseInOut => { - tf.set_as_bezier(nsTimingFunction_Type::EaseInOut, p1, p2); - }, - _ => unreachable!("Unexpected bezier function type"), - } - }, - ComputedTimingFunction::Steps(steps, StartEnd::Start) => { - debug_assert!(keyword == FunctionKeyword::StepStart && steps == 1); - tf.set_as_step(nsTimingFunction_Type::StepStart, steps); - }, - ComputedTimingFunction::Steps(steps, StartEnd::End) => { - debug_assert!(keyword == FunctionKeyword::StepEnd && steps == 1); - tf.set_as_step(nsTimingFunction_Type::StepEnd, steps); - }, - ComputedTimingFunction::Frames(frames) => { - tf.set_as_frames(frames) - }, - ComputedTimingFunction::Keyword(_) => { - panic!("Keyword function should not appear") - }, - } + GenericTimingFunction::Keyword(keyword) => { + let (p1, p2) = keyword.to_bezier_points(); + tf.set_as_bezier(keyword.into(), p1, p2) }, } tf } } impl From<nsTimingFunction> for ComputedTimingFunction { fn from(function: nsTimingFunction) -> ComputedTimingFunction { match function.mType { nsTimingFunction_Type::StepStart => { - ComputedTimingFunction::Steps( + GenericTimingFunction::Steps( unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames }, - StartEnd::Start) + StepPosition::Start) }, nsTimingFunction_Type::StepEnd => { - ComputedTimingFunction::Steps( + GenericTimingFunction::Steps( unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames }, - StartEnd::End) + StepPosition::End) }, nsTimingFunction_Type::Frames => { - ComputedTimingFunction::Frames( + GenericTimingFunction::Frames( unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames }) } nsTimingFunction_Type::Ease => { - ComputedTimingFunction::Keyword(FunctionKeyword::Ease) + GenericTimingFunction::Keyword(TimingKeyword::Ease) }, nsTimingFunction_Type::Linear => { - ComputedTimingFunction::Keyword(FunctionKeyword::Linear) + GenericTimingFunction::Keyword(TimingKeyword::Linear) }, nsTimingFunction_Type::EaseIn => { - ComputedTimingFunction::Keyword(FunctionKeyword::EaseIn) + GenericTimingFunction::Keyword(TimingKeyword::EaseIn) }, nsTimingFunction_Type::EaseOut => { - ComputedTimingFunction::Keyword(FunctionKeyword::EaseOut) + GenericTimingFunction::Keyword(TimingKeyword::EaseOut) }, nsTimingFunction_Type::EaseInOut => { - ComputedTimingFunction::Keyword(FunctionKeyword::EaseInOut) + GenericTimingFunction::Keyword(TimingKeyword::EaseInOut) }, nsTimingFunction_Type::CubicBezier => { - ComputedTimingFunction::CubicBezier( + GenericTimingFunction::CubicBezier( TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX1 }, unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY1 }), TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX2 }, unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY2 })) }, } } } + +impl From<TimingKeyword> for nsTimingFunction_Type { + fn from(keyword: TimingKeyword) -> Self { + match keyword { + TimingKeyword::Linear => nsTimingFunction_Type::Linear, + TimingKeyword::Ease => nsTimingFunction_Type::Ease, + TimingKeyword::EaseIn => nsTimingFunction_Type::EaseIn, + TimingKeyword::EaseOut => nsTimingFunction_Type::EaseOut, + TimingKeyword::EaseInOut => nsTimingFunction_Type::EaseInOut, + } + } +}
--- a/servo/components/style/properties/longhand/box.mako.rs +++ b/servo/components/style/properties/longhand/box.mako.rs @@ -409,346 +409,25 @@ initial_specified_value="specified::Time::zero()", parse_method="parse_non_negative", vector=True, need_index=True, animation_value_type="none", extra_prefixes="moz webkit", spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration")} -// TODO(pcwalton): Lots more timing functions. -<%helpers:vector_longhand name="transition-timing-function" - need_index="True" - animation_value_type="none" - 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 style_traits::ToCss; - - // FIXME: This could use static variables and const functions when they are available. - #[inline(always)] - fn ease() -> computed_value::T { - computed_value::T::CubicBezier(TypedPoint2D::new(0.25, 0.1), - 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)) - } - - #[inline(always)] - fn ease_in() -> computed_value::T { - computed_value::T::CubicBezier(TypedPoint2D::new(0.42, 0.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)) - } - - #[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)) - } - - static STEP_START: computed_value::T = - computed_value::T::Steps(1, StartEnd::Start); - static STEP_END: computed_value::T = - computed_value::T::Steps(1, StartEnd::End); - - pub mod computed_value { - use euclid::point::Point2D; - use std::fmt; - use style_traits::ToCss; - use super::FunctionKeyword; - use values::specified; - - pub use super::parse; - - #[derive(Copy, Clone, Debug, PartialEq)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub enum T { - CubicBezier(Point2D<f32>, Point2D<f32>), - Steps(u32, StartEnd), - Frames(u32), - Keyword(FunctionKeyword), - } - - impl ToCss for T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result - where W: fmt::Write, - { - match *self { - T::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(")") - }, - T::Steps(steps, start_end) => { - super::serialize_steps(dest, specified::Integer::new(steps as i32), start_end) - }, - T::Frames(frames) => { - try!(dest.write_str("frames(")); - try!(frames.to_css(dest)); - dest.write_str(")") - }, - T::Keyword(keyword) => { - super::serialize_keyword(dest, keyword) - } - } - } - } - - #[derive(Copy, Clone, Debug, PartialEq)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub enum StartEnd { - Start, - End, - } - - impl ToCss for StartEnd { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result - where W: fmt::Write, - { - match *self { - StartEnd::Start => dest.write_str("start"), - StartEnd::End => dest.write_str("end"), - } - } - } - } - - 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 SpecifiedValue { - CubicBezier(Point2D<Number>, Point2D<Number>), - Steps(specified::Integer, StartEnd), - Frames(specified::Integer), - Keyword(FunctionKeyword), - } - - impl Parse for SpecifiedValue { - fn parse(context: &ParserContext, input: &mut ::cssparser::Parser) -> Result<Self, ()> { - 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) = - (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(context, input)); - try!(input.expect_comma()); - p1y = try!(specified::parse_number(context, input)); - try!(input.expect_comma()); - p2x = try!(specified::parse_number(context, input)); - try!(input.expect_comma()); - p2y = try!(specified::parse_number(context, input)); - Ok(()) - })); - if p1x.get() < 0.0 || p1x.get() > 1.0 || - p2x.get() < 0.0 || p2x.get() > 1.0 { - return Err(()) - } - - let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y)); - Ok(SpecifiedValue::CubicBezier(p1, p2)) - }, - "steps" => { - let (mut step_count, mut start_end) = (specified::Integer::new(0), StartEnd::End); - try!(input.parse_nested_block(|input| { - step_count = try!(specified::parse_integer(context, input)); - if step_count.value() < 1 { - return Err(()) - } - - 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(SpecifiedValue::Steps(step_count, start_end)) - }, - "frames" => { - // https://drafts.csswg.org/css-timing/#frames-timing-functions - let frames = try!(input.parse_nested_block(|input| { - specified::Integer::parse_with_minimum(context, input, 2) - })); - Ok(SpecifiedValue::Frames(frames)) - }, - _ => Err(()) - } - } - Ok(SpecifiedValue::Keyword(try!(FunctionKeyword::parse(input)))) - } - } - - fn serialize_steps<W>(dest: &mut W, - steps: specified::Integer, - 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(")") - } - - fn serialize_keyword<W>(dest: &mut W, keyword: FunctionKeyword) -> fmt::Result - where W: fmt::Write, - { - match keyword { - FunctionKeyword::StepStart => { - serialize_steps(dest, specified::Integer::new(1), StartEnd::Start) - }, - FunctionKeyword::StepEnd => { - serialize_steps(dest, specified::Integer::new(1), StartEnd::End) - }, - _ => { - keyword.to_css(dest) - }, - } - } - - // https://drafts.csswg.org/css-transitions/#serializing-a-timing-function - impl ToCss for SpecifiedValue { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - SpecifiedValue::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(")") - }, - SpecifiedValue::Steps(steps, start_end) => { - serialize_steps(dest, steps, start_end) - }, - SpecifiedValue::Frames(frames) => { - try!(dest.write_str("frames(")); - try!(frames.to_css(dest)); - dest.write_str(")") - }, - SpecifiedValue::Keyword(keyword) => { - serialize_keyword(dest, keyword) - }, - } - } - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value(&self, context: &Context) -> computed_value::T { - match *self { - SpecifiedValue::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.to_computed_value(context) as u32, start_end) - }, - SpecifiedValue::Frames(frames) => { - computed_value::T::Frames(frames.to_computed_value(context) as u32) - }, - SpecifiedValue::Keyword(keyword) => { - computed_value::T::Keyword(keyword) - }, - } - } - #[inline] - fn from_computed_value(computed: &computed_value::T) -> Self { - match *computed { - computed_value::T::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) => { - let int_count = count as i32; - SpecifiedValue::Steps(specified::Integer::from_computed_value(&int_count), start_end) - }, - computed_value::T::Frames(frames) => { - let frames = frames as i32; - SpecifiedValue::Frames(specified::Integer::from_computed_value(&frames)) - }, - computed_value::T::Keyword(keyword) => { - SpecifiedValue::Keyword(keyword) - }, - } - } - } - - impl FunctionKeyword { - #[inline] - pub fn to_non_keyword_value(&self) -> computed_value::T { - match *self { - 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, - } - } - } - - no_viewport_percentage!(SpecifiedValue); - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T::Keyword(FunctionKeyword::Ease) - } - - #[inline] - pub fn get_initial_specified_value() -> SpecifiedValue { - SpecifiedValue::Keyword(FunctionKeyword::Ease) - } - - pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { - SpecifiedValue::parse(context, input) - } -</%helpers:vector_longhand> +${helpers.predefined_type("transition-timing-function", + "TimingFunction", + "computed::TimingFunction::ease()", + initial_specified_value="specified::TimingFunction::ease()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes="moz webkit", + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function")} <%helpers:vector_longhand name="transition-property" allow_empty="True" need_index="True" animation_value_type="none" extra_prefixes="moz webkit" spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property">
--- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -34,17 +34,17 @@ pub use super::specified::{AlignItems, A pub use super::specified::{BorderStyle, Percentage, UrlOrNone}; pub use super::generics::grid::GridLine; pub use super::specified::url::SpecifiedUrl; pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; pub use self::length::{MaxLength, MozLength}; pub use self::position::Position; pub use self::text::{LetterSpacing, LineHeight, WordSpacing}; -pub use self::transform::TransformOrigin; +pub use self::transform::{TimingFunction, TransformOrigin}; pub mod background; pub mod basic_shape; pub mod border; pub mod image; pub mod length; pub mod position; pub mod rect;
--- a/servo/components/style/values/computed/transform.rs +++ b/servo/components/style/values/computed/transform.rs @@ -1,21 +1,25 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Computed types for CSS values that are related to transformations. use properties::animated_properties::Animatable; -use values::computed::{Length, LengthOrPercentage}; +use values::computed::{Length, LengthOrPercentage, Number}; +use values::generics::transform::TimingFunction as GenericTimingFunction; use values::generics::transform::TransformOrigin as GenericTransformOrigin; /// The computed value of a CSS `<transform-origin>` pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>; +/// A computed timing function. +pub type TimingFunction = GenericTimingFunction<u32, Number>; + impl TransformOrigin { /// Returns the initial computed value for `transform-origin`. #[inline] pub fn initial_value() -> Self { Self::new( LengthOrPercentage::Percentage(0.5), LengthOrPercentage::Percentage(0.5), Length::from_px(0)
--- a/servo/components/style/values/generics/transform.rs +++ b/servo/components/style/values/generics/transform.rs @@ -1,28 +1,130 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Generic types for CSS values that are related to transformations. +use euclid::Point2D; +use std::fmt; +use style_traits::{HasViewportPercentage, ToCss}; +use values::CSSFloat; + /// A generic transform origin. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)] pub struct TransformOrigin<H, V, Depth> { /// The horizontal origin. pub horizontal: H, /// The vertical origin. pub vertical: V, /// The depth. pub depth: Depth, } +/// A generic timing function. +/// +/// https://drafts.csswg.org/css-timing-1/#single-timing-function-production +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum TimingFunction<Integer, Number> { + /// `linear | ease | ease-in | ease-out | ease-in-out` + Keyword(TimingKeyword), + /// `cubic-bezier(<number>, <number>, <number>, <number>)` + CubicBezier(Point2D<Number>, Point2D<Number>), + /// `step-start | step-end | steps(<integer>, [ start | end ]?)` + Steps(Integer, StepPosition), + /// `frames(<integer>)` + Frames(Integer), +} + +impl<I, N> HasViewportPercentage for TimingFunction<I, N> { + fn has_viewport_percentage(&self) -> bool { false } +} + +define_css_keyword_enum! { TimingKeyword: + "linear" => Linear, + "ease" => Ease, + "ease-in" => EaseIn, + "ease-out" => EaseOut, + "ease-in-out" => EaseInOut, +} +add_impls_for_keyword_enum!(TimingKeyword); + +define_css_keyword_enum! { StepPosition: + "start" => Start, + "end" => End, +} +add_impls_for_keyword_enum!(StepPosition); + impl<H, V, D> TransformOrigin<H, V, D> { /// Returns a new transform origin. pub fn new(horizontal: H, vertical: V, depth: D) -> Self { Self { horizontal: horizontal, vertical: vertical, depth: depth, } } } + +impl<Integer, Number> TimingFunction<Integer, Number> { + /// `ease` + #[inline] + pub fn ease() -> Self { + TimingFunction::Keyword(TimingKeyword::Ease) + } +} + +impl<Integer, Number> ToCss for TimingFunction<Integer, Number> +where + Integer: ToCss, + Number: ToCss, +{ + fn to_css<W>(&self, dest: &mut W) -> fmt::Result + where + W: fmt::Write, + { + match *self { + TimingFunction::Keyword(keyword) => keyword.to_css(dest), + TimingFunction::CubicBezier(ref p1, ref p2) => { + dest.write_str("cubic-bezier(")?; + p1.x.to_css(dest)?; + dest.write_str(", ")?; + p1.y.to_css(dest)?; + dest.write_str(", ")?; + p2.x.to_css(dest)?; + dest.write_str(", ")?; + p2.y.to_css(dest)?; + dest.write_str(")") + }, + TimingFunction::Steps(ref intervals, position) => { + dest.write_str("steps(")?; + intervals.to_css(dest)?; + if position != StepPosition::End { + dest.write_str(", ")?; + position.to_css(dest)?; + } + dest.write_str(")") + }, + TimingFunction::Frames(ref frames) => { + dest.write_str("frames(")?; + frames.to_css(dest)?; + dest.write_str(")") + }, + } + } +} + +impl TimingKeyword { + /// Returns this timing keyword as a pair of `cubic-bezier()` points. + #[inline] + pub fn to_bezier_points(self) -> (Point2D<CSSFloat>, Point2D<CSSFloat>) { + match self { + TimingKeyword::Linear => (Point2D::new(0., 0.), Point2D::new(1., 1.)), + TimingKeyword::Ease => (Point2D::new(0.25, 0.1), Point2D::new(0.25, 1.)), + TimingKeyword::EaseIn => (Point2D::new(0.42, 0.), Point2D::new(1., 1.)), + TimingKeyword::EaseOut => (Point2D::new(0., 0.), Point2D::new(0.58, 1.)), + TimingKeyword::EaseInOut => (Point2D::new(0.42, 0.), Point2D::new(0.58, 1.)), + } + } +}
--- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -39,17 +39,17 @@ pub use self::image::{ColorStop, EndingS pub use self::image::{GradientItem, GradientKind, Image, ImageRect, ImageLayer}; pub use self::length::AbsoluteLength; pub use self::length::{FontRelativeLength, ViewportPercentageLength, CharacterWidth, Length, CalcLengthOrPercentage}; pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{LengthOrPercentageOrNone, LengthOrPercentageOrAutoOrContent, NoCalcLength}; pub use self::length::{MaxLength, MozLength}; pub use self::position::{Position, PositionComponent}; pub use self::text::{LetterSpacing, LineHeight, WordSpacing}; -pub use self::transform::TransformOrigin; +pub use self::transform::{TimingFunction, TransformOrigin}; #[cfg(feature = "gecko")] pub mod align; pub mod background; pub mod basic_shape; pub mod border; pub mod calc; pub mod color;
--- a/servo/components/style/values/specified/transform.rs +++ b/servo/components/style/values/specified/transform.rs @@ -1,20 +1,24 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Specified types for CSS values that are related to transformations. use cssparser::Parser; +use euclid::Point2D; use parser::{Parse, ParserContext}; use std::fmt; use style_traits::ToCss; use values::computed::{LengthOrPercentage as ComputedLengthOrPercentage, Context, ToComputedValue}; -use values::generics::transform::TransformOrigin as GenericTransformOrigin; +use values::computed::transform::TimingFunction as ComputedTimingFunction; +use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction}; +use values::generics::transform::{TimingKeyword, TransformOrigin as GenericTransformOrigin}; +use values::specified::{Integer, Number}; use values::specified::length::{Length, LengthOrPercentage}; use values::specified::position::{Side, X, Y}; /// The specified value of a CSS `<transform-origin>` pub type TransformOrigin = GenericTransformOrigin<OriginComponent<X>, OriginComponent<Y>, Length>; /// The specified value of a component of a CSS `<transform-origin>`. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -23,16 +27,19 @@ pub enum OriginComponent<S> { /// `center` Center, /// `<lop>` Length(LengthOrPercentage), /// `<side>` Side(S), } +/// A specified timing function. +pub type TimingFunction = GenericTimingFunction<Integer, Number>; + impl Parse for TransformOrigin { fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { let parse_depth = |input: &mut Parser| { input.try(|i| Length::parse(context, i)).unwrap_or(Length::from_px(0.)) }; match input.try(|i| OriginComponent::parse(context, i)) { Ok(x_origin @ OriginComponent::Center) => { if let Ok(y_origin) = input.try(|i| OriginComponent::parse(context, i)) { @@ -125,8 +132,126 @@ impl<S> ToComputedValue for OriginCompon }, } } fn from_computed_value(computed: &Self::ComputedValue) -> Self { OriginComponent::Length(ToComputedValue::from_computed_value(computed)) } } + +impl Parse for TimingFunction { + fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + if let Ok(keyword) = input.try(TimingKeyword::parse) { + return Ok(GenericTimingFunction::Keyword(keyword)); + } + if let Ok(ident) = input.try(|i| i.expect_ident()) { + let position = match_ignore_ascii_case! { &ident, + "step-start" => StepPosition::Start, + "step-end" => StepPosition::End, + _ => return Err(()), + }; + return Ok(GenericTimingFunction::Steps(Integer::new(1), position)); + } + let function = input.expect_function()?; + input.parse_nested_block(|i| { + match_ignore_ascii_case! { &function, + "cubic-bezier" => { + let p1x = Number::parse(context, i)?; + i.expect_comma()?; + let p1y = Number::parse(context, i)?; + i.expect_comma()?; + let p2x = Number::parse(context, i)?; + i.expect_comma()?; + let p2y = Number::parse(context, i)?; + + if p1x.get() < 0.0 || p1x.get() > 1.0 || p2x.get() < 0.0 || p2x.get() > 1.0 { + return Err(()); + } + + let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y)); + Ok(GenericTimingFunction::CubicBezier(p1, p2)) + }, + "steps" => { + let steps = Integer::parse_positive(context, i)?; + let position = i.try(|i| { + i.expect_comma()?; + StepPosition::parse(i) + }).unwrap_or(StepPosition::End); + Ok(GenericTimingFunction::Steps(steps, position)) + }, + "frames" => { + let frames = Integer::parse_with_minimum(context, i, 2)?; + Ok(GenericTimingFunction::Frames(frames)) + }, + _ => Err(()), + } + }) + } +} + +impl ToComputedValue for TimingFunction { + type ComputedValue = ComputedTimingFunction; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + GenericTimingFunction::Keyword(keyword) => { + GenericTimingFunction::Keyword(keyword) + }, + GenericTimingFunction::CubicBezier(p1, p2) => { + GenericTimingFunction::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), + ), + ) + }, + GenericTimingFunction::Steps(steps, position) => { + GenericTimingFunction::Steps( + steps.to_computed_value(context) as u32, + position, + ) + }, + GenericTimingFunction::Frames(frames) => { + GenericTimingFunction::Frames( + frames.to_computed_value(context) as u32, + ) + }, + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + GenericTimingFunction::Keyword(keyword) => { + GenericTimingFunction::Keyword(keyword) + }, + GenericTimingFunction::CubicBezier(p1, p2) => { + GenericTimingFunction::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), + ), + ) + }, + GenericTimingFunction::Steps(steps, position) => { + GenericTimingFunction::Steps( + Integer::from_computed_value(&(steps as i32)), + position, + ) + }, + GenericTimingFunction::Frames(frames) => { + GenericTimingFunction::Frames( + Integer::from_computed_value(&(frames as i32)), + ) + }, + } + } +}