Bug 1049975 - Part 5: Move timing related code into AnimationEffectReadOnly. draft
authorBoris Chiou <boris.chiou@gmail.com>
Mon, 25 Jul 2016 18:27:33 +0800
changeset 405233 39dd12ce13c01282d19c8a533bcb2a9f68b180a4
parent 405232 e78fe8ab037b42a14658ac1851a247316d8ffd21
child 405234 7914562c12adec123e76f20b36fa29c91c644584
push id27442
push userbmo:boris.chiou@gmail.com
push dateThu, 25 Aug 2016 04:26:27 +0000
bugs1049975
milestone51.0a1
Bug 1049975 - Part 5: Move timing related code into AnimationEffectReadOnly. Move the mTiming, mAnimation, and the implementaion of timing into AnimationEffectReadOnly. MozReview-Commit-ID: EZhlbphVvCo
dom/animation/AnimationEffectReadOnly.cpp
dom/animation/AnimationEffectReadOnly.h
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffect.h
gfx/layers/composite/AsyncCompositionManager.cpp
--- a/dom/animation/AnimationEffectReadOnly.cpp
+++ b/dom/animation/AnimationEffectReadOnly.cpp
@@ -1,24 +1,355 @@
 /* -*- 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 "mozilla/dom/AnimationEffectReadOnly.h"
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
+#include "mozilla/AnimationUtils.h"
+#include "mozilla/FloatingPoint.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationEffectReadOnly, mDocument)
+NS_IMPL_CYCLE_COLLECTION_CLASS(AnimationEffectReadOnly)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AnimationEffectReadOnly)
+  if (tmp->mTiming) {
+    tmp->mTiming->Unlink();
+  }
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument, mTiming, mAnimation)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AnimationEffectReadOnly)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument, mTiming, mAnimation)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AnimationEffectReadOnly)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationEffectReadOnly)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationEffectReadOnly)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationEffectReadOnly)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
+AnimationEffectReadOnly::AnimationEffectReadOnly(
+  nsIDocument* aDocument, AnimationEffectTimingReadOnly* aTiming)
+  : mDocument(aDocument)
+  , mTiming(aTiming)
+{
+  MOZ_ASSERT(aTiming);
+}
+
+// https://w3c.github.io/web-animations/#in-play
+bool
+AnimationEffectReadOnly::IsInPlay() const
+{
+  if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
+    return false;
+  }
+
+  return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase::Active;
+}
+
+// https://w3c.github.io/web-animations/#current
+bool
+AnimationEffectReadOnly::IsCurrent() const
+{
+  if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
+    return false;
+  }
+
+  ComputedTiming computedTiming = GetComputedTiming();
+  return computedTiming.mPhase == ComputedTiming::AnimationPhase::Before ||
+         computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
+}
+
+// https://w3c.github.io/web-animations/#in-effect
+bool
+AnimationEffectReadOnly::IsInEffect() const
+{
+  ComputedTiming computedTiming = GetComputedTiming();
+  return !computedTiming.mProgress.IsNull();
+}
+
+already_AddRefed<AnimationEffectTimingReadOnly>
+AnimationEffectReadOnly::Timing()
+{
+  RefPtr<AnimationEffectTimingReadOnly> temp(mTiming);
+  return temp.forget();
+}
+
+void
+AnimationEffectReadOnly::SetSpecifiedTiming(const TimingParams& aTiming)
+{
+  if (mTiming->AsTimingParams() == aTiming) {
+    return;
+  }
+  mTiming->SetTimingParams(aTiming);
+  if (mAnimation) {
+    mAnimation->NotifyEffectTimingUpdated();
+  }
+  // NotifyEffectTimingUpdated will eventually cause
+  // NotifyAnimationTimingUpdated to be called on this object which will
+  // update our registration with the target element.
+}
+
+ComputedTiming
+AnimationEffectReadOnly::GetComputedTimingAt(
+    const Nullable<TimeDuration>& aLocalTime,
+    const TimingParams& aTiming,
+    double aPlaybackRate)
+{
+  const StickyTimeDuration zeroDuration;
+
+  // Always return the same object to benefit from return-value optimization.
+  ComputedTiming result;
+
+  if (aTiming.mDuration) {
+    MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
+               "Iteration duration should be positive");
+    result.mDuration = aTiming.mDuration.ref();
+  }
+
+  MOZ_ASSERT(aTiming.mIterations >= 0.0 && !IsNaN(aTiming.mIterations),
+             "mIterations should be nonnegative & finite, as ensured by "
+             "ValidateIterations or CSSParser");
+  result.mIterations = aTiming.mIterations;
+
+  MOZ_ASSERT(aTiming.mIterationStart >= 0.0,
+             "mIterationStart should be nonnegative, as ensured by "
+             "ValidateIterationStart");
+  result.mIterationStart = aTiming.mIterationStart;
+
+  result.mActiveDuration = aTiming.ActiveDuration();
+  result.mEndTime = aTiming.EndTime();
+  result.mFill = aTiming.mFill == dom::FillMode::Auto ?
+                 dom::FillMode::None :
+                 aTiming.mFill;
+
+  // The default constructor for ComputedTiming sets all other members to
+  // values consistent with an animation that has not been sampled.
+  if (aLocalTime.IsNull()) {
+    return result;
+  }
+  const TimeDuration& localTime = aLocalTime.Value();
+
+  // Calculate the time within the active interval.
+  // https://w3c.github.io/web-animations/#active-time
+  StickyTimeDuration activeTime;
+
+  StickyTimeDuration beforeActiveBoundary =
+    std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime);
+  StickyTimeDuration activeAfterBoundary =
+    std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration),
+             result.mEndTime);
+
+  if (localTime > activeAfterBoundary ||
+      (aPlaybackRate >= 0 && localTime == activeAfterBoundary)) {
+    result.mPhase = ComputedTiming::AnimationPhase::After;
+    if (!result.FillsForwards()) {
+      // The animation isn't active or filling at this time.
+      return result;
+    }
+    activeTime = std::max(std::min(result.mActiveDuration,
+                                   result.mActiveDuration + aTiming.mEndDelay),
+                          zeroDuration);
+  } else if (localTime < beforeActiveBoundary ||
+             (aPlaybackRate < 0 && localTime == beforeActiveBoundary)) {
+    result.mPhase = ComputedTiming::AnimationPhase::Before;
+    if (!result.FillsBackwards()) {
+      // The animation isn't active or filling at this time.
+      return result;
+    }
+    // activeTime is zero
+  } else {
+    MOZ_ASSERT(result.mActiveDuration != zeroDuration,
+               "How can we be in the middle of a zero-duration interval?");
+    result.mPhase = ComputedTiming::AnimationPhase::Active;
+    activeTime = localTime - aTiming.mDelay;
+  }
+
+  // Convert active time to a multiple of iterations.
+  // https://w3c.github.io/web-animations/#overall-progress
+  double overallProgress;
+  if (result.mDuration == zeroDuration) {
+    overallProgress = result.mPhase == ComputedTiming::AnimationPhase::Before
+                      ? 0.0
+                      : result.mIterations;
+  } else {
+    overallProgress = activeTime / result.mDuration;
+  }
+
+  // Factor in iteration start offset.
+  if (IsFinite(overallProgress)) {
+    overallProgress += result.mIterationStart;
+  }
+
+  // Determine the 0-based index of the current iteration.
+  // https://w3c.github.io/web-animations/#current-iteration
+  result.mCurrentIteration =
+    IsInfinite(result.mIterations) &&
+      result.mPhase == ComputedTiming::AnimationPhase::After
+    ? UINT64_MAX // In GetComputedTimingDictionary(),
+                 // we will convert this into Infinity
+    : static_cast<uint64_t>(overallProgress);
+
+  // Convert the overall progress to a fraction of a single iteration--the
+  // simply iteration progress.
+  // https://w3c.github.io/web-animations/#simple-iteration-progress
+  double progress = IsFinite(overallProgress)
+                    ? fmod(overallProgress, 1.0)
+                    : fmod(result.mIterationStart, 1.0);
+
+  // When we finish exactly at the end of an iteration we need to report
+  // the end of the final iteration and not the start of the next iteration.
+  // We *don't* want to do this when we have a zero-iteration animation or
+  // when the animation has been effectively made into a zero-duration animation
+  // using a negative end-delay, however.
+  if (result.mPhase == ComputedTiming::AnimationPhase::After &&
+      progress == 0.0 &&
+      result.mIterations != 0.0 &&
+      (activeTime != zeroDuration || result.mDuration == zeroDuration)) {
+    // The only way we can be in the after phase with a progress of zero and
+    // a current iteration of zero, is if we have a zero iteration count or
+    // were clipped using a negative end delay--both of which we should have
+    // detected above.
+    MOZ_ASSERT(result.mCurrentIteration != 0,
+               "Should not have zero current iteration");
+    progress = 1.0;
+    if (result.mCurrentIteration != UINT64_MAX) {
+      result.mCurrentIteration--;
+    }
+  }
+
+  // Factor in the direction.
+  bool thisIterationReverse = false;
+  switch (aTiming.mDirection) {
+    case PlaybackDirection::Normal:
+      thisIterationReverse = false;
+      break;
+    case PlaybackDirection::Reverse:
+      thisIterationReverse = true;
+      break;
+    case PlaybackDirection::Alternate:
+      thisIterationReverse = (result.mCurrentIteration & 1) == 1;
+      break;
+    case PlaybackDirection::Alternate_reverse:
+      thisIterationReverse = (result.mCurrentIteration & 1) == 0;
+      break;
+    default:
+      MOZ_ASSERT(true, "Unknown PlaybackDirection type");
+  }
+  if (thisIterationReverse) {
+    progress = 1.0 - progress;
+  }
+
+  // Calculate the 'before flag' which we use when applying step timing
+  // functions.
+  if ((result.mPhase == ComputedTiming::AnimationPhase::After &&
+       thisIterationReverse) ||
+      (result.mPhase == ComputedTiming::AnimationPhase::Before &&
+       !thisIterationReverse)) {
+    result.mBeforeFlag = ComputedTimingFunction::BeforeFlag::Set;
+  }
+
+  // Apply the easing.
+  if (aTiming.mFunction) {
+    progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag);
+  }
+
+  MOZ_ASSERT(IsFinite(progress), "Progress value should be finite");
+  result.mProgress.SetValue(progress);
+  return result;
+}
+
+ComputedTiming
+AnimationEffectReadOnly::GetComputedTiming(const TimingParams* aTiming) const
+{
+  double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
+  return GetComputedTimingAt(GetLocalTime(),
+                             aTiming ? *aTiming : SpecifiedTiming(),
+                             playbackRate);
+}
+
+// Helper functions for generating a ComputedTimingProperties dictionary
+static void
+GetComputedTimingDictionary(const ComputedTiming& aComputedTiming,
+                            const Nullable<TimeDuration>& aLocalTime,
+                            const TimingParams& aTiming,
+                            ComputedTimingProperties& aRetVal)
+{
+  // AnimationEffectTimingProperties
+  aRetVal.mDelay = aTiming.mDelay.ToMilliseconds();
+  aRetVal.mEndDelay = aTiming.mEndDelay.ToMilliseconds();
+  aRetVal.mFill = aComputedTiming.mFill;
+  aRetVal.mIterations = aComputedTiming.mIterations;
+  aRetVal.mIterationStart = aComputedTiming.mIterationStart;
+  aRetVal.mDuration.SetAsUnrestrictedDouble() =
+    aComputedTiming.mDuration.ToMilliseconds();
+  aRetVal.mDirection = aTiming.mDirection;
+
+  // ComputedTimingProperties
+  aRetVal.mActiveDuration = aComputedTiming.mActiveDuration.ToMilliseconds();
+  aRetVal.mEndTime = aComputedTiming.mEndTime.ToMilliseconds();
+  aRetVal.mLocalTime = AnimationUtils::TimeDurationToDouble(aLocalTime);
+  aRetVal.mProgress = aComputedTiming.mProgress;
+
+  if (!aRetVal.mProgress.IsNull()) {
+    // Convert the returned currentIteration into Infinity if we set
+    // (uint64_t) aComputedTiming.mCurrentIteration to UINT64_MAX
+    double iteration = aComputedTiming.mCurrentIteration == UINT64_MAX
+                       ? PositiveInfinity<double>()
+                       : static_cast<double>(aComputedTiming.mCurrentIteration);
+    aRetVal.mCurrentIteration.SetValue(iteration);
+  }
+}
+
+void
+AnimationEffectReadOnly::GetComputedTimingAsDict(
+  ComputedTimingProperties& aRetVal) const
+{
+  double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
+  const Nullable<TimeDuration> currentTime = GetLocalTime();
+  GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
+                                                  SpecifiedTiming(),
+                                                  playbackRate),
+                              currentTime,
+                              SpecifiedTiming(),
+                              aRetVal);
+}
+
+void
+AnimationEffectReadOnly::SetAnimation(Animation* aAnimation)
+{
+  mAnimation = aAnimation;
+  NotifyAnimationTimingUpdated();
+}
+
+AnimationEffectReadOnly::~AnimationEffectReadOnly()
+{
+  // mTiming is cycle collected, so we have to do null check first even though
+  // mTiming shouldn't be null during the lifetime of KeyframeEffect.
+  if (mTiming) {
+    mTiming->Unlink();
+  }
+}
+
+Nullable<TimeDuration>
+AnimationEffectReadOnly::GetLocalTime() const
+{
+  // Since the *animation* start time is currently always zero, the local
+  // time is equal to the parent time.
+  Nullable<TimeDuration> result;
+  if (mAnimation) {
+    result = mAnimation->GetCurrentTime();
+  }
+  return result;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/AnimationEffectReadOnly.h
+++ b/dom/animation/AnimationEffectReadOnly.h
@@ -2,17 +2,24 @@
 /* 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_dom_AnimationEffectReadOnly_h
 #define mozilla_dom_AnimationEffectReadOnly_h
 
+#include "mozilla/ComputedTiming.h"
+#include "mozilla/dom/AnimationEffectTimingReadOnly.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/StickyTimeDuration.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/TimingParams.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 
 struct ElementPropertyTransition;
 
 namespace dom {
@@ -24,52 +31,70 @@ struct ComputedTimingProperties;
 
 class AnimationEffectReadOnly : public nsISupports,
                                 public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationEffectReadOnly)
 
-  explicit AnimationEffectReadOnly(nsIDocument* aDocument)
-    : mDocument(aDocument)
-  {
-  }
+  AnimationEffectReadOnly(nsIDocument* aDocument,
+                          AnimationEffectTimingReadOnly* aTiming);
 
   virtual KeyframeEffectReadOnly* AsKeyframeEffect() { return nullptr; }
 
   virtual ElementPropertyTransition* AsTransition() { return nullptr; }
   virtual const ElementPropertyTransition* AsTransition() const
   {
     return nullptr;
   }
 
   nsISupports* GetParentObject() const { return mDocument; }
 
-  virtual bool IsInPlay() const = 0;
-  virtual bool IsCurrent() const = 0;
-  virtual bool IsInEffect() const = 0;
+  bool IsInPlay() const;
+  bool IsCurrent() const;
+  bool IsInEffect() const;
 
-  virtual already_AddRefed<AnimationEffectTimingReadOnly> Timing() const = 0;
-  virtual const TimingParams& SpecifiedTiming() const = 0;
-  virtual void SetSpecifiedTiming(const TimingParams& aTiming) = 0;
+  already_AddRefed<AnimationEffectTimingReadOnly> Timing();
+  const TimingParams& SpecifiedTiming() const
+  {
+    return mTiming->AsTimingParams();
+  }
+  void SetSpecifiedTiming(const TimingParams& aTiming);
   virtual void NotifyAnimationTimingUpdated() = 0;
 
+  // This function takes as input the timing parameters of an animation and
+  // returns the computed timing at the specified local time.
+  //
+  // The local time may be null in which case only static parameters such as the
+  // active duration are calculated. All other members of the returned object
+  // are given a null/initial value.
+  //
+  // This function returns a null mProgress member of the return value
+  // if the animation should not be run
+  // (because it is not currently active and is not filling at this time).
+  static ComputedTiming
+  GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
+                      const TimingParams& aTiming,
+                      double aPlaybackRate);
   // Shortcut that gets the computed timing using the current local time as
   // calculated from the timeline time.
-  virtual ComputedTiming GetComputedTiming(
-    const TimingParams* aTiming = nullptr) const = 0;
-  virtual void GetComputedTimingAsDict(
-    ComputedTimingProperties& aRetVal) const = 0;
+  ComputedTiming GetComputedTiming(const TimingParams* aTiming = nullptr) const;
+  void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const;
 
-  virtual void SetAnimation(Animation* aAnimation) = 0;
+  void SetAnimation(Animation* aAnimation);
+  Animation* GetAnimation() const { return mAnimation; };
 
 protected:
-  virtual ~AnimationEffectReadOnly() = default;
+  virtual ~AnimationEffectReadOnly();
+
+  Nullable<TimeDuration> GetLocalTime() const;
 
 protected:
   RefPtr<nsIDocument> mDocument;
+  RefPtr<AnimationEffectTimingReadOnly> mTiming;
+  RefPtr<Animation> mAnimation;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AnimationEffectReadOnly_h
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -19,65 +19,21 @@
 #include "nsCSSPropertyIDSet.h"
 #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
 #include "nsCSSPseudoElements.h" // For CSSPseudoElementType
 #include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
 #include "nsIPresShell.h" // For nsIPresShell
 #include "nsIScriptError.h"
 
 namespace mozilla {
-
-// Helper functions for generating a ComputedTimingProperties dictionary
-static void
-GetComputedTimingDictionary(const ComputedTiming& aComputedTiming,
-                            const Nullable<TimeDuration>& aLocalTime,
-                            const TimingParams& aTiming,
-                            dom::ComputedTimingProperties& aRetVal)
-{
-  // AnimationEffectTimingProperties
-  aRetVal.mDelay = aTiming.mDelay.ToMilliseconds();
-  aRetVal.mEndDelay = aTiming.mEndDelay.ToMilliseconds();
-  aRetVal.mFill = aComputedTiming.mFill;
-  aRetVal.mIterations = aComputedTiming.mIterations;
-  aRetVal.mIterationStart = aComputedTiming.mIterationStart;
-  aRetVal.mDuration.SetAsUnrestrictedDouble() =
-    aComputedTiming.mDuration.ToMilliseconds();
-  aRetVal.mDirection = aTiming.mDirection;
-
-  // ComputedTimingProperties
-  aRetVal.mActiveDuration = aComputedTiming.mActiveDuration.ToMilliseconds();
-  aRetVal.mEndTime = aComputedTiming.mEndTime.ToMilliseconds();
-  aRetVal.mLocalTime = AnimationUtils::TimeDurationToDouble(aLocalTime);
-  aRetVal.mProgress = aComputedTiming.mProgress;
-
-  if (!aRetVal.mProgress.IsNull()) {
-    // Convert the returned currentIteration into Infinity if we set
-    // (uint64_t) aComputedTiming.mCurrentIteration to UINT64_MAX
-    double iteration = aComputedTiming.mCurrentIteration == UINT64_MAX
-                     ? PositiveInfinity<double>()
-                     : static_cast<double>(aComputedTiming.mCurrentIteration);
-    aRetVal.mCurrentIteration.SetValue(iteration);
-  }
-}
-
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(KeyframeEffectReadOnly)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(KeyframeEffectReadOnly,
-                                                AnimationEffectReadOnly)
-  if (tmp->mTiming) {
-    tmp->mTiming->Unlink();
-  }
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTarget, mAnimation, mTiming)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(KeyframeEffectReadOnly,
-                                                  AnimationEffectReadOnly)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget, mAnimation, mTiming)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly,
+                                   AnimationEffectReadOnly,
+                                   mTarget)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly,
                                                AnimationEffectReadOnly)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly)
 NS_INTERFACE_MAP_END_INHERITING(AnimationEffectReadOnly)
 
@@ -96,24 +52,22 @@ KeyframeEffectReadOnly::KeyframeEffectRe
 {
 }
 
 KeyframeEffectReadOnly::KeyframeEffectReadOnly(
   nsIDocument* aDocument,
   const Maybe<OwningAnimationTarget>& aTarget,
   AnimationEffectTimingReadOnly* aTiming,
   const KeyframeEffectParams& aOptions)
-  : AnimationEffectReadOnly(aDocument)
+  : AnimationEffectReadOnly(aDocument, aTiming)
   , mTarget(aTarget)
-  , mTiming(aTiming)
   , mEffectOptions(aOptions)
   , mInEffectOnLastAnimationTimingUpdate(false)
   , mCumulativeChangeHint(nsChangeHint(0))
 {
-  MOZ_ASSERT(aTiming);
 }
 
 JSObject*
 KeyframeEffectReadOnly::WrapObject(JSContext* aCx,
                                    JS::Handle<JSObject*> aGivenProto)
 {
   return KeyframeEffectReadOnlyBinding::Wrap(aCx, this, aGivenProto);
 }
@@ -125,38 +79,16 @@ KeyframeEffectReadOnly::IterationComposi
 }
 
 CompositeOperation
 KeyframeEffectReadOnly::Composite() const
 {
   return CompositeOperation::Replace;
 }
 
-already_AddRefed<AnimationEffectTimingReadOnly>
-KeyframeEffectReadOnly::Timing() const
-{
-  RefPtr<AnimationEffectTimingReadOnly> temp(mTiming);
-  return temp.forget();
-}
-
-void
-KeyframeEffectReadOnly::SetSpecifiedTiming(const TimingParams& aTiming)
-{
-  if (mTiming->AsTimingParams() == aTiming) {
-    return;
-  }
-  mTiming->SetTimingParams(aTiming);
-  if (mAnimation) {
-    mAnimation->NotifyEffectTimingUpdated();
-  }
-  // NotifyEffectTimingUpdated will eventually cause
-  // NotifyAnimationTimingUpdated to be called on this object which will
-  // update our registration with the target element.
-}
-
 void
 KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
 {
   UpdateTargetRegistration();
 
   // If the effect is not relevant it will be removed from the target
   // element's effect set. However, effects not in the effect set
   // will not be included in the set of candidate effects for running on
@@ -200,259 +132,16 @@ KeyframeEffectReadOnly::NotifyAnimationT
   // called and we will never have a chance to update mProgressOnLastCompose.
   // We clear mProgressOnLastCompose here to ensure that if we later become
   // "in effect" we will request a restyle (above).
   if (!inEffect) {
      mProgressOnLastCompose.SetNull();
   }
 }
 
-Nullable<TimeDuration>
-KeyframeEffectReadOnly::GetLocalTime() const
-{
-  // Since the *animation* start time is currently always zero, the local
-  // time is equal to the parent time.
-  Nullable<TimeDuration> result;
-  if (mAnimation) {
-    result = mAnimation->GetCurrentTime();
-  }
-  return result;
-}
-
-void
-KeyframeEffectReadOnly::GetComputedTimingAsDict(
-    ComputedTimingProperties& aRetVal) const
-{
-  double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
-  const Nullable<TimeDuration> currentTime = GetLocalTime();
-  GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
-                                                  SpecifiedTiming(),
-                                                  playbackRate),
-                              currentTime,
-                              SpecifiedTiming(),
-                              aRetVal);
-}
-
-ComputedTiming
-KeyframeEffectReadOnly::GetComputedTimingAt(
-    const Nullable<TimeDuration>& aLocalTime,
-    const TimingParams& aTiming,
-    double aPlaybackRate)
-{
-  const StickyTimeDuration zeroDuration;
-
-  // Always return the same object to benefit from return-value optimization.
-  ComputedTiming result;
-
-  if (aTiming.mDuration) {
-    MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
-               "Iteration duration should be positive");
-    result.mDuration = aTiming.mDuration.ref();
-  }
-
-  MOZ_ASSERT(aTiming.mIterations >= 0.0 && !IsNaN(aTiming.mIterations),
-             "mIterations should be nonnegative & finite, as ensured by "
-             "ValidateIterations or CSSParser");
-  result.mIterations = aTiming.mIterations;
-
-  MOZ_ASSERT(aTiming.mIterationStart >= 0.0,
-             "mIterationStart should be nonnegative, as ensured by "
-             "ValidateIterationStart");
-  result.mIterationStart = aTiming.mIterationStart;
-
-  result.mActiveDuration = aTiming.ActiveDuration();
-  result.mEndTime = aTiming.EndTime();
-  result.mFill = aTiming.mFill == dom::FillMode::Auto ?
-                 dom::FillMode::None :
-                 aTiming.mFill;
-
-  // The default constructor for ComputedTiming sets all other members to
-  // values consistent with an animation that has not been sampled.
-  if (aLocalTime.IsNull()) {
-    return result;
-  }
-  const TimeDuration& localTime = aLocalTime.Value();
-
-  // Calculate the time within the active interval.
-  // https://w3c.github.io/web-animations/#active-time
-  StickyTimeDuration activeTime;
-
-  StickyTimeDuration beforeActiveBoundary =
-    std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime);
-  StickyTimeDuration activeAfterBoundary =
-    std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration),
-             result.mEndTime);
-
-  if (localTime > activeAfterBoundary ||
-      (aPlaybackRate >= 0 && localTime == activeAfterBoundary)) {
-    result.mPhase = ComputedTiming::AnimationPhase::After;
-    if (!result.FillsForwards()) {
-      // The animation isn't active or filling at this time.
-      return result;
-    }
-    activeTime = std::max(std::min(result.mActiveDuration,
-                                   result.mActiveDuration + aTiming.mEndDelay),
-                          zeroDuration);
-  } else if (localTime < beforeActiveBoundary ||
-             (aPlaybackRate < 0 && localTime == beforeActiveBoundary)) {
-    result.mPhase = ComputedTiming::AnimationPhase::Before;
-    if (!result.FillsBackwards()) {
-      // The animation isn't active or filling at this time.
-      return result;
-    }
-    // activeTime is zero
-  } else {
-    MOZ_ASSERT(result.mActiveDuration != zeroDuration,
-               "How can we be in the middle of a zero-duration interval?");
-    result.mPhase = ComputedTiming::AnimationPhase::Active;
-    activeTime = localTime - aTiming.mDelay;
-  }
-
-  // Convert active time to a multiple of iterations.
-  // https://w3c.github.io/web-animations/#overall-progress
-  double overallProgress;
-  if (result.mDuration == zeroDuration) {
-    overallProgress = result.mPhase == ComputedTiming::AnimationPhase::Before
-                      ? 0.0
-                      : result.mIterations;
-  } else {
-    overallProgress = activeTime / result.mDuration;
-  }
-
-  // Factor in iteration start offset.
-  if (IsFinite(overallProgress)) {
-    overallProgress += result.mIterationStart;
-  }
-
-  // Determine the 0-based index of the current iteration.
-  // https://w3c.github.io/web-animations/#current-iteration
-  result.mCurrentIteration =
-    IsInfinite(result.mIterations) &&
-      result.mPhase == ComputedTiming::AnimationPhase::After
-    ? UINT64_MAX // In GetComputedTimingDictionary(),
-                 // we will convert this into Infinity
-    : static_cast<uint64_t>(overallProgress);
-
-  // Convert the overall progress to a fraction of a single iteration--the
-  // simply iteration progress.
-  // https://w3c.github.io/web-animations/#simple-iteration-progress
-  double progress = IsFinite(overallProgress)
-                    ? fmod(overallProgress, 1.0)
-                    : fmod(result.mIterationStart, 1.0);
-
-  // When we finish exactly at the end of an iteration we need to report
-  // the end of the final iteration and not the start of the next iteration.
-  // We *don't* want to do this when we have a zero-iteration animation or
-  // when the animation has been effectively made into a zero-duration animation
-  // using a negative end-delay, however.
-  if (result.mPhase == ComputedTiming::AnimationPhase::After &&
-      progress == 0.0 &&
-      result.mIterations != 0.0 &&
-      (activeTime != zeroDuration || result.mDuration == zeroDuration)) {
-    // The only way we can be in the after phase with a progress of zero and
-    // a current iteration of zero, is if we have a zero iteration count or
-    // were clipped using a negative end delay--both of which we should have
-    // detected above.
-    MOZ_ASSERT(result.mCurrentIteration != 0,
-               "Should not have zero current iteration");
-    progress = 1.0;
-    if (result.mCurrentIteration != UINT64_MAX) {
-      result.mCurrentIteration--;
-    }
-  }
-
-  // Factor in the direction.
-  bool thisIterationReverse = false;
-  switch (aTiming.mDirection) {
-    case PlaybackDirection::Normal:
-      thisIterationReverse = false;
-      break;
-    case PlaybackDirection::Reverse:
-      thisIterationReverse = true;
-      break;
-    case PlaybackDirection::Alternate:
-      thisIterationReverse = (result.mCurrentIteration & 1) == 1;
-      break;
-    case PlaybackDirection::Alternate_reverse:
-      thisIterationReverse = (result.mCurrentIteration & 1) == 0;
-      break;
-    default:
-      MOZ_ASSERT(true, "Unknown PlaybackDirection type");
-  }
-  if (thisIterationReverse) {
-    progress = 1.0 - progress;
-  }
-
-  // Calculate the 'before flag' which we use when applying step timing
-  // functions.
-  if ((result.mPhase == ComputedTiming::AnimationPhase::After &&
-       thisIterationReverse) ||
-      (result.mPhase == ComputedTiming::AnimationPhase::Before &&
-       !thisIterationReverse)) {
-    result.mBeforeFlag = ComputedTimingFunction::BeforeFlag::Set;
-  }
-
-  // Apply the easing.
-  if (aTiming.mFunction) {
-    progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag);
-  }
-
-  MOZ_ASSERT(IsFinite(progress), "Progress value should be finite");
-  result.mProgress.SetValue(progress);
-  return result;
-}
-
-ComputedTiming
-KeyframeEffectReadOnly::GetComputedTiming(const TimingParams* aTiming) const
-{
-  double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
-  return GetComputedTimingAt(GetLocalTime(),
-                             aTiming ? *aTiming : SpecifiedTiming(),
-                             playbackRate);
-}
-
-// https://w3c.github.io/web-animations/#in-play
-bool
-KeyframeEffectReadOnly::IsInPlay() const
-{
-  if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
-    return false;
-  }
-
-  return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase::Active;
-}
-
-// https://w3c.github.io/web-animations/#current
-bool
-KeyframeEffectReadOnly::IsCurrent() const
-{
-  if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
-    return false;
-  }
-
-  ComputedTiming computedTiming = GetComputedTiming();
-  return computedTiming.mPhase == ComputedTiming::AnimationPhase::Before ||
-         computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
-}
-
-// https://w3c.github.io/web-animations/#in-effect
-bool
-KeyframeEffectReadOnly::IsInEffect() const
-{
-  ComputedTiming computedTiming = GetComputedTiming();
-  return !computedTiming.mProgress.IsNull();
-}
-
-void
-KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
-{
-  mAnimation = aAnimation;
-  NotifyAnimationTimingUpdated();
-}
-
 static bool
 KeyframesEqualIgnoringComputedOffsets(const nsTArray<Keyframe>& aLhs,
                                       const nsTArray<Keyframe>& aRhs)
 {
   if (aLhs.Length() != aRhs.Length()) {
     return false;
   }
 
@@ -767,20 +456,16 @@ KeyframeEffectReadOnly::SetIsRunningOnCo
 void
 KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
 {
   for (AnimationProperty& property : mProperties) {
     property.mIsRunningOnCompositor = false;
   }
 }
 
-KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
-{
-}
-
 static const KeyframeEffectOptions&
 KeyframeEffectOptionsFromUnion(
   const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions)
 {
   MOZ_ASSERT(aOptions.IsKeyframeEffectOptions());
   return aOptions.GetAsKeyframeEffectOptions();
 }
 
@@ -1678,19 +1363,10 @@ KeyframeEffect::SetTarget(const Nullable
       nsNodeUtils::AnimationAdded(mAnimation);
     }
   } else if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
     // New target is null, so fall back to distribute spacing.
     KeyframeUtils::ApplyDistributeSpacing(mKeyframes);
   }
 }
 
-KeyframeEffect::~KeyframeEffect()
-{
-  // mTiming is cycle collected, so we have to do null check first even though
-  // mTiming shouldn't be null during the lifetime of KeyframeEffect.
-  if (mTiming) {
-    mTiming->Unlink();
-  }
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -11,30 +11,23 @@
 #include "nsCSSPropertyID.h"
 #include "nsCSSValue.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/AnimationTarget.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/ComputedTiming.h"
 #include "mozilla/ComputedTimingFunction.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/KeyframeEffectParams.h"
 #include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords
-#include "mozilla/Maybe.h"
-#include "mozilla/StickyTimeDuration.h"
 #include "mozilla/StyleAnimationValue.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/TimingParams.h"
 #include "mozilla/dom/AnimationEffectReadOnly.h"
-#include "mozilla/dom/AnimationEffectTimingReadOnly.h"
 #include "mozilla/dom/Element.h"
-#include "mozilla/dom/Nullable.h"
 
 struct JSContext;
 class nsCSSPropertyIDSet;
 class nsIContent;
 class nsIDocument;
 class nsIFrame;
 class nsIPresShell;
 class nsPresContext;
@@ -232,55 +225,18 @@ public:
 
   IterationCompositeOperation IterationComposite() const;
   CompositeOperation Composite() const;
   void GetSpacing(nsString& aRetVal) const
   {
     mEffectOptions.GetSpacingAsString(aRetVal);
   }
 
-  already_AddRefed<AnimationEffectTimingReadOnly> Timing() const override;
-
-  const TimingParams& SpecifiedTiming() const override
-  {
-    return mTiming->AsTimingParams();
-  }
-  void SetSpecifiedTiming(const TimingParams& aTiming) override;
   void NotifyAnimationTimingUpdated() override;
 
-  Nullable<TimeDuration> GetLocalTime() const;
-
-  // This function takes as input the timing parameters of an animation and
-  // returns the computed timing at the specified local time.
-  //
-  // The local time may be null in which case only static parameters such as the
-  // active duration are calculated. All other members of the returned object
-  // are given a null/initial value.
-  //
-  // This function returns a null mProgress member of the return value
-  // if the animation should not be run
-  // (because it is not currently active and is not filling at this time).
-  static ComputedTiming
-  GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
-                      const TimingParams& aTiming,
-                      double aPlaybackRate);
-
-  ComputedTiming
-  GetComputedTiming(const TimingParams* aTiming = nullptr) const override;
-
-  void
-  GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const override;
-
-  bool IsInPlay() const override;
-  bool IsCurrent() const override;
-  bool IsInEffect() const override;
-
-  void SetAnimation(Animation* aAnimation) override;
-  Animation* GetAnimation() const { return mAnimation; }
-
   void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
                     ErrorResult& aRv);
   void SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
                     nsStyleContext* aStyleContext);
   const AnimationProperty*
   GetAnimationOfProperty(nsCSSPropertyID aProperty) const;
   bool HasAnimationOfProperty(nsCSSPropertyID aProperty) const
   {
@@ -346,17 +302,17 @@ public:
   bool CanIgnoreIfNotVisible() const;
 
 protected:
   KeyframeEffectReadOnly(nsIDocument* aDocument,
                          const Maybe<OwningAnimationTarget>& aTarget,
                          AnimationEffectTimingReadOnly* aTiming,
                          const KeyframeEffectParams& aOptions);
 
-  virtual ~KeyframeEffectReadOnly();
+  ~KeyframeEffectReadOnly() override = default;
 
   template<class KeyframeEffectType, class OptionsType>
   static already_AddRefed<KeyframeEffectType>
   ConstructKeyframeEffect(const GlobalObject& aGlobal,
                           const Nullable<ElementOrCSSPseudoElement>& aTarget,
                           JS::Handle<JSObject*> aKeyframes,
                           const OptionsType& aOptions,
                           ErrorResult& aRv);
@@ -389,19 +345,17 @@ protected:
   // We need to be careful to *not* call this when we are updating the style
   // context. That's because calling GetStyleContextForElement when we are in
   // the process of building a style context may trigger various forms of
   // infinite recursion.
   already_AddRefed<nsStyleContext>
   GetTargetStyleContext();
 
   Maybe<OwningAnimationTarget> mTarget;
-  RefPtr<Animation> mAnimation;
 
-  RefPtr<AnimationEffectTimingReadOnly> mTiming;
   KeyframeEffectParams mEffectOptions;
 
   // The specified keyframes.
   nsTArray<Keyframe>          mKeyframes;
 
   // A set of per-property value arrays, derived from |mKeyframes|.
   nsTArray<AnimationProperty> mProperties;
 
@@ -464,17 +418,14 @@ public:
   void NotifySpecifiedTimingUpdated();
 
   // This method calls GetTargetStyleContext which is not safe to use when
   // we are in the middle of updating style. If we need to use this when
   // updating style, we should pass the nsStyleContext into this method and use
   // that to update the properties rather than calling
   // GetStyleContextForElement.
   void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
-
-protected:
-  ~KeyframeEffect() override;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_KeyframeEffect_h
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -646,17 +646,17 @@ SampleAnimations(Layer* aLayer, TimeStam
           // example, while they are waiting to be removed) we currently just
           // assume that we should fill.
           timing.mFill = dom::FillMode::Both;
           timing.mFunction =
             AnimationUtils::TimingFunctionToComputedTimingFunction(
               animation.easingFunction());
 
           ComputedTiming computedTiming =
-            dom::KeyframeEffectReadOnly::GetComputedTimingAt(
+            dom::AnimationEffectReadOnly::GetComputedTimingAt(
               Nullable<TimeDuration>(elapsedDuration), timing,
               animation.playbackRate());
 
           MOZ_ASSERT(!computedTiming.mProgress.IsNull(),
                      "iteration progress should not be null");
 
           uint32_t segmentIndex = 0;
           size_t segmentSize = animation.segments().Length();