Bug 1332211 - Move Layer::SetAnimation into AnimationHelper, r?hiro draft
authorpeter chang <pchang@mozilla.com>
Wed, 25 Jan 2017 10:51:30 +0800
changeset 470269 901019ced6ce29e6899f1420ff67b3a57a1f17ec
parent 469974 c8102da699dba43d85eb80b70956e80c6793feca
child 470270 21e0a1404cc5799d31ab761877b83d6f8aff6660
push id43976
push userbmo:howareyou322@gmail.com
push dateFri, 03 Feb 2017 14:05:30 +0000
reviewershiro
bugs1332211
milestone54.0a1
Bug 1332211 - Move Layer::SetAnimation into AnimationHelper, r?hiro MozReview-Commit-ID: JQMAJt8lvYo
gfx/layers/AnimationHelper.cpp
gfx/layers/AnimationHelper.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/composite/AsyncCompositionManager.cpp
gfx/layers/moz.build
new file mode 100644
--- /dev/null
+++ b/gfx/layers/AnimationHelper.cpp
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "AnimationHelper.h"
+#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
+#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for FillMode
+#include "mozilla/layers/LayerAnimationUtils.h"  // for TimingFunctionToComputedTimingFunction
+#include "mozilla/layers/LayersMessages.h"  // for TransformFunction, etc
+#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
+
+namespace mozilla {
+namespace layers {
+
+static inline void
+SetCSSAngle(const CSSAngle& aAngle, nsCSSValue& aValue)
+{
+  aValue.SetFloatValue(aAngle.value(), nsCSSUnit(aAngle.unit()));
+}
+
+static nsCSSValueSharedList*
+CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
+{
+  nsAutoPtr<nsCSSValueList> result;
+  nsCSSValueList** resultTail = getter_Transfers(result);
+  for (uint32_t i = 0; i < aFunctions.Length(); i++) {
+    RefPtr<nsCSSValue::Array> arr;
+    switch (aFunctions[i].type()) {
+      case TransformFunction::TRotationX:
+      {
+        const CSSAngle& angle = aFunctions[i].get_RotationX().angle();
+        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatex,
+                                                           resultTail);
+        SetCSSAngle(angle, arr->Item(1));
+        break;
+      }
+      case TransformFunction::TRotationY:
+      {
+        const CSSAngle& angle = aFunctions[i].get_RotationY().angle();
+        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatey,
+                                                           resultTail);
+        SetCSSAngle(angle, arr->Item(1));
+        break;
+      }
+      case TransformFunction::TRotationZ:
+      {
+        const CSSAngle& angle = aFunctions[i].get_RotationZ().angle();
+        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatez,
+                                                           resultTail);
+        SetCSSAngle(angle, arr->Item(1));
+        break;
+      }
+      case TransformFunction::TRotation:
+      {
+        const CSSAngle& angle = aFunctions[i].get_Rotation().angle();
+        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate,
+                                                           resultTail);
+        SetCSSAngle(angle, arr->Item(1));
+        break;
+      }
+      case TransformFunction::TRotation3D:
+      {
+        float x = aFunctions[i].get_Rotation3D().x();
+        float y = aFunctions[i].get_Rotation3D().y();
+        float z = aFunctions[i].get_Rotation3D().z();
+        const CSSAngle& angle = aFunctions[i].get_Rotation3D().angle();
+        arr =
+          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate3d,
+                                                       resultTail);
+        arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
+        arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
+        arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
+        SetCSSAngle(angle, arr->Item(4));
+        break;
+      }
+      case TransformFunction::TScale:
+      {
+        arr =
+          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_scale3d,
+                                                       resultTail);
+        arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number);
+        arr->Item(2).SetFloatValue(aFunctions[i].get_Scale().y(), eCSSUnit_Number);
+        arr->Item(3).SetFloatValue(aFunctions[i].get_Scale().z(), eCSSUnit_Number);
+        break;
+      }
+      case TransformFunction::TTranslation:
+      {
+        arr =
+          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_translate3d,
+                                                       resultTail);
+        arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel);
+        arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
+        arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
+        break;
+      }
+      case TransformFunction::TSkewX:
+      {
+        const CSSAngle& x = aFunctions[i].get_SkewX().x();
+        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewx,
+                                                           resultTail);
+        SetCSSAngle(x, arr->Item(1));
+        break;
+      }
+      case TransformFunction::TSkewY:
+      {
+        const CSSAngle& y = aFunctions[i].get_SkewY().y();
+        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewy,
+                                                           resultTail);
+        SetCSSAngle(y, arr->Item(1));
+        break;
+      }
+      case TransformFunction::TSkew:
+      {
+        const CSSAngle& x = aFunctions[i].get_Skew().x();
+        const CSSAngle& y = aFunctions[i].get_Skew().y();
+        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skew,
+                                                           resultTail);
+        SetCSSAngle(x, arr->Item(1));
+        SetCSSAngle(y, arr->Item(2));
+        break;
+      }
+      case TransformFunction::TTransformMatrix:
+      {
+        arr =
+          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_matrix3d,
+                                                       resultTail);
+        const gfx::Matrix4x4& matrix = aFunctions[i].get_TransformMatrix().value();
+        arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
+        arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
+        arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number);
+        arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number);
+        arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number);
+        arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number);
+        arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number);
+        arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number);
+        arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number);
+        arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number);
+        arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number);
+        arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number);
+        arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number);
+        arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number);
+        arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number);
+        arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number);
+        break;
+      }
+      case TransformFunction::TPerspective:
+      {
+        float perspective = aFunctions[i].get_Perspective().value();
+        arr =
+          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_perspective,
+                                                       resultTail);
+        arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel);
+        break;
+      }
+      default:
+        NS_ASSERTION(false, "All functions should be implemented?");
+    }
+  }
+  if (aFunctions.Length() == 0) {
+    result = new nsCSSValueList();
+    result->mValue.SetNoneValue();
+  }
+  return new nsCSSValueSharedList(result.forget());
+}
+
+static StyleAnimationValue
+ToStyleAnimationValue(const Animatable& aAnimatable)
+{
+  StyleAnimationValue result;
+
+  switch (aAnimatable.type()) {
+    case Animatable::Tnull_t:
+      break;
+    case Animatable::TArrayOfTransformFunction: {
+      const InfallibleTArray<TransformFunction>& transforms =
+        aAnimatable.get_ArrayOfTransformFunction();
+      result.SetTransformValue(CreateCSSValueList(transforms));
+      break;
+    }
+    case Animatable::Tfloat:
+      result.SetFloatValue(aAnimatable.get_float());
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unsupported type");
+  }
+
+  return result;
+}
+
+void
+AnimationHelper::SetAnimations(AnimationArray& aAnimations,
+                               InfallibleTArray<AnimData>& aAnimData,
+                               StyleAnimationValue& aBaseAnimationStyle)
+{
+  for (uint32_t i = 0; i < aAnimations.Length(); i++) {
+    Animation& animation = aAnimations[i];
+    // Adjust fill mode to fill forwards so that if the main thread is delayed
+    // in clearing this animation we don't introduce flicker by jumping back to
+    // the old underlying value
+    switch (static_cast<dom::FillMode>(animation.fillMode())) {
+      case dom::FillMode::None:
+        animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Forwards);
+        break;
+      case dom::FillMode::Backwards:
+        animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
+        break;
+      default:
+        break;
+    }
+
+    if (animation.baseStyle().type() != Animatable::Tnull_t) {
+      aBaseAnimationStyle = ToStyleAnimationValue(animation.baseStyle());
+    }
+
+    AnimData* data = aAnimData.AppendElement();
+    InfallibleTArray<Maybe<ComputedTimingFunction>>& functions =
+      data->mFunctions;
+    const InfallibleTArray<AnimationSegment>& segments = animation.segments();
+    for (uint32_t j = 0; j < segments.Length(); j++) {
+      TimingFunction tf = segments.ElementAt(j).sampleFn();
+
+      Maybe<ComputedTimingFunction> ctf =
+        AnimationUtils::TimingFunctionToComputedTimingFunction(tf);
+      functions.AppendElement(ctf);
+    }
+
+    // Precompute the StyleAnimationValues that we need if this is a transform
+    // animation.
+    InfallibleTArray<StyleAnimationValue>& startValues = data->mStartValues;
+    InfallibleTArray<StyleAnimationValue>& endValues = data->mEndValues;
+    for (const AnimationSegment& segment : segments) {
+      startValues.AppendElement(ToStyleAnimationValue(segment.startState()));
+      endValues.AppendElement(ToStyleAnimationValue(segment.endState()));
+    }
+  }
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/AnimationHelper.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 mozilla_layers_AnimationHelper_h
+#define mozilla_layers_AnimationHelper_h
+
+#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
+#include "mozilla/TimeStamp.h"          // for TimeStamp
+
+
+namespace mozilla {
+  class StyleAnimationValue;
+namespace layers {
+class Animation;
+
+typedef InfallibleTArray<layers::Animation> AnimationArray;
+
+struct AnimData {
+  InfallibleTArray<mozilla::StyleAnimationValue> mStartValues;
+  InfallibleTArray<mozilla::StyleAnimationValue> mEndValues;
+  InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions;
+};
+
+class AnimationHelper
+{
+public:
+  static void
+  SetAnimations(AnimationArray& aAnimations,
+                InfallibleTArray<AnimData>& aAnimData,
+                StyleAnimationValue& aBaseAnimationStyle);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_AnimationHelper_h
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -19,27 +19,26 @@
 #include "gfxEnv.h"
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxPrefs.h"
 #include "gfxUtils.h"                   // for gfxUtils, etc
 #include "gfx2DGlue.h"
 #include "mozilla/DebugOnly.h"          // for DebugOnly
 #include "mozilla/Telemetry.h"          // for Accumulate
 #include "mozilla/ToString.h"
-#include "mozilla/dom/Animation.h"      // for ComputedTimingFunction
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/gfx/Polygon.h"        // for Polygon
+#include "mozilla/layers/AnimationHelper.h"
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/layers/BSPTree.h"     // for BSPTree
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"
-#include "mozilla/layers/LayerAnimationUtils.h"  // for TimingFunctionToComputedTimingFunction
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersMessages.h"  // for TransformFunction, etc
 #include "mozilla/layers/LayersTypes.h"  // for TextureDumpMode
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowableLayer
 #include "nsAString.h"
 #include "nsCSSValue.h"                 // for nsCSSValue::Array, etc
@@ -248,239 +247,26 @@ Layer::ClearAnimationsForNextTransaction
   // Ensure we have a non-null mPendingAnimations to mark a future clear.
   if (!mPendingAnimations) {
     mPendingAnimations = new AnimationArray;
   }
 
   mPendingAnimations->Clear();
 }
 
-static inline void
-SetCSSAngle(const CSSAngle& aAngle, nsCSSValue& aValue)
-{
-  aValue.SetFloatValue(aAngle.value(), nsCSSUnit(aAngle.unit()));
-}
-
-static nsCSSValueSharedList*
-CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
-{
-  nsAutoPtr<nsCSSValueList> result;
-  nsCSSValueList** resultTail = getter_Transfers(result);
-  for (uint32_t i = 0; i < aFunctions.Length(); i++) {
-    RefPtr<nsCSSValue::Array> arr;
-    switch (aFunctions[i].type()) {
-      case TransformFunction::TRotationX:
-      {
-        const CSSAngle& angle = aFunctions[i].get_RotationX().angle();
-        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatex,
-                                                           resultTail);
-        SetCSSAngle(angle, arr->Item(1));
-        break;
-      }
-      case TransformFunction::TRotationY:
-      {
-        const CSSAngle& angle = aFunctions[i].get_RotationY().angle();
-        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatey,
-                                                           resultTail);
-        SetCSSAngle(angle, arr->Item(1));
-        break;
-      }
-      case TransformFunction::TRotationZ:
-      {
-        const CSSAngle& angle = aFunctions[i].get_RotationZ().angle();
-        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotatez,
-                                                           resultTail);
-        SetCSSAngle(angle, arr->Item(1));
-        break;
-      }
-      case TransformFunction::TRotation:
-      {
-        const CSSAngle& angle = aFunctions[i].get_Rotation().angle();
-        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate,
-                                                           resultTail);
-        SetCSSAngle(angle, arr->Item(1));
-        break;
-      }
-      case TransformFunction::TRotation3D:
-      {
-        float x = aFunctions[i].get_Rotation3D().x();
-        float y = aFunctions[i].get_Rotation3D().y();
-        float z = aFunctions[i].get_Rotation3D().z();
-        const CSSAngle& angle = aFunctions[i].get_Rotation3D().angle();
-        arr =
-          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_rotate3d,
-                                                       resultTail);
-        arr->Item(1).SetFloatValue(x, eCSSUnit_Number);
-        arr->Item(2).SetFloatValue(y, eCSSUnit_Number);
-        arr->Item(3).SetFloatValue(z, eCSSUnit_Number);
-        SetCSSAngle(angle, arr->Item(4));
-        break;
-      }
-      case TransformFunction::TScale:
-      {
-        arr =
-          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_scale3d,
-                                                       resultTail);
-        arr->Item(1).SetFloatValue(aFunctions[i].get_Scale().x(), eCSSUnit_Number);
-        arr->Item(2).SetFloatValue(aFunctions[i].get_Scale().y(), eCSSUnit_Number);
-        arr->Item(3).SetFloatValue(aFunctions[i].get_Scale().z(), eCSSUnit_Number);
-        break;
-      }
-      case TransformFunction::TTranslation:
-      {
-        arr =
-          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_translate3d,
-                                                       resultTail);
-        arr->Item(1).SetFloatValue(aFunctions[i].get_Translation().x(), eCSSUnit_Pixel);
-        arr->Item(2).SetFloatValue(aFunctions[i].get_Translation().y(), eCSSUnit_Pixel);
-        arr->Item(3).SetFloatValue(aFunctions[i].get_Translation().z(), eCSSUnit_Pixel);
-        break;
-      }
-      case TransformFunction::TSkewX:
-      {
-        const CSSAngle& x = aFunctions[i].get_SkewX().x();
-        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewx,
-                                                           resultTail);
-        SetCSSAngle(x, arr->Item(1));
-        break;
-      }
-      case TransformFunction::TSkewY:
-      {
-        const CSSAngle& y = aFunctions[i].get_SkewY().y();
-        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skewy,
-                                                           resultTail);
-        SetCSSAngle(y, arr->Item(1));
-        break;
-      }
-      case TransformFunction::TSkew:
-      {
-        const CSSAngle& x = aFunctions[i].get_Skew().x();
-        const CSSAngle& y = aFunctions[i].get_Skew().y();
-        arr = StyleAnimationValue::AppendTransformFunction(eCSSKeyword_skew,
-                                                           resultTail);
-        SetCSSAngle(x, arr->Item(1));
-        SetCSSAngle(y, arr->Item(2));
-        break;
-      }
-      case TransformFunction::TTransformMatrix:
-      {
-        arr =
-          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_matrix3d,
-                                                       resultTail);
-        const gfx::Matrix4x4& matrix = aFunctions[i].get_TransformMatrix().value();
-        arr->Item(1).SetFloatValue(matrix._11, eCSSUnit_Number);
-        arr->Item(2).SetFloatValue(matrix._12, eCSSUnit_Number);
-        arr->Item(3).SetFloatValue(matrix._13, eCSSUnit_Number);
-        arr->Item(4).SetFloatValue(matrix._14, eCSSUnit_Number);
-        arr->Item(5).SetFloatValue(matrix._21, eCSSUnit_Number);
-        arr->Item(6).SetFloatValue(matrix._22, eCSSUnit_Number);
-        arr->Item(7).SetFloatValue(matrix._23, eCSSUnit_Number);
-        arr->Item(8).SetFloatValue(matrix._24, eCSSUnit_Number);
-        arr->Item(9).SetFloatValue(matrix._31, eCSSUnit_Number);
-        arr->Item(10).SetFloatValue(matrix._32, eCSSUnit_Number);
-        arr->Item(11).SetFloatValue(matrix._33, eCSSUnit_Number);
-        arr->Item(12).SetFloatValue(matrix._34, eCSSUnit_Number);
-        arr->Item(13).SetFloatValue(matrix._41, eCSSUnit_Number);
-        arr->Item(14).SetFloatValue(matrix._42, eCSSUnit_Number);
-        arr->Item(15).SetFloatValue(matrix._43, eCSSUnit_Number);
-        arr->Item(16).SetFloatValue(matrix._44, eCSSUnit_Number);
-        break;
-      }
-      case TransformFunction::TPerspective:
-      {
-        float perspective = aFunctions[i].get_Perspective().value();
-        arr =
-          StyleAnimationValue::AppendTransformFunction(eCSSKeyword_perspective,
-                                                       resultTail);
-        arr->Item(1).SetFloatValue(perspective, eCSSUnit_Pixel);
-        break;
-      }
-      default:
-        NS_ASSERTION(false, "All functions should be implemented?");
-    }
-  }
-  if (aFunctions.Length() == 0) {
-    result = new nsCSSValueList();
-    result->mValue.SetNoneValue();
-  }
-  return new nsCSSValueSharedList(result.forget());
-}
-
-static StyleAnimationValue
-ToStyleAnimationValue(const Animatable& aAnimatable)
-{
-  StyleAnimationValue result;
-
-  switch (aAnimatable.type()) {
-    case Animatable::Tnull_t:
-      break;
-    case Animatable::TArrayOfTransformFunction: {
-      const InfallibleTArray<TransformFunction>& transforms =
-        aAnimatable.get_ArrayOfTransformFunction();
-      result.SetTransformValue(CreateCSSValueList(transforms));
-      break;
-    }
-    case Animatable::Tfloat:
-      result.SetFloatValue(aAnimatable.get_float());
-      break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unsupported type");
-  }
-
-  return result;
-}
-
 void
 Layer::SetAnimations(const AnimationArray& aAnimations)
 {
   MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) SetAnimations", this));
 
   mAnimations = aAnimations;
   mAnimationData.Clear();
