Bug 1496619 - part 6: Generate StyleTimingFunction and drop ns_timing_function.rs r=emilio,birtles
authorBoris Chiou <boris.chiou@gmail.com>
Fri, 26 Oct 2018 18:03:35 +0000
changeset 502451 79675a8112d6e66dd4c1d17985ff2c70f0378ee2
parent 502450 5b744b960fe0280854c3129aa26b7894083ef632
child 502452 77d9e79ed3b9e2a52d635068107d1f2c28369bf9
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio, birtles
bugs1496619
milestone65.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
Bug 1496619 - part 6: Generate StyleTimingFunction and drop ns_timing_function.rs r=emilio,birtles First, we generate StyleComputedTimingFunction by cbindgen from Rust, and use it in nsTimingFunction, so we could copy it directly without handling the different memory layout. However, we have to rewrite the nsTimingFunction and mozilla::ComputedTimingFunction for this. Second, the rust-bindgen seems cannot generate the correct generic members from complex C++ templates, especially for the nested template struct, (https://github.com/rust-lang-nursery/rust-bindgen/issues/1429) So we have to hide StyleTimingFunction to avoid the compilation errors. Depends on D9312 Differential Revision: https://phabricator.services.mozilla.com/D9313
dom/animation/ComputedTimingFunction.cpp
dom/animation/ComputedTimingFunction.h
dom/animation/TimingParams.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/ipc/LayerAnimationUtils.cpp
gfx/layers/ipc/LayersMessages.ipdlh
layout/painting/nsDisplayList.cpp
layout/style/GeckoBindings.cpp
layout/style/ServoBindings.toml
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleUtil.cpp
layout/style/nsStyleUtil.h
layout/style/nsTimingFunction.h
layout/style/nsTransitionManager.cpp
servo/components/style/cbindgen.toml
servo/components/style/gecko_bindings/sugar/mod.rs
servo/components/style/gecko_bindings/sugar/ns_timing_function.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/values/computed/easing.rs
servo/components/style/values/generics/easing.rs
servo/components/style/values/specified/easing.rs
servo/ports/geckolib/glue.rs
--- a/dom/animation/ComputedTimingFunction.cpp
+++ b/dom/animation/ComputedTimingFunction.cpp
@@ -8,51 +8,75 @@
 #include "nsAlgorithm.h" // For clamped()
 #include "nsStyleUtil.h"
 
 namespace mozilla {
 
 void
 ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
 {
-  mType = aFunction.mType;
-  if (nsTimingFunction::IsSplineType(mType)) {
-    mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1,
-                         aFunction.mFunc.mX2, aFunction.mFunc.mY2);
-  } else {
-    mSteps = aFunction.mSteps;
+  const StyleComputedTimingFunction& timing = aFunction.mTiming;
+  switch (timing.tag) {
+    case StyleComputedTimingFunction::Tag::Keyword: {
+      mType = static_cast<Type>(static_cast<uint8_t>(timing.keyword._0));
+
+      static_assert(
+        static_cast<uint8_t>(StyleTimingKeyword::Linear) == 0 &&
+          static_cast<uint8_t>(StyleTimingKeyword::Ease) == 1 &&
+          static_cast<uint8_t>(StyleTimingKeyword::EaseIn) == 2 &&
+          static_cast<uint8_t>(StyleTimingKeyword::EaseOut) == 3 &&
+          static_cast<uint8_t>(StyleTimingKeyword::EaseInOut) == 4,
+        "transition timing function constants not as expected");
+
+      static const float timingFunctionValues[5][4] = {
+        { 0.00f, 0.00f, 1.00f, 1.00f }, // linear
+        { 0.25f, 0.10f, 0.25f, 1.00f }, // ease
+        { 0.42f, 0.00f, 1.00f, 1.00f }, // ease-in
+        { 0.00f, 0.00f, 0.58f, 1.00f }, // ease-out
+        { 0.42f, 0.00f, 0.58f, 1.00f }  // ease-in-out
+      };
+      const float (&values)[4] = timingFunctionValues[uint8_t(mType)];
+      mTimingFunction.Init(values[0], values[1], values[2], values[3]);
+      break;
+    }
+    case StyleComputedTimingFunction::Tag::CubicBezier:
+      mType = Type::CubicBezier;
+      mTimingFunction.Init(timing.cubic_bezier.x1, timing.cubic_bezier.y1,
+                           timing.cubic_bezier.x2, timing.cubic_bezier.y2);
+      break;
+    case StyleComputedTimingFunction::Tag::Steps:
+      mType = Type::Step;
+      mSteps.mSteps = static_cast<uint32_t>(timing.steps._0);
+      mSteps.mPos = timing.steps._1;
+      break;
   }
 }
 
 static inline double
-StepTiming(uint32_t aSteps,
+StepTiming(const ComputedTimingFunction::StepFunc& aStepFunc,
            double aPortion,
-           ComputedTimingFunction::BeforeFlag aBeforeFlag,
-           nsTimingFunction::Type aType)
+           ComputedTimingFunction::BeforeFlag aBeforeFlag)
 {
-  MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
-             aType == nsTimingFunction::Type::StepEnd, "invalid type");
-
   // Calculate current step using step-end behavior
-  int32_t step = floor(aPortion * aSteps);
+  int32_t step = floor(aPortion * aStepFunc.mSteps);
 
   // step-start is one step ahead
-  if (aType == nsTimingFunction::Type::StepStart) {
+  if (aStepFunc.mPos == StyleStepPosition::Start) {
     step++;
   }
 
   // If the "before flag" is set and we are at a transition point,
   // drop back a step
   if (aBeforeFlag == ComputedTimingFunction::BeforeFlag::Set &&
-      fmod(aPortion * aSteps, 1) == 0) {
+      fmod(aPortion * aStepFunc.mSteps, 1) == 0) {
     step--;
   }
 
   // Convert to a progress value
-  double result = double(step) / double(aSteps);
+  double result = double(step) / double(aStepFunc.mSteps);
 
   // We should not produce a result outside [0, 1] unless we have an
   // input outside that range. This takes care of steps that would otherwise
   // occur at boundaries.
   if (result < 0.0 && aPortion >= 0.0) {
     return 0.0;
   }
   if (result > 1.0 && aPortion <= 1.0) {
@@ -107,80 +131,81 @@ ComputedTimingFunction::GetValue(
       }
       // If we can't calculate a sensible tangent, don't extrapolate at all.
       return 1.0;
     }
 
     return mTimingFunction.GetSplineValue(aPortion);
   }
 
-  return StepTiming(mSteps, aPortion, aBeforeFlag, mType);
+  return StepTiming(mSteps, aPortion, aBeforeFlag);
 }
 
 int32_t
 ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const
 {
   if (mType != aRhs.mType) {
     return int32_t(mType) - int32_t(aRhs.mType);
   }
 
-  if (mType == nsTimingFunction::Type::CubicBezier) {
+  if (mType == Type::CubicBezier) {
     int32_t order = mTimingFunction.Compare(aRhs.mTimingFunction);
     if (order != 0) {
       return order;
     }
-  } else if (mType == nsTimingFunction::Type::StepStart ||
-             mType == nsTimingFunction::Type::StepEnd) {
-    if (mSteps != aRhs.mSteps) {
-      return int32_t(mSteps) - int32_t(aRhs.mSteps);
+  } else if (mType == Type::Step) {
+    if (mSteps.mPos != aRhs.mSteps.mPos) {
+      return int32_t(mSteps.mPos) - int32_t(aRhs.mSteps.mPos);
+    } else if (mSteps.mSteps != aRhs.mSteps.mSteps) {
+      return int32_t(mSteps.mSteps) - int32_t(aRhs.mSteps.mSteps);
     }
   }
 
   return 0;
 }
 
 void
 ComputedTimingFunction::AppendToString(nsAString& aResult) const
 {
   switch (mType) {
-    case nsTimingFunction::Type::CubicBezier:
+    case Type::CubicBezier:
       nsStyleUtil::AppendCubicBezierTimingFunction(mTimingFunction.X1(),
                                                    mTimingFunction.Y1(),
                                                    mTimingFunction.X2(),
                                                    mTimingFunction.Y2(),
                                                    aResult);
       break;
-    case nsTimingFunction::Type::StepStart:
-    case nsTimingFunction::Type::StepEnd:
-      nsStyleUtil::AppendStepsTimingFunction(mType, mSteps, aResult);
+    case Type::Step:
+      nsStyleUtil::AppendStepsTimingFunction(mSteps.mSteps,
+                                             mSteps.mPos,
+                                             aResult);
       break;
     default:
-      nsStyleUtil::AppendCubicBezierKeywordTimingFunction(mType, aResult);
+      nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
+        StyleTimingKeyword(uint8_t(mType)), aResult);
       break;
   }
 }
 
 /* static */ int32_t
 ComputedTimingFunction::Compare(const Maybe<ComputedTimingFunction>& aLhs,
                                 const Maybe<ComputedTimingFunction>& aRhs)
 {
   // We can't use |operator<| for const Maybe<>& here because
   // 'ease' is prior to 'linear' which is represented by Nothing().
   // So we have to convert Nothing() as 'linear' and check it first.
-  nsTimingFunction::Type lhsType = aLhs.isNothing() ?
-    nsTimingFunction::Type::Linear : aLhs->GetType();
-  nsTimingFunction::Type rhsType = aRhs.isNothing() ?
-    nsTimingFunction::Type::Linear : aRhs->GetType();
+  Type lhsType = aLhs.isNothing() ? Type::Linear : aLhs->GetType();
+  Type rhsType = aRhs.isNothing() ? Type::Linear : aRhs->GetType();
 
   if (lhsType != rhsType) {
     return int32_t(lhsType) - int32_t(rhsType);
   }
 
   // Both of them are Nothing().
-  if (lhsType == nsTimingFunction::Type::Linear) {
+  if (lhsType == Type::Linear) {
     return 0;
   }
 
   // Other types.
   return aLhs->Compare(aRhs.value());
 }
 
 } // namespace mozilla
--- a/dom/animation/ComputedTimingFunction.h
+++ b/dom/animation/ComputedTimingFunction.h
@@ -15,35 +15,49 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Maybe.h"
 
 namespace mozilla {
 
 class ComputedTimingFunction
 {
 public:
+  enum class Type: uint8_t {
+    Ease      = uint8_t(StyleTimingKeyword::Ease),         // ease
+    Linear    = uint8_t(StyleTimingKeyword::Linear),       // linear
+    EaseIn    = uint8_t(StyleTimingKeyword::EaseIn),       // ease-in
+    EaseOut   = uint8_t(StyleTimingKeyword::EaseOut),      // ease-out
+    EaseInOut = uint8_t(StyleTimingKeyword::EaseInOut),    // ease-in-out
+    CubicBezier,  // cubic-bezier()
+    Step,         // step-start | step-end | steps()
+  };
+
+  struct StepFunc {
+    uint32_t mSteps;
+    StyleStepPosition mPos;
+    bool operator==(const StepFunc& aOther) const
+    {
+      return mSteps == aOther.mSteps && mPos == aOther.mPos;
+    }
+  };
+
   static ComputedTimingFunction
   CubicBezier(double x1, double y1, double x2, double y2)
   {
     return ComputedTimingFunction(x1, y1, x2, y2);
   }
   static ComputedTimingFunction
-  Steps(nsTimingFunction::Type aType, uint32_t aSteps)
+  Steps(uint32_t aSteps, StyleStepPosition aPos)
   {
-    MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
-               aType == nsTimingFunction::Type::StepEnd,
-               "The type of timing function should be either step-start or "
-               "step-end");
     MOZ_ASSERT(aSteps > 0, "The number of steps should be 1 or more");
-    return ComputedTimingFunction(aType, aSteps);
+    return ComputedTimingFunction(aSteps, aPos);
   }
 
   ComputedTimingFunction() = default;
   explicit ComputedTimingFunction(const nsTimingFunction& aFunction)
-    : mSteps(0)
   {
     Init(aFunction);
   }
   void Init(const nsTimingFunction& aFunction);
 
   // BeforeFlag is used in step timing function.
   // https://drafts.csswg.org/css-easing/#before-flag
   enum class BeforeFlag {
@@ -51,44 +65,50 @@ public:
     Set
   };
   double GetValue(double aPortion, BeforeFlag aBeforeFlag) const;
   const nsSMILKeySpline* GetFunction() const
   {
     NS_ASSERTION(HasSpline(), "Type mismatch");
     return &mTimingFunction;
   }
-  nsTimingFunction::Type GetType() const { return mType; }
-  bool HasSpline() const { return nsTimingFunction::IsSplineType(mType); }
-  uint32_t GetSteps() const
+  Type GetType() const { return mType; }
+  bool HasSpline() const { return mType != Type::Step; }
+  const StepFunc& GetSteps() const
   {
-    MOZ_ASSERT(mType == nsTimingFunction::Type::StepStart ||
-               mType == nsTimingFunction::Type::StepEnd);
+    MOZ_ASSERT(mType == Type::Step);
     return mSteps;
   }
   bool operator==(const ComputedTimingFunction& aOther) const
   {
     return mType == aOther.mType &&
            (HasSpline() ?
             mTimingFunction == aOther.mTimingFunction :
             mSteps == aOther.mSteps);
   }
   bool operator!=(const ComputedTimingFunction& aOther) const
   {
     return !(*this == aOther);
   }
   bool operator==(const nsTimingFunction& aOther) const
   {
-    return mType == aOther.mType &&
-           (HasSpline()
-            ? mTimingFunction.X1() == aOther.mFunc.mX1 &&
-              mTimingFunction.Y1() == aOther.mFunc.mY1 &&
-              mTimingFunction.X2() == aOther.mFunc.mX2 &&
-              mTimingFunction.Y2() == aOther.mFunc.mY2
-            : mSteps == aOther.mSteps);
+    switch (aOther.mTiming.tag) {
+      case StyleComputedTimingFunction::Tag::Keyword:
+        return uint8_t(mType) == uint8_t(aOther.mTiming.keyword._0);
+      case StyleComputedTimingFunction::Tag::CubicBezier:
+        return mTimingFunction.X1() == aOther.mTiming.cubic_bezier.x1 &&
+               mTimingFunction.Y1() == aOther.mTiming.cubic_bezier.y1 &&
+               mTimingFunction.X2() == aOther.mTiming.cubic_bezier.x2 &&
+               mTimingFunction.Y2() == aOther.mTiming.cubic_bezier.y2;
+      case StyleComputedTimingFunction::Tag::Steps:
+        return mSteps.mSteps == uint32_t(aOther.mTiming.steps._0) &&
+               mSteps.mPos == aOther.mTiming.steps._1;
+      default:
+        return false;
+    }
   }
   bool operator!=(const nsTimingFunction& aOther) const
   {
     return !(*this == aOther);
   }
   int32_t Compare(const ComputedTimingFunction& aRhs) const;
   void AppendToString(nsAString& aResult) const;
 
@@ -98,36 +118,37 @@ public:
   {
     return aFunction ? aFunction->GetValue(aPortion, aBeforeFlag) : aPortion;
   }
   static int32_t Compare(const Maybe<ComputedTimingFunction>& aLhs,
                          const Maybe<ComputedTimingFunction>& aRhs);
 
 private:
   ComputedTimingFunction(double x1, double y1, double x2, double y2)
-    : mType(nsTimingFunction::Type::CubicBezier)
+    : mType(Type::CubicBezier)
     , mTimingFunction(x1, y1, x2, y2)
-    , mSteps(0)
   {
   }
-  ComputedTimingFunction(nsTimingFunction::Type aType, uint32_t aSteps)
-    : mType(aType)
-    , mSteps(aSteps) { }
+  ComputedTimingFunction(uint32_t aSteps, StyleStepPosition aPos)
+    : mType(Type::Step)
+    , mSteps { aSteps, aPos }
+  {
+  }
 
-  nsTimingFunction::Type mType = nsTimingFunction::Type::Linear;
+  Type mType;
   nsSMILKeySpline mTimingFunction;
-  uint32_t mSteps;
+  StepFunc mSteps;
 };
 
 inline bool
 operator==(const Maybe<ComputedTimingFunction>& aLHS,
            const nsTimingFunction& aRHS)
 {
   if (aLHS.isNothing()) {
-    return aRHS.mType == nsTimingFunction::Type::Linear;
+    return aRHS.IsLinear();
   }
   return aLHS.value() == aRHS;
 }
 
 inline bool
 operator!=(const Maybe<ComputedTimingFunction>& aLHS,
            const nsTimingFunction& aRHS)
 {
--- a/dom/animation/TimingParams.cpp
+++ b/dom/animation/TimingParams.cpp
@@ -210,17 +210,17 @@ TimingParams::ParseEasing(const nsAStrin
 
   nsTimingFunction timingFunction;
   RefPtr<URLExtraData> url = ServoCSSParser::GetURLExtraData(aDocument);
   if (!ServoCSSParser::ParseEasing(aEasing, url, timingFunction)) {
     aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>(aEasing);
     return Nothing();
   }
 
-  if (timingFunction.mType == nsTimingFunction::Type::Linear) {
+  if (timingFunction.IsLinear()) {
     return Nothing();
   }
 
   return Some(ComputedTimingFunction(timingFunction));
 }
 
 bool
 TimingParams::operator==(const TimingParams& aOther) const
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -805,17 +805,17 @@ AsyncPanZoomController::InitializeGlobal
   static bool sInitialized = false;
   if (sInitialized)
     return;
   sInitialized = true;
 
   MOZ_ASSERT(NS_IsMainThread());
 
   gZoomAnimationFunction = new ComputedTimingFunction(
-    nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
+    nsTimingFunction(StyleTimingKeyword::Ease));
   ClearOnShutdown(&gZoomAnimationFunction);
   gVelocityCurveFunction = new ComputedTimingFunction(
     nsTimingFunction(gfxPrefs::APZCurveFunctionX1(),
                      gfxPrefs::APZCurveFunctionY1(),
                      gfxPrefs::APZCurveFunctionX2(),
                      gfxPrefs::APZCurveFunctionY2()));
   ClearOnShutdown(&gVelocityCurveFunction);
 
--- a/gfx/layers/ipc/LayerAnimationUtils.cpp
+++ b/gfx/layers/ipc/LayerAnimationUtils.cpp
@@ -21,20 +21,18 @@ AnimationUtils::TimingFunctionToComputed
       return Nothing();
     case TimingFunction::TCubicBezierFunction: {
       CubicBezierFunction cbf = aTimingFunction.get_CubicBezierFunction();
       return Some(ComputedTimingFunction::CubicBezier(cbf.x1(), cbf.y1(),
                                                       cbf.x2(), cbf.y2()));
     }
     case TimingFunction::TStepFunction: {
       StepFunction sf = aTimingFunction.get_StepFunction();
-      nsTimingFunction::Type type = sf.type() == 1 ?
-        nsTimingFunction::Type::StepStart :
-        nsTimingFunction::Type::StepEnd;
-      return Some(ComputedTimingFunction::Steps(type, sf.steps()));
+      StyleStepPosition pos = static_cast<StyleStepPosition>(sf.type());
+      return Some(ComputedTimingFunction::Steps(sf.steps(), pos));
     }
     default:
       MOZ_ASSERT_UNREACHABLE(
         "Function must be null, bezier, step or frames");
       break;
   }
   return Nothing();
 }
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -91,18 +91,17 @@ struct CubicBezierFunction {
   float x1;
   float y1;
   float x2;
   float y2;
 };
 
 struct StepFunction {
   int steps;
-  // 1 = nsTimingFunction::StepStart, 2 = nsTimingFunction::StepEnd
-  int type;
+  uint8_t type; // Converted from StyleStepPosition.
 };
 
 union TimingFunction {
   null_t;
   CubicBezierFunction;
   StepFunction;
 };
 
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -415,18 +415,18 @@ ToTimingFunction(const Maybe<ComputedTim
   }
 
   if (aCTF->HasSpline()) {
     const nsSMILKeySpline* spline = aCTF->GetFunction();
     return TimingFunction(CubicBezierFunction(
       spline->X1(), spline->Y1(), spline->X2(), spline->Y2()));
   }
 
-  uint32_t type = aCTF->GetType() == nsTimingFunction::Type::StepStart ? 1 : 2;
-  return TimingFunction(StepFunction(aCTF->GetSteps(), type));
+  return TimingFunction(StepFunction(
+    aCTF->GetSteps().mSteps, static_cast<uint8_t>(aCTF->GetSteps().mPos)));
 }
 
 static void
 SetAnimatable(nsCSSPropertyID aProperty,
               const AnimationValue& aAnimationValue,
               nsIFrame* aFrame,
               TransformReferenceBox& aRefBox,
               layers::Animatable& aAnimatable)
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1786,17 +1786,17 @@ GetOrCreateKeyframe(nsTArray<Keyframe>* 
   }
 
   Keyframe* keyframe =
     aKeyframes->InsertElementAt(
       aInsertPosition == KeyframeInsertPosition::Prepend
                          ? 0
                          : keyframeIndex);
   keyframe->mOffset.emplace(aOffset);
-  if (aTimingFunction->mType != nsTimingFunction::Type::Linear) {
+  if (!aTimingFunction->IsLinear()) {
     keyframe->mTimingFunction.emplace();
     keyframe->mTimingFunction->Init(*aTimingFunction);
   }
 
   return keyframe;
 }
 
 Keyframe*
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -56,16 +56,21 @@ hide-types = [
     "JS::Value",
     "mozilla::binding_danger::TErrorResult.*",
     "mozilla::ErrorResult.*", # Causes JSWhyMagic to be included & handled incorrectly.
     "mozilla::dom::CallbackFunction",
     "mozilla::dom::CallbackObject.*",
     "nsINode_ErrorResult",
     "nsDOMAttributeMap_ErrorResult",
     # End of JS::Value related types
+    # This is generated by cbindgen, and we map it to its original rust type.
+    # Note: This could also avoid some rust-bindgen issues on complex template
+    # structs. i.e. We may use incorrect template parameters for nested structs.
+    # https://github.com/rust-lang-nursery/rust-bindgen/issues/1429
+    "mozilla::StyleTimingFunction.*",
 ]
 bitfield-enums = [
     "nsChangeHint",
     "nsRestyleHint",
     "mozilla::OriginFlags",
 ]
 rusty-enums = [
     "nsCompatibility",
@@ -88,17 +93,16 @@ rusty-enums = [
     "nsCSSFontDesc",
     "nsCSSPropertyID",
     "nsCSSCounterDesc",
     "nsMediaFeature_RangeType",
     "nsMediaFeature_ValueType",
     "nsresult",
     "nsAtom_AtomKind",
     "nsStyleImageLayers_LayerType",
-    "nsTimingFunction_Type",
     "mozilla::ServoElementSnapshotFlags",
     "mozilla::Side",
     "mozilla::dom::PlaybackDirection",
     "mozilla::dom::FillMode",
     "mozilla::HalfCorner",
     "mozilla::StyleDisplay",
     "mozilla::StyleDisplayMode",
     "mozilla::StyleFloatEdge",
@@ -389,16 +393,17 @@ mapped-generic-types = [
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
     { generic = false, gecko = "mozilla::ServoVisitedStyle", servo = "Option<::servo_arc::RawOffsetArc<::properties::ComputedValues>>" },
     { generic = false, gecko = "mozilla::ServoComputedValueFlags", servo = "::properties::computed_value_flags::ComputedValueFlags" },
     { generic = true, gecko = "mozilla::ServoRawOffsetArc", servo = "::servo_arc::RawOffsetArc" },
     { generic = false, gecko = "ComputedStyleStrong", servo = "::gecko_bindings::sugar::ownership::Strong<::properties::ComputedValues>" },
+    { generic = false, gecko = "mozilla::StyleComputedTimingFunction", servo = "::values::computed::easing::TimingFunction" },
 ]
 fixups = [
     { pat = "\\broot\\s*::\\s*nsString\\b", rep = "::nsstring::nsStringRepr" },
     { pat = "\\broot\\s*::\\s*nsTString\\s*<\\s*u16\\s*>", rep = "::nsstring::nsStringRepr" },
 ]
 
 [bindings]
 headers = [
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -215,18 +215,16 @@ CSS_KEY(skewx, skewx)
 CSS_KEY(skewy, skewy)
 CSS_KEY(small-caps, small_caps)
 CSS_KEY(solid, solid)
 CSS_KEY(space-around, space_around)
 CSS_KEY(space-between, space_between)
 CSS_KEY(space-evenly, space_evenly)
 CSS_KEY(span, span)
 CSS_KEY(start, start)
-CSS_KEY(step-end, step_end)
-CSS_KEY(step-start, step_start)
 CSS_KEY(stretch, stretch)
 CSS_KEY(strict, strict)
 CSS_KEY(style, style)
 CSS_KEY(sub, sub)
 CSS_KEY(subgrid, subgrid)
 CSS_KEY(super, super)
 CSS_KEY(sw-resize, sw_resize)
 CSS_KEY(table, table)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -29,18 +29,16 @@
 
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPrefs.h"
 
 using namespace mozilla;
 
 typedef nsCSSProps::KTableEntry KTableEntry;
 
-using namespace mozilla;
-
 static int32_t gPropertyTableRefCount;
 static nsStaticCaseInsensitiveNameTable* gFontDescTable;
 static nsStaticCaseInsensitiveNameTable* gCounterDescTable;
 static nsDataHashtable<nsCStringHashKey,nsCSSPropertyID>* gPropertyIDLNameTable;
 
 static const char* const kCSSRawFontDescs[] = {
 #define CSS_FONT_DESC(name_, method_) #name_,
 #include "nsCSSFontDescList.h"
@@ -618,23 +616,21 @@ const KTableEntry nsCSSProps::kTouchActi
   { eCSSKeyword_auto,         NS_STYLE_TOUCH_ACTION_AUTO },
   { eCSSKeyword_pan_x,        NS_STYLE_TOUCH_ACTION_PAN_X },
   { eCSSKeyword_pan_y,        NS_STYLE_TOUCH_ACTION_PAN_Y },
   { eCSSKeyword_manipulation, NS_STYLE_TOUCH_ACTION_MANIPULATION },
   { eCSSKeyword_UNKNOWN,      -1 }
 };
 
 const KTableEntry nsCSSProps::kTransitionTimingFunctionKTable[] = {
-  { eCSSKeyword_ease, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE },
-  { eCSSKeyword_linear, NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR },
-  { eCSSKeyword_ease_in, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN },
-  { eCSSKeyword_ease_out, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT },
-  { eCSSKeyword_ease_in_out, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT },
-  { eCSSKeyword_step_start, NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START },
-  { eCSSKeyword_step_end, NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END },
+  { eCSSKeyword_linear,       StyleTimingKeyword::Linear },
+  { eCSSKeyword_ease,         StyleTimingKeyword::Ease },
+  { eCSSKeyword_ease_in,      StyleTimingKeyword::EaseIn },
+  { eCSSKeyword_ease_out,     StyleTimingKeyword::EaseOut },
+  { eCSSKeyword_ease_in_out,  StyleTimingKeyword::EaseInOut },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kVerticalAlignKTable[] = {
   { eCSSKeyword_baseline, NS_STYLE_VERTICAL_ALIGN_BASELINE },
   { eCSSKeyword_sub, NS_STYLE_VERTICAL_ALIGN_SUB },
   { eCSSKeyword_super, NS_STYLE_VERTICAL_ALIGN_SUPER },
   { eCSSKeyword_top, NS_STYLE_VERTICAL_ALIGN_TOP },
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -4578,33 +4578,35 @@ nsComputedDOMStyle::DoGetTransitionPrope
 
 void
 nsComputedDOMStyle::AppendTimingFunction(nsDOMCSSValueList *aValueList,
                                          const nsTimingFunction& aTimingFunction)
 {
   RefPtr<nsROCSSPrimitiveValue> timingFunction = new nsROCSSPrimitiveValue;
 
   nsAutoString tmp;
-  switch (aTimingFunction.mType) {
-    case nsTimingFunction::Type::CubicBezier:
-      nsStyleUtil::AppendCubicBezierTimingFunction(aTimingFunction.mFunc.mX1,
-                                                   aTimingFunction.mFunc.mY1,
-                                                   aTimingFunction.mFunc.mX2,
-                                                   aTimingFunction.mFunc.mY2,
-                                                   tmp);
+  switch (aTimingFunction.mTiming.tag) {
+    case StyleComputedTimingFunction::Tag::CubicBezier:
+      nsStyleUtil::AppendCubicBezierTimingFunction(
+        aTimingFunction.mTiming.cubic_bezier.x1,
+        aTimingFunction.mTiming.cubic_bezier.y1,
+        aTimingFunction.mTiming.cubic_bezier.x2,
+        aTimingFunction.mTiming.cubic_bezier.y2,
+        tmp);
       break;
-    case nsTimingFunction::Type::StepStart:
-    case nsTimingFunction::Type::StepEnd:
-      nsStyleUtil::AppendStepsTimingFunction(aTimingFunction.mType,
-                                             aTimingFunction.mSteps,
-                                             tmp);
+    case StyleComputedTimingFunction::Tag::Steps:
+      nsStyleUtil::AppendStepsTimingFunction(
+        aTimingFunction.mTiming.steps._0,
+        aTimingFunction.mTiming.steps._1,
+        tmp);
       break;
-    default:
-      nsStyleUtil::AppendCubicBezierKeywordTimingFunction(aTimingFunction.mType,
-                                                          tmp);
+    case StyleComputedTimingFunction::Tag::Keyword:
+      nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
+        aTimingFunction.mTiming.keyword._0,
+        tmp);
       break;
   }
   timingFunction->SetString(tmp);
   aValueList->AppendCSSValue(timingFunction.forget());
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetTransitionTimingFunction()
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -816,25 +816,16 @@ enum class StyleGridTrackBreadth : uint8
 #define NS_STYLE_TOUCH_ACTION_PAN_X           (1 << 2)
 #define NS_STYLE_TOUCH_ACTION_PAN_Y           (1 << 3)
 #define NS_STYLE_TOUCH_ACTION_MANIPULATION    (1 << 4)
 
 // See nsStyleDisplay
 #define NS_STYLE_TOP_LAYER_NONE   0 // not in the top layer
 #define NS_STYLE_TOP_LAYER_TOP    1 // in the top layer
 
-// See nsStyleDisplay
-#define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE         0
-#define NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR       1
-#define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN      2
-#define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT     3
-#define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT  4
-#define NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START   5
-#define NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END     6
-
 // See nsStyleText
 // Note: these values pickup after the text-align values because there
 // are a few html cases where an object can have both types of
 // alignment applied with a single attribute
 #define NS_STYLE_VERTICAL_ALIGN_BASELINE             14
 #define NS_STYLE_VERTICAL_ALIGN_SUB                  15
 #define NS_STYLE_VERTICAL_ALIGN_SUPER                16
 #define NS_STYLE_VERTICAL_ALIGN_TOP                  17
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3313,75 +3313,29 @@ nsStyleBackground::IsTransparent(const n
 bool
 nsStyleBackground::IsTransparent(mozilla::ComputedStyle* aStyle) const
 {
   return BottomLayer().mImage.IsEmpty() &&
          mImage.mImageCount == 1 &&
          NS_GET_A(BackgroundColor(aStyle)) == 0;
 }
 
-void
-nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType)
-{
-  switch (aTimingFunctionType) {
-    case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START:
-      mType = Type::StepStart;
-      mSteps = 1;
-      return;
-    default:
-      MOZ_FALLTHROUGH_ASSERT("aTimingFunctionType must be a keyword value");
-    case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END:
-      mType = Type::StepEnd;
-      mSteps = 1;
-      return;
-    case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE:
-    case NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR:
-    case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN:
-    case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT:
-    case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT:
-      mType = static_cast<Type>(aTimingFunctionType);
-      break;
-  }
-
-  static_assert(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE == 0 &&
-                NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR == 1 &&
-                NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN == 2 &&
-                NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT == 3 &&
-                NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT == 4,
-                "transition timing function constants not as expected");
-
-  static const float timingFunctionValues[5][4] = {
-    { 0.25f, 0.10f, 0.25f, 1.00f }, // ease
-    { 0.00f, 0.00f, 1.00f, 1.00f }, // linear
-    { 0.42f, 0.00f, 1.00f, 1.00f }, // ease-in
-    { 0.00f, 0.00f, 0.58f, 1.00f }, // ease-out
-    { 0.42f, 0.00f, 0.58f, 1.00f }  // ease-in-out
-  };
-
-  MOZ_ASSERT(0 <= aTimingFunctionType && aTimingFunctionType < 5,
-             "keyword out of range");
-  mFunc.mX1 = timingFunctionValues[aTimingFunctionType][0];
-  mFunc.mY1 = timingFunctionValues[aTimingFunctionType][1];
-  mFunc.mX2 = timingFunctionValues[aTimingFunctionType][2];
-  mFunc.mY2 = timingFunctionValues[aTimingFunctionType][3];
-}
-
 StyleTransition::StyleTransition(const StyleTransition& aCopy)
   : mTimingFunction(aCopy.mTimingFunction)
   , mDuration(aCopy.mDuration)
   , mDelay(aCopy.mDelay)
   , mProperty(aCopy.mProperty)
   , mUnknownProperty(aCopy.mUnknownProperty)
 {
 }
 
 void
 StyleTransition::SetInitialValues()
 {
-  mTimingFunction = nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE);
+  mTimingFunction = nsTimingFunction(StyleTimingKeyword::Ease);
   mDuration = 0.0;
   mDelay = 0.0;
   mProperty = eCSSPropertyExtra_all_properties;
 }
 
 bool
 StyleTransition::operator==(const StyleTransition& aOther) const
 {
@@ -3403,17 +3357,17 @@ StyleAnimation::StyleAnimation(const Sty
   , mPlayState(aCopy.mPlayState)
   , mIterationCount(aCopy.mIterationCount)
 {
 }
 
 void
 StyleAnimation::SetInitialValues()
 {
-  mTimingFunction = nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE);
+  mTimingFunction = nsTimingFunction(StyleTimingKeyword::Ease);
   mDuration = 0.0;
   mDelay = 0.0;
   mName = nsGkAtoms::_empty;
   mDirection = dom::PlaybackDirection::Normal;
   mFillMode = dom::FillMode::None;
   mPlayState = StyleAnimationPlayState::Running;
   mIterationCount = 1.0f;
 }
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -270,29 +270,29 @@ nsStyleUtil::AppendPaintOrderValue(uint8
       default:
         MOZ_ASSERT_UNREACHABLE("unexpected paint-order component value");
     }
     aValue >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
   }
 }
 
 /* static */ void
-nsStyleUtil::AppendStepsTimingFunction(nsTimingFunction::Type aType,
-                                       uint32_t aSteps,
+nsStyleUtil::AppendStepsTimingFunction(uint32_t aStepNumber,
+                                       mozilla::StyleStepPosition aStepPos,
                                        nsAString& aResult)
 {
-  MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
-             aType == nsTimingFunction::Type::StepEnd);
-
   aResult.AppendLiteral("steps(");
-  aResult.AppendInt(aSteps);
-  if (aType == nsTimingFunction::Type::StepStart) {
-    aResult.AppendLiteral(", start)");
-  } else {
-    aResult.AppendLiteral(")");
+  aResult.AppendInt(aStepNumber);
+  switch (aStepPos) {
+    case StyleStepPosition::Start:
+      aResult.AppendLiteral(", start)");
+      break;
+    case StyleStepPosition::End:
+    default:
+      aResult.AppendLiteral(")");
   }
 }
 
 /* static */ void
 nsStyleUtil::AppendCubicBezierTimingFunction(float aX1, float aY1,
                                              float aX2, float aY2,
                                              nsAString& aResult)
 {
@@ -306,25 +306,25 @@ nsStyleUtil::AppendCubicBezierTimingFunc
   aResult.AppendFloat(aX2);
   aResult.AppendLiteral(", ");
   aResult.AppendFloat(aY2);
   aResult.Append(')');
 }
 
 /* static */ void
 nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
-    nsTimingFunction::Type aType,
+    StyleTimingKeyword aType,
     nsAString& aResult)
 {
   switch (aType) {
-    case nsTimingFunction::Type::Ease:
-    case nsTimingFunction::Type::Linear:
-    case nsTimingFunction::Type::EaseIn:
-    case nsTimingFunction::Type::EaseOut:
-    case nsTimingFunction::Type::EaseInOut: {
+    case StyleTimingKeyword::Linear:
+    case StyleTimingKeyword::Ease:
+    case StyleTimingKeyword::EaseIn:
+    case StyleTimingKeyword::EaseOut:
+    case StyleTimingKeyword::EaseInOut: {
       nsCSSKeyword keyword = nsCSSProps::ValueToKeywordEnum(
           static_cast<int32_t>(aType),
           nsCSSProps::kTransitionTimingFunctionKTable);
       AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(keyword),
                          aResult);
       break;
     }
     default:
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -71,24 +71,24 @@ public:
 
   static void AppendPaintOrderValue(uint8_t aValue, nsAString& aResult);
 
   static void AppendCSSNumber(float aNumber, nsAString& aResult)
   {
     aResult.AppendFloat(aNumber);
   }
 
-  static void AppendStepsTimingFunction(nsTimingFunction::Type aType,
-                                        uint32_t aSteps,
+  static void AppendStepsTimingFunction(uint32_t aStepNumber,
+                                        mozilla::StyleStepPosition aStepPos,
                                         nsAString& aResult);
   static void AppendCubicBezierTimingFunction(float aX1, float aY1,
                                               float aX2, float aY2,
                                               nsAString& aResult);
   static void AppendCubicBezierKeywordTimingFunction(
-      nsTimingFunction::Type aType,
+      mozilla::StyleTimingKeyword aType,
       nsAString& aResult);
 
   /*
    * Convert an author-provided floating point number to an integer (0
    * ... 255) appropriate for use in the alpha component of a color.
    */
   static uint8_t FloatToColorComponent(float aAlpha)
   {
--- a/layout/style/nsTimingFunction.h
+++ b/layout/style/nsTimingFunction.h
@@ -2,120 +2,43 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef nsTimingFunction_h
 #define nsTimingFunction_h
 
-#include "nsStyleConsts.h"
+#include "mozilla/ServoStyleConsts.h"
 
 struct nsTimingFunction
 {
-  enum class Type {
-    Ease,         // ease
-    Linear,       // linear
-    EaseIn,       // ease-in
-    EaseOut,      // ease-out
-    EaseInOut,    // ease-in-out
-    StepStart,    // step-start and steps(..., start)
-    StepEnd,      // step-end, steps(..., end) and steps(...)
-    CubicBezier,  // cubic-bezier()
-  };
+  mozilla::StyleComputedTimingFunction mTiming;
 
-  // Whether the timing function type is represented by a spline,
-  // and thus will have mFunc filled in.
-  static bool IsSplineType(Type aType)
+  explicit nsTimingFunction(mozilla::StyleTimingKeyword aKeyword =
+                              mozilla::StyleTimingKeyword::Ease)
+    : mTiming(mozilla::StyleComputedTimingFunction::Keyword(aKeyword))
   {
-    return aType != Type::StepStart && aType != Type::StepEnd;
-  }
-
-  explicit nsTimingFunction(
-    int32_t aTimingFunctionType = NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)
-    : mType(Type::Ease)
-    , mFunc{}
-  {
-    AssignFromKeyword(aTimingFunctionType);
   }
 
   nsTimingFunction(float x1, float y1, float x2, float y2)
-    : mType(Type::CubicBezier)
+    : mTiming(mozilla::StyleComputedTimingFunction::CubicBezier(x1, y1, x2, y2))
   {
-    mFunc.mX1 = x1;
-    mFunc.mY1 = y1;
-    mFunc.mX2 = x2;
-    mFunc.mY2 = y2;
-  }
-
-  enum class Keyword { Implicit, Explicit };
-
-  nsTimingFunction(Type aType, uint32_t aSteps)
-    : mType(aType)
-  {
-    MOZ_ASSERT(mType == Type::StepStart || mType == Type::StepEnd,
-               "wrong type");
-    mSteps = aSteps;
-  }
-
-  nsTimingFunction(const nsTimingFunction& aOther)
-  {
-    *this = aOther;
   }
 
-  Type mType;
-  union {
-    struct {
-      float mX1;
-      float mY1;
-      float mX2;
-      float mY2;
-    } mFunc;
-    struct {
-      uint32_t mSteps;
-    };
-  };
-
-  nsTimingFunction&
-  operator=(const nsTimingFunction& aOther)
+  bool IsLinear() const
   {
-    if (&aOther == this) {
-      return *this;
-    }
-
-    mType = aOther.mType;
-
-    if (HasSpline()) {
-      mFunc.mX1 = aOther.mFunc.mX1;
-      mFunc.mY1 = aOther.mFunc.mY1;
-      mFunc.mX2 = aOther.mFunc.mX2;
-      mFunc.mY2 = aOther.mFunc.mY2;
-    } else {
-      mSteps = aOther.mSteps;
-    }
-
-    return *this;
+    return mTiming.IsKeyword() &&
+           mTiming.keyword._0 == mozilla::StyleTimingKeyword::Linear;
   }
 
   bool operator==(const nsTimingFunction& aOther) const
   {
-    if (mType != aOther.mType) {
-      return false;
-    }
-    if (HasSpline()) {
-      return mFunc.mX1 == aOther.mFunc.mX1 && mFunc.mY1 == aOther.mFunc.mY1 &&
-             mFunc.mX2 == aOther.mFunc.mX2 && mFunc.mY2 == aOther.mFunc.mY2;
-    }
-    return mSteps == aOther.mSteps;
+    return mTiming == aOther.mTiming;
   }
 
   bool operator!=(const nsTimingFunction& aOther) const
   {
     return !(*this == aOther);
   }
-
-  bool HasSpline() const { return IsSplineType(mType); }
-
-private:
-  void AssignFromKeyword(int32_t aTimingFunctionType);
 };
 
 #endif // nsTimingFunction_h
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -626,17 +626,17 @@ GetTransitionKeyframes(nsCSSPropertyID a
                        AnimationValue&& aStartValue,
                        AnimationValue&& aEndValue,
                        const nsTimingFunction& aTimingFunction)
 {
   nsTArray<Keyframe> keyframes(2);
 
   Keyframe& fromFrame = AppendKeyframe(0.0, aProperty, std::move(aStartValue),
                                        keyframes);
-  if (aTimingFunction.mType != nsTimingFunction::Type::Linear) {
+  if (!aTimingFunction.IsLinear()) {
     fromFrame.mTimingFunction.emplace();
     fromFrame.mTimingFunction->Init(aTimingFunction);
   }
 
   AppendKeyframe(1.0, aProperty, std::move(aEndValue), keyframes);
 
   return keyframes;
 }
--- a/servo/components/style/cbindgen.toml
+++ b/servo/components/style/cbindgen.toml
@@ -37,18 +37,20 @@ derive_helper_methods = true
 
 [export]
 prefix = "Style"
 include = [
   "StyleAppearance",
   "StyleComputedFontStretchRange",
   "StyleComputedFontStyleDescriptor",
   "StyleComputedFontWeightRange",
+  "StyleComputedTimingFunction",
   "StyleDisplay",
   "StyleDisplayMode",
   "StyleFillRule",
   "StyleFontDisplay",
   "StyleFontFaceSourceListComponent",
   "StyleFontLanguageOverride",
+  "StyleTimingFunction",
   "StylePathCommand",
   "StyleUnicodeRange",
 ]
 item_types = ["enums", "structs", "typedefs"]
--- a/servo/components/style/gecko_bindings/sugar/mod.rs
+++ b/servo/components/style/gecko_bindings/sugar/mod.rs
@@ -7,13 +7,12 @@
 mod ns_com_ptr;
 mod ns_compatibility;
 mod ns_css_shadow_array;
 mod ns_css_shadow_item;
 pub mod ns_css_value;
 mod ns_style_auto_array;
 pub mod ns_style_coord;
 mod ns_t_array;
-mod ns_timing_function;
 pub mod origin_flags;
 pub mod ownership;
 pub mod refptr;
 mod style_complex_color;
deleted file mode 100644
--- a/servo/components/style/gecko_bindings/sugar/ns_timing_function.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-/* 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 gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type};
-use std::mem;
-use values::computed::ToComputedValue;
-use values::computed::easing::TimingFunction as ComputedTimingFunction;
-use values::generics::easing::{StepPosition, TimingKeyword};
-use values::generics::easing::TimingFunction as GenericTimingFunction;
-use values::specified::easing::TimingFunction;
-
-impl nsTimingFunction {
-    fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: i32) {
-        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 {
-            self.__bindgen_anon_1
-                .__bindgen_anon_1
-                .as_mut()
-                .mSteps = steps as u32;
-        }
-    }
-
-    fn set_as_bezier(
-        &mut self,
-        function_type: nsTimingFunction_Type,
-        x1: f32,
-        y1: f32,
-        x2: f32,
-        y2: f32,
-    ) {
-        self.mType = function_type;
-        unsafe {
-            let ref mut gecko_cubic_bezier = self.__bindgen_anon_1.mFunc.as_mut();
-            gecko_cubic_bezier.mX1 = x1;
-            gecko_cubic_bezier.mY1 = y1;
-            gecko_cubic_bezier.mX2 = x2;
-            gecko_cubic_bezier.mY2 = y2;
-        }
-    }
-}
-
-impl From<ComputedTimingFunction> for nsTimingFunction {
-    fn from(function: ComputedTimingFunction) -> nsTimingFunction {
-        TimingFunction::from_computed_value(&function).into()
-    }
-}
-
-impl From<TimingFunction> for nsTimingFunction {
-    fn from(function: TimingFunction) -> nsTimingFunction {
-        let mut tf: nsTimingFunction = unsafe { mem::zeroed() };
-
-        match function {
-            GenericTimingFunction::Steps(steps, StepPosition::Start) => {
-                debug_assert!(steps.value() >= 0);
-                tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value());
-            },
-            GenericTimingFunction::Steps(steps, StepPosition::End) => {
-                debug_assert!(steps.value() >= 0);
-                tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value());
-            },
-            GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {
-                tf.set_as_bezier(
-                    nsTimingFunction_Type::CubicBezier,
-                    x1.get(),
-                    y1.get(),
-                    x2.get(),
-                    y2.get(),
-                );
-            },
-            GenericTimingFunction::Keyword(keyword) => {
-                let (x1, y1, x2, y2) = keyword.to_bezier();
-                tf.set_as_bezier(keyword.into(), x1, y1, x2, y2);
-            },
-        }
-        tf
-    }
-}
-
-impl From<nsTimingFunction> for ComputedTimingFunction {
-    fn from(function: nsTimingFunction) -> ComputedTimingFunction {
-        match function.mType {
-            nsTimingFunction_Type::StepStart => GenericTimingFunction::Steps(
-                unsafe {
-                    function
-                        .__bindgen_anon_1
-                        .__bindgen_anon_1
-                        .as_ref()
-                        .mSteps as i32
-                },
-                StepPosition::Start,
-            ),
-            nsTimingFunction_Type::StepEnd => GenericTimingFunction::Steps(
-                unsafe {
-                    function
-                        .__bindgen_anon_1
-                        .__bindgen_anon_1
-                        .as_ref()
-                        .mSteps as i32
-                },
-                StepPosition::End,
-            ),
-            nsTimingFunction_Type::Ease => GenericTimingFunction::Keyword(TimingKeyword::Ease),
-            nsTimingFunction_Type::Linear => GenericTimingFunction::Keyword(TimingKeyword::Linear),
-            nsTimingFunction_Type::EaseIn => GenericTimingFunction::Keyword(TimingKeyword::EaseIn),
-            nsTimingFunction_Type::EaseOut => {
-                GenericTimingFunction::Keyword(TimingKeyword::EaseOut)
-            },
-            nsTimingFunction_Type::EaseInOut => {
-                GenericTimingFunction::Keyword(TimingKeyword::EaseInOut)
-            },
-            nsTimingFunction_Type::CubicBezier => unsafe {
-                GenericTimingFunction::CubicBezier {
-                    x1: function.__bindgen_anon_1.mFunc.as_ref().mX1,
-                    y1: function.__bindgen_anon_1.mFunc.as_ref().mY1,
-                    x2: function.__bindgen_anon_1.mFunc.as_ref().mX2,
-                    y2: 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/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2850,30 +2850,34 @@ fn static_assert() {
         };
 
         self.gecko.mMinFontSizeRatio = percentage as u8;
     }
 
     ${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')}
 </%self:impl_trait>
 
-<%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)">
+<%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name, member=None)">
     #[allow(non_snake_case)]
     pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {
         self.gecko.m${type.capitalize()}s.ensure_len(other.gecko.m${type.capitalize()}s.len());
 
         let count = other.gecko.m${type.capitalize()}${gecko_ffi_name}Count;
         self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = count;
 
         let iter = self.gecko.m${type.capitalize()}s.iter_mut().take(count as usize).zip(
             other.gecko.m${type.capitalize()}s.iter()
         );
 
         for (ours, others) in iter {
+            % if member:
+            ours.m${gecko_ffi_name}.${member} = others.m${gecko_ffi_name}.${member};
+            % else:
             ours.m${gecko_ffi_name} = others.m${gecko_ffi_name};
+            % endif
         }
     }
 
     #[allow(non_snake_case)]
     pub fn reset_${type}_${ident}(&mut self, other: &Self) {
         self.copy_${type}_${ident}_from(other)
     }
 </%def>
@@ -2918,24 +2922,24 @@ fn static_assert() {
     {
         let v = v.into_iter();
         debug_assert_ne!(v.len(), 0);
         let input_len = v.len();
         self.gecko.m${type.capitalize()}s.ensure_len(input_len);
 
         self.gecko.m${type.capitalize()}TimingFunctionCount = input_len as u32;
         for (gecko, servo) in self.gecko.m${type.capitalize()}s.iter_mut().take(input_len as usize).zip(v) {
-            gecko.mTimingFunction = servo.into();
+            gecko.mTimingFunction.mTiming = servo;
         }
     }
     ${impl_animation_or_transition_count(type, 'timing_function', 'TimingFunction')}
-    ${impl_copy_animation_or_transition_value(type, 'timing_function', 'TimingFunction')}
+    ${impl_copy_animation_or_transition_value(type, 'timing_function', "TimingFunction", "mTiming")}
     pub fn ${type}_timing_function_at(&self, index: usize)
         -> longhands::${type}_timing_function::computed_value::SingleComputedValue {
-        self.gecko.m${type.capitalize()}s[index].mTimingFunction.into()
+        self.gecko.m${type.capitalize()}s[index].mTimingFunction.mTiming
     }
 </%def>
 
 <%def name="impl_transition_time_value(ident, gecko_ffi_name)">
     ${impl_animation_or_transition_time_value('transition', ident, gecko_ffi_name)}
 </%def>
 
 <%def name="impl_transition_count(ident, gecko_ffi_name)">
--- a/servo/components/style/values/computed/easing.rs
+++ b/servo/components/style/values/computed/easing.rs
@@ -1,11 +1,14 @@
 /* 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 Easing functions.
 
 use values::computed::{Integer, Number};
-use values::generics::easing::TimingFunction as GenericTimingFunction;
+use values::generics::easing;
 
 /// A computed timing function.
-pub type TimingFunction = GenericTimingFunction<Integer, Number>;
+pub type ComputedTimingFunction = easing::TimingFunction<Integer, Number>;
+
+/// An alias of the computed timing function.
+pub type TimingFunction = ComputedTimingFunction;
--- a/servo/components/style/values/generics/easing.rs
+++ b/servo/components/style/values/generics/easing.rs
@@ -5,16 +5,17 @@
 //! Generic types for CSS Easing Functions.
 //! https://drafts.csswg.org/css-easing/#timing-functions
 
 use values::CSSFloat;
 
 /// A generic easing function.
 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
 #[value_info(ty = "TIMING_FUNCTION")]
+#[repr(u8, C)]
 pub enum TimingFunction<Integer, Number> {
     /// `linear | ease | ease-in | ease-out | ease-in-out`
     Keyword(TimingKeyword),
     /// `cubic-bezier(<number>, <number>, <number>, <number>)`
     #[allow(missing_docs)]
     #[css(comma, function)]
     CubicBezier {
         x1: Number,
@@ -37,27 +38,29 @@ pub enum TimingFunction<Integer, Number>
     Eq,
     MallocSizeOf,
     Parse,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
 )]
+#[repr(u8)]
 pub enum TimingKeyword {
     Linear,
     Ease,
     EaseIn,
     EaseOut,
     EaseInOut,
 }
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
+#[repr(u8)]
 pub enum StepPosition {
     Start,
     End,
 }
 
 #[inline]
 fn is_end(position: &StepPosition) -> bool {
     *position == StepPosition::End
--- a/servo/components/style/values/specified/easing.rs
+++ b/servo/components/style/values/specified/easing.rs
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for CSS Easing functions.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 use style_traits::{ParseError, StyleParseErrorKind};
+use values::computed::easing::TimingFunction as ComputedTimingFunction;
 use values::generics::easing::{StepPosition, TimingKeyword};
 use values::generics::easing::TimingFunction as GenericTimingFunction;
 use values::specified::{Integer, Number};
 
 /// A specified timing function.
 pub type TimingFunction = GenericTimingFunction<Integer, Number>;
 
 impl Parse for TimingFunction {
@@ -66,8 +67,31 @@ impl Parse for TimingFunction {
             }).map_err(|()| {
                 location.new_custom_error(
                     StyleParseErrorKind::UnexpectedFunction(function.clone())
                 )
             })
         })
     }
 }
+
+// We need this for converting the specified TimingFunction into computed TimingFunction without
+// Context (for some FFIs in glue.rs). In fact, we don't really need Context to get the computed
+// value of TimingFunction.
+impl TimingFunction {
+    /// Generate the ComputedTimingFunction without Context.
+    pub fn to_computed_value_without_context(&self) -> ComputedTimingFunction {
+        match *self {
+            GenericTimingFunction::Steps(steps, pos) => {
+                GenericTimingFunction::Steps(steps.value(), pos)
+            },
+            GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {
+                GenericTimingFunction::CubicBezier {
+                    x1: x1.get(),
+                    y1: y1.get(),
+                    x2: x2.get(),
+                    y2: y2.get(),
+                }
+            },
+            GenericTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(keyword),
+        }
+    }
+}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -116,16 +116,17 @@ use style::gecko_bindings::structs::Serv
 use style::gecko_bindings::structs::StyleRuleInclusion;
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::structs::gfxFontFeatureValueSet;
 use style::gecko_bindings::structs::nsCSSValueSharedList;
 use style::gecko_bindings::structs::nsCompatibility;
 use style::gecko_bindings::structs::nsIDocument;
 use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
 use style::gecko_bindings::structs::nsTArray;
+use style::gecko_bindings::structs::nsTimingFunction;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties;
 use style::invalidation::element::restyle_hints;
 use style::media_queries::MediaList;
 use style::parser::{Parse, ParserContext, self};
@@ -3462,17 +3463,18 @@ pub extern "C" fn Servo_ParseEasing(
     );
     let easing = unsafe { (*easing).to_string() };
     let mut input = ParserInput::new(&easing);
     let mut parser = Parser::new(&mut input);
     let result =
         parser.parse_entirely(|p| transition_timing_function::single_value::parse(&context, p));
     match result {
         Ok(parsed_easing) => {
-            *output = parsed_easing.into();
+            // We store as computed value in nsTimingFunction.
+            (*output).mTiming = parsed_easing.to_computed_value_without_context();
             true
         },
         Err(_) => false
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_GetProperties_Overriding_Animation(
@@ -5069,19 +5071,21 @@ pub unsafe extern "C" fn Servo_StyleSet_
     // ones).
     for step in animation.steps.iter().rev() {
         if step.start_percentage.0 != current_offset {
             properties_set_at_current_offset.clear();
             current_offset = step.start_percentage.0;
         }
 
         // Override timing_function if the keyframe has an animation-timing-function.
-        let timing_function = match step.get_animation_timing_function(&guard) {
-            Some(val) => val.into(),
-            None => *inherited_timing_function,
+        let timing_function = nsTimingFunction {
+            mTiming: match step.get_animation_timing_function(&guard) {
+                Some(val) => val.to_computed_value_without_context(),
+                None => (*inherited_timing_function).mTiming,
+            }
         };
 
         // Look for an existing keyframe with the same offset and timing
         // function or else add a new keyframe at the beginning of the keyframe
         // array.
         let keyframe = Gecko_GetOrCreateKeyframeAtStart(
             keyframes,
             step.start_percentage.0 as f32,