-  for (uint32_t i = 0; i < mAnimations.Length(); i++) {
-    Animation& animation = mAnimations[i];
-    // Adjust fill mode to fill forwards so that if the main thread is delayed
-    // in clearing this animation we don't introduce flicker by jumping back to
-    // the old underlying value
-    switch (static_cast<dom::FillMode>(animation.fillMode())) {
-      case dom::FillMode::None:
-        animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Forwards);
-        break;
-      case dom::FillMode::Backwards:
-        animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
-        break;
-      default:
-        break;
-    }
-
-    if (animation.baseStyle().type() != Animatable::Tnull_t) {
-      mBaseAnimationStyle = ToStyleAnimationValue(animation.baseStyle());
-    }
-
-    AnimData* data = mAnimationData.AppendElement();
-    InfallibleTArray<Maybe<ComputedTimingFunction>>& functions =
-      data->mFunctions;
-    const InfallibleTArray<AnimationSegment>& segments = animation.segments();
-    for (uint32_t j = 0; j < segments.Length(); j++) {
-      TimingFunction tf = segments.ElementAt(j).sampleFn();
-
-      Maybe<ComputedTimingFunction> ctf =
-        AnimationUtils::TimingFunctionToComputedTimingFunction(tf);
-      functions.AppendElement(ctf);
-    }
-
-    // Precompute the StyleAnimationValues that we need if this is a transform
-    // animation.
-    InfallibleTArray<StyleAnimationValue>& startValues = data->mStartValues;
-    InfallibleTArray<StyleAnimationValue>& endValues = data->mEndValues;
-    for (const AnimationSegment& segment : segments) {
-      startValues.AppendElement(ToStyleAnimationValue(segment.startState()));
-      endValues.AppendElement(ToStyleAnimationValue(segment.endState()));
-    }
-  }
+  AnimationHelper::SetAnimations(mAnimations,
+                                 mAnimationData,
+                                 mBaseAnimationStyle);
 
   Mutated();
 }
 
 void
 Layer::StartPendingAnimations(const TimeStamp& aReadyTime)
 {
   ForEachNode<ForwardIterator>(
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -53,17 +53,16 @@
 #include "ImageContainer.h"
 
 class gfxContext;
 
 extern uint8_t gLayerManagerLayerBuilder;
 
 namespace mozilla {
 
-class ComputedTimingFunction;
 class FrameLayerBuilder;
 class StyleAnimationValue;
 
 namespace gl {
 class GLContext;
 } // namespace gl
 
 namespace gfx {
@@ -94,16 +93,17 @@ class HostLayer;
 class ShadowableLayer;
 class ShadowLayerForwarder;
 class LayerManagerComposite;
 class SpecificLayerAttributes;
 class Compositor;
 class FrameUniformityData;
 class PersistentBufferProvider;
 class GlyphArray;
+struct AnimData;
 
 namespace layerscope {
 class LayersPacket;
 } // namespace layerscope
 
 #define MOZ_LAYER_DECL_NAME(n, e)                              \
   virtual const char* Name() const override { return n; }  \
   virtual LayerType GetType() const override { return e; }
@@ -723,31 +723,25 @@ public:
   void SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
                                                 const ScrollUpdateInfo& aUpdateInfo);
   Maybe<ScrollUpdateInfo> GetPendingScrollInfoUpdate(FrameMetrics::ViewID aScrollId);
   void ClearPendingScrollInfoUpdate();
 private:
   std::map<FrameMetrics::ViewID,ScrollUpdateInfo> mPendingScrollUpdates;
 };
 
-typedef InfallibleTArray<Animation> AnimationArray;
-
-struct AnimData {
-  InfallibleTArray<mozilla::StyleAnimationValue> mStartValues;
-  InfallibleTArray<mozilla::StyleAnimationValue> mEndValues;
-  InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions;
-};
-
 /**
  * A Layer represents anything that can be rendered onto a destination
  * surface.
  */
 class Layer {
   NS_INLINE_DECL_REFCOUNTING(Layer)
 
+  typedef InfallibleTArray<Animation> AnimationArray;
+
 public:
   // Keep these in alphabetical order
   enum LayerType {
     TYPE_CANVAS,
     TYPE_COLOR,
     TYPE_CONTAINER,
     TYPE_IMAGE,
     TYPE_TEXT,
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/WidgetUtils.h"        // for ComputeTransformForRotation
 #include "mozilla/dom/KeyframeEffectReadOnly.h"
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
 #include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Point.h"          // for RoundedToInt, PointTyped
 #include "mozilla/gfx/Rect.h"           // for RoundedToInt, RectTyped
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
+#include "mozilla/layers/AnimationHelper.h"
 #include "mozilla/layers/APZUtils.h"    // for CompleteAsyncTransform
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels, etc
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -94,16 +94,17 @@ EXPORTS.gfxipc += [
     'ipc/ShadowLayerUtils.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'apz/util/CheckerboardReportService.h',
 ]
 
 EXPORTS.mozilla.layers += [
+    'AnimationHelper.h',
     'apz/public/CompositorController.h',
     'apz/public/GeckoContentController.h',
     'apz/public/IAPZCTreeManager.h',
     'apz/public/MetricsSharingController.h',
     # exporting things from apz/src is temporary until we extract a
     # proper interface for the code there
     'apz/src/APZCTreeManager.h',
     'apz/src/APZUtils.h',
@@ -242,16 +243,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     UNIFIED_SOURCES += [
         'apz/src/AndroidAPZ.cpp',
     ]
 
 UNIFIED_SOURCES += [
+    'AnimationHelper.cpp',
     'apz/public/IAPZCTreeManager.cpp',
     'apz/src/APZCTreeManager.cpp',
     'apz/src/AsyncPanZoomController.cpp',
     'apz/src/Axis.cpp',
     'apz/src/CheckerboardEvent.cpp',
     'apz/src/DragTracker.cpp',
     'apz/src/GestureEventListener.cpp',
     'apz/src/HitTestingTreeNode.cpp',