Bug 1436659 - Implement pending playback rate mechanism; r?hiro draft
authorBrian Birtles <birtles@gmail.com>
Tue, 13 Feb 2018 15:04:18 +0900
changeset 755204 a9c1e4f2c5621c3c5c5bb629b8642ab8b4c4fe5d
parent 755203 081e7dda9d5de988f0235102154571ba8f82e5eb
child 755205 128a32d4a860526a26ead233cf6145c5311bd199
push id99115
push userbmo:bbirtles@mozilla.com
push dateWed, 14 Feb 2018 23:26:48 +0000
reviewershiro
bugs1436659
milestone60.0a1
Bug 1436659 - Implement pending playback rate mechanism; r?hiro This reflects the following changes to the Web Animations specification: 1. https://github.com/w3c/csswg-drafts/commit/5af5e276badf4df0271bcfa0b8e7837fff24133a 2. https://github.com/w3c/csswg-drafts/commit/673f6fc1269829743c707c53dcb04092f958de35 which can be viewed as a merged diff at: https://gist.github.com/birtles/d147eb2e0e2d4d37fadf217abd709411 MozReview-Commit-ID: 3DoaWUkxBTo
dom/animation/Animation.cpp
dom/animation/Animation.h
testing/web-platform/meta/web-animations/timing-model/animations/finishing-an-animation.html.ini
testing/web-platform/meta/web-animations/timing-model/animations/playing-an-animation.html.ini
testing/web-platform/meta/web-animations/timing-model/animations/reversing-an-animation.html.ini
testing/web-platform/meta/web-animations/timing-model/animations/seamlessly-updating-the-playback-rate-of-an-animation.html.ini
testing/web-platform/meta/web-animations/timing-model/animations/setting-the-current-time-of-an-animation.html.ini
testing/web-platform/meta/web-animations/timing-model/animations/setting-the-start-time-of-an-animation.html.ini
testing/web-platform/meta/web-animations/timing-model/animations/setting-the-target-effect-of-an-animation.html.ini
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -268,17 +268,20 @@ Animation::SetStartTime(const Nullable<T
     // the already null time to null.
     timelineTime = mTimeline->GetCurrentTime();
   }
   if (timelineTime.IsNull() && !aNewStartTime.IsNull()) {
     mHoldTime.SetNull();
   }
 
   Nullable<TimeDuration> previousCurrentTime = GetCurrentTime();
+
+  ApplyPendingPlaybackRate();
   mStartTime = aNewStartTime;
+
   if (!aNewStartTime.IsNull()) {
     if (mPlaybackRate != 0.0) {
       mHoldTime.SetNull();
     }
   } else {
     mHoldTime = previousCurrentTime;
   }
 
@@ -332,35 +335,39 @@ Animation::SetCurrentTime(const TimeDura
 
   AutoMutationBatchForAnimation mb(*this);
 
   SilentlySetCurrentTime(aSeekTime);
 
   if (mPendingState == PendingState::PausePending) {
     // Finish the pause operation
     mHoldTime.SetValue(aSeekTime);
+
+    ApplyPendingPlaybackRate();
     mStartTime.SetNull();
 
     if (mReady) {
       mReady->MaybeResolve(this);
     }
     CancelPendingTasks();
   }
 
   UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
   if (IsRelevant()) {
     nsNodeUtils::AnimationChanged(this);
   }
   PostUpdate();
 }
 
-// https://drafts.csswg.org/web-animations/#set-the-animation-playback-rate
+// https://drafts.csswg.org/web-animations/#set-the-playback-rate
 void
 Animation::SetPlaybackRate(double aPlaybackRate)
 {
+  mPendingPlaybackRate.reset();
+
   if (aPlaybackRate == mPlaybackRate) {
     return;
   }
 
   AutoMutationBatchForAnimation mb(*this);
 
   Nullable<TimeDuration> previousTime = GetCurrentTime();
   mPlaybackRate = aPlaybackRate;
@@ -378,16 +385,93 @@ Animation::SetPlaybackRate(double aPlayb
   // - update the playback rate on animations on layers.
   UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
   if (IsRelevant()) {
     nsNodeUtils::AnimationChanged(this);
   }
   PostUpdate();
 }
 
+// https://drafts.csswg.org/web-animations/#seamlessly-update-the-playback-rate
+void
+Animation::UpdatePlaybackRate(double aPlaybackRate)
+{
+  if (mPendingPlaybackRate && mPendingPlaybackRate.value() == aPlaybackRate) {
+    return;
+  }
+
+  mPendingPlaybackRate = Some(aPlaybackRate);
+
+  // If we already have a pending task, there is nothing more to do since the
+  // playback rate will be applied then.
+  if (Pending()) {
+    return;
+  }
+
+  AutoMutationBatchForAnimation mb(*this);
+
+  AnimationPlayState playState = PlayState();
+  if (playState == AnimationPlayState::Idle ||
+      playState == AnimationPlayState::Paused) {
+    // We are either idle or paused. In either case we can apply the pending
+    // playback rate immediately.
+    ApplyPendingPlaybackRate();
+
+    // We don't need to update timing or post an update here because:
+    //
+    // * the current time hasn't changed -- it's either unresolved or fixed
+    //   with a hold time -- so the output won't have changed
+    // * the finished state won't have changed even if the sign of the
+    //   playback rate changed since we're not finished (we're paused or idle)
+    // * the playback rate on layers doesn't need to be updated since we're not
+    //   moving. Once we get a start time etc. we'll update the playback rate
+    //   then.
+    //
+    // All we need to do is update observers so that, e.g. DevTools, report the
+    // right information.
+    if (IsRelevant()) {
+      nsNodeUtils::AnimationChanged(this);
+    }
+  } else if (playState == AnimationPlayState::Finished) {
+    MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
+               "If we have no active timeline, we should be idle or paused");
+    if (aPlaybackRate != 0) {
+      // The unconstrained current time can only be unresolved if either we
+      // don't have an active timeline (and we already asserted that is not
+      // true) or we have an unresolved start time (in which case we should be
+      // paused).
+      MOZ_ASSERT(!GetUnconstrainedCurrentTime().IsNull(),
+                 "Unconstrained current time should be resolved");
+      TimeDuration unconstrainedCurrentTime =
+        GetUnconstrainedCurrentTime().Value();
+      TimeDuration timelineTime = mTimeline->GetCurrentTime().Value();
+      mStartTime = StartTimeFromTimelineTime(
+        timelineTime, unconstrainedCurrentTime, aPlaybackRate);
+    } else {
+      mStartTime = mTimeline->GetCurrentTime();
+    }
+
+    ApplyPendingPlaybackRate();
+
+    // Even though we preserve the current time, we might now leave the finished
+    // state (e.g. if the playback rate changes sign) so we need to update
+    // timing.
+    UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
+    if (IsRelevant()) {
+      nsNodeUtils::AnimationChanged(this);
+    }
+    PostUpdate();
+  } else {
+    ErrorResult rv;
+    Play(rv, LimitBehavior::Continue);
+    MOZ_ASSERT(!rv.Failed(),
+               "We should only fail to play when using auto-rewind behavior");
+  }
+}
+
 // https://drafts.csswg.org/web-animations/#play-state
 AnimationPlayState
 Animation::PlayState() const
 {
   if (!nsContentUtils::AnimationsAPIPendingMemberEnabled() && Pending()) {
     return AnimationPlayState::Pending;
   }
 
@@ -450,24 +534,28 @@ Animation::Cancel()
   CancelNoUpdate();
   PostUpdate();
 }
 
 // https://drafts.csswg.org/web-animations/#finish-an-animation
 void
 Animation::Finish(ErrorResult& aRv)
 {
-  if (mPlaybackRate == 0 ||
-      (mPlaybackRate > 0 && EffectEnd() == TimeDuration::Forever())) {
+  double effectivePlaybackRate = CurrentOrPendingPlaybackRate();
+
+  if (effectivePlaybackRate == 0 ||
+      (effectivePlaybackRate > 0 && EffectEnd() == TimeDuration::Forever())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   AutoMutationBatchForAnimation mb(*this);
 
+  ApplyPendingPlaybackRate();
+
   // Seek to the end
   TimeDuration limit =
     mPlaybackRate > 0 ? TimeDuration(EffectEnd()) : TimeDuration(0);
   bool didChange = GetCurrentTime() != Nullable<TimeDuration>(limit);
   SilentlySetCurrentTime(limit);
 
   // If we are paused or play-pending we need to fill in the start time in
   // order to transition to the finished state.
@@ -525,35 +613,34 @@ Animation::Pause(ErrorResult& aRv)
 void
 Animation::Reverse(ErrorResult& aRv)
 {
   if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  if (mPlaybackRate == 0.0) {
+  double effectivePlaybackRate = CurrentOrPendingPlaybackRate();
+
+  if (effectivePlaybackRate == 0.0) {
     return;
   }
 
-  AutoMutationBatchForAnimation mb(*this);
+  Maybe<double> originalPendingPlaybackRate = mPendingPlaybackRate;
 
-  SilentlySetPlaybackRate(-mPlaybackRate);
+  mPendingPlaybackRate = Some(-effectivePlaybackRate);
+
   Play(aRv, LimitBehavior::AutoRewind);
 
   // If Play() threw, restore state and don't report anything to mutation
   // observers.
   if (aRv.Failed()) {
-    SilentlySetPlaybackRate(-mPlaybackRate);
-    return;
+    mPendingPlaybackRate = originalPendingPlaybackRate;
   }
 
-  if (IsRelevant()) {
-    nsNodeUtils::AnimationChanged(this);
-  }
   // Play(), above, unconditionally calls PostUpdate so we don't need to do
   // it here.
 }
 
 // ---------------------------------------------------------------------------
 //
 // JS wrappers for Animation interface:
 //
@@ -673,16 +760,37 @@ Animation::TriggerNow()
   FinishPendingAt(mTimeline->GetCurrentTime().Value());
 }
 
 Nullable<TimeDuration>
 Animation::GetCurrentOrPendingStartTime() const
 {
   Nullable<TimeDuration> result;
 
+  // If we have a pending playback rate, work out what start time we will use
+  // when we come to updating that playback rate.
+  //
+  // This logic roughly shadows that in ResumeAt but is just different enough
+  // that it is difficult to extract out the common functionality (and
+  // extracting that functionality out would make it harder to match ResumeAt up
+  // against the spec).
+  if (mPendingPlaybackRate && !mPendingReadyTime.IsNull() &&
+      !mStartTime.IsNull()) {
+    // If we have a hold time, use it as the current time to match.
+    TimeDuration currentTimeToMatch =
+      !mHoldTime.IsNull()
+        ? mHoldTime.Value()
+        : CurrentTimeFromTimelineTime(
+            mPendingReadyTime.Value(), mStartTime.Value(), mPlaybackRate);
+
+    result = StartTimeFromTimelineTime(
+      mPendingReadyTime.Value(), currentTimeToMatch, *mPendingPlaybackRate);
+    return result;
+  }
+
   if (!mStartTime.IsNull()) {
     result = mStartTime;
     return result;
   }
 
   if (mPendingReadyTime.IsNull() || mHoldTime.IsNull()) {
     return result;
   }
@@ -758,26 +866,16 @@ Animation::SilentlySetCurrentTime(const 
   } else {
     mStartTime = StartTimeFromTimelineTime(
       mTimeline->GetCurrentTime().Value(), aSeekTime, mPlaybackRate);
   }
 
   mPreviousCurrentTime.SetNull();
 }
 
-void
-Animation::SilentlySetPlaybackRate(double aPlaybackRate)
-{
-  Nullable<TimeDuration> previousTime = GetCurrentTime();
-  mPlaybackRate = aPlaybackRate;
-  if (!previousTime.IsNull()) {
-    SilentlySetCurrentTime(previousTime.Value());
-  }
-}
-
 // https://drafts.csswg.org/web-animations/#cancel-an-animation
 void
 Animation::CancelNoUpdate()
 {
   if (PlayState() != AnimationPlayState::Idle) {
     ResetPendingTasks();
 
     if (mFinished) {
@@ -1031,48 +1129,57 @@ Animation::NotifyGeometricAnimationsStar
 // https://drafts.csswg.org/web-animations/#play-an-animation
 void
 Animation::PlayNoUpdate(ErrorResult& aRv, LimitBehavior aLimitBehavior)
 {
   AutoMutationBatchForAnimation mb(*this);
 
   bool abortedPause = mPendingState == PendingState::PausePending;
 
+  double effectivePlaybackRate = CurrentOrPendingPlaybackRate();
+
   Nullable<TimeDuration> currentTime = GetCurrentTime();
-  if (mPlaybackRate > 0.0 &&
+  if (effectivePlaybackRate > 0.0 &&
       (currentTime.IsNull() ||
        (aLimitBehavior == LimitBehavior::AutoRewind &&
         (currentTime.Value() < TimeDuration() ||
          currentTime.Value() >= EffectEnd())))) {
     mHoldTime.SetValue(TimeDuration(0));
-  } else if (mPlaybackRate < 0.0 &&
+  } else if (effectivePlaybackRate < 0.0 &&
              (currentTime.IsNull() ||
               (aLimitBehavior == LimitBehavior::AutoRewind &&
                (currentTime.Value() <= TimeDuration() ||
                 currentTime.Value() > EffectEnd())))) {
     if (EffectEnd() == TimeDuration::Forever()) {
       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
       return;
     }
     mHoldTime.SetValue(TimeDuration(EffectEnd()));
-  } else if (mPlaybackRate == 0.0 && currentTime.IsNull()) {
+  } else if (effectivePlaybackRate == 0.0 && currentTime.IsNull()) {
     mHoldTime.SetValue(TimeDuration(0));
   }
 
   bool reuseReadyPromise = false;
   if (mPendingState != PendingState::NotPending) {
     CancelPendingTasks();
     reuseReadyPromise = true;
   }
 
-  // If the hold time is null then we're either already playing normally (and
-  // we can ignore this call) or we aborted a pending pause operation (in which
-  // case, for consistency, we need to go through the motions of doing an
-  // asynchronous start even though we already have a resolved start time).
-  if (mHoldTime.IsNull() && !abortedPause) {
+  // If the hold time is null then we're already playing normally and,
+  // typically, we can bail out here.
+  //
+  // However, there are two cases where we can't do that:
+  //
+  // (a) If we just aborted a pause. In this case, for consistency, we need to
+  //     go through the motions of doing an asynchronous start.
+  //
+  // (b) If we have timing changes (specifically a change to the playbackRate)
+  //     that should be applied asynchronously.
+  //
+  if (mHoldTime.IsNull() && !abortedPause && !mPendingPlaybackRate) {
     return;
   }
 
   // Clear the start time until we resolve a new one. We do this except
   // for the case where we are aborting a pause and don't have a hold time.
   //
   // If we're aborting a pause and *do* have a hold time (e.g. because
   // the animation is finished or we just applied the auto-rewind behavior
@@ -1159,56 +1266,79 @@ Animation::PauseNoUpdate(ErrorResult& aR
   }
 
   UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
   if (IsRelevant()) {
     nsNodeUtils::AnimationChanged(this);
   }
 }
 
+// https://drafts.csswg.org/web-animations/#play-an-animation
 void
 Animation::ResumeAt(const TimeDuration& aReadyTime)
 {
   // This method is only expected to be called for an animation that is
   // waiting to play. We can easily adapt it to handle other states
   // but it's currently not necessary.
   MOZ_ASSERT(mPendingState == PendingState::PlayPending,
              "Expected to resume a play-pending animation");
   MOZ_ASSERT(!mHoldTime.IsNull() || !mStartTime.IsNull(),
              "An animation in the play-pending state should have either a"
              " resolved hold time or resolved start time");
 
-  // If we aborted a pending pause operation we will already have a start time
-  // we should use. In all other cases, we resolve it from the ready time.
-  if (mStartTime.IsNull()) {
+  AutoMutationBatchForAnimation mb(*this);
+  bool hadPendingPlaybackRate = mPendingPlaybackRate.isSome();
+
+  if (!mHoldTime.IsNull()) {
+    // The hold time is set, so we don't need any special handling to preserve
+    // the current time.
+    ApplyPendingPlaybackRate();
     mStartTime =
       StartTimeFromTimelineTime(aReadyTime, mHoldTime.Value(), mPlaybackRate);
     if (mPlaybackRate != 0) {
       mHoldTime.SetNull();
     }
+  } else if (!mStartTime.IsNull() && mPendingPlaybackRate) {
+    // Apply any pending playback rate, preserving the current time.
+    TimeDuration currentTimeToMatch = CurrentTimeFromTimelineTime(
+      aReadyTime, mStartTime.Value(), mPlaybackRate);
+    ApplyPendingPlaybackRate();
+    mStartTime =
+      StartTimeFromTimelineTime(aReadyTime, currentTimeToMatch, mPlaybackRate);
+    if (mPlaybackRate == 0) {
+      mHoldTime.SetValue(currentTimeToMatch);
+    }
   }
+
   mPendingState = PendingState::NotPending;
 
   UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
 
+  // If we had a pending playback rate, we will have now applied it so we need
+  // to notify observers.
+  if (hadPendingPlaybackRate && IsRelevant()) {
+    nsNodeUtils::AnimationChanged(this);
+  }
+
   if (mReady) {
     mReady->MaybeResolve(this);
   }
 }
 
 void
 Animation::PauseAt(const TimeDuration& aReadyTime)
 {
   MOZ_ASSERT(mPendingState == PendingState::PausePending,
              "Expected to pause a pause-pending animation");
 
   if (!mStartTime.IsNull() && mHoldTime.IsNull()) {
     mHoldTime = CurrentTimeFromTimelineTime(
       aReadyTime, mStartTime.Value(), mPlaybackRate);
   }
+  ApplyPendingPlaybackRate();
   mStartTime.SetNull();
   mPendingState = PendingState::NotPending;
 
   UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
 
   if (mReady) {
     mReady->MaybeResolve(this);
   }
@@ -1346,16 +1476,18 @@ Animation::CancelPendingTasks()
 void
 Animation::ResetPendingTasks()
 {
   if (mPendingState == PendingState::NotPending) {
     return;
   }
 
   CancelPendingTasks();
+  ApplyPendingPlaybackRate();
+
   if (mReady) {
     mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     mReady = nullptr;
   }
 }
 
 bool
 Animation::IsPossiblyOrphanedPendingAnimation() const
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -115,17 +115,17 @@ public:
   bool Pending() const { return mPendingState != PendingState::NotPending; }
   virtual Promise* GetReady(ErrorResult& aRv);
   Promise* GetFinished(ErrorResult& aRv);
   void Cancel();
   void Finish(ErrorResult& aRv);
   virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior);
   virtual void Pause(ErrorResult& aRv);
   void Reverse(ErrorResult& aRv);
-  void UpdatePlaybackRate(double /*aPlaybackRate*/) {}
+  void UpdatePlaybackRate(double aPlaybackRate);
   bool IsRunningOnCompositor() const;
   IMPL_EVENT_HANDLER(finish);
   IMPL_EVENT_HANDLER(cancel);
 
   // Wrapper functions for Animation DOM methods when called
   // from script.
   //
   // We often use the same methods internally and from script but when called
@@ -239,16 +239,25 @@ public:
    * animations on the next tick and apply the start time stored here.
    *
    * This method returns the start time, if resolved. Otherwise, if we have
    * a pending ready time, it returns the corresponding start time. If neither
    * of those are available, it returns null.
    */
   Nullable<TimeDuration> GetCurrentOrPendingStartTime() const;
 
+  /**
+   * As with the start time, we should use the pending playback rate when
+   * producing layer animations.
+   */
+  double CurrentOrPendingPlaybackRate() const
+  {
+    return mPendingPlaybackRate.valueOr(mPlaybackRate);
+  }
+  bool HasPendingPlaybackRate() const { return mPendingPlaybackRate.isSome(); }
 
   /**
    * The following relationship from the definition of the 'current time' is
    * re-used in many algorithms so we extract it here into a static method that
    * can be re-used:
    *
    *   current time = (timeline time - start time) * playback rate
    *
@@ -388,32 +397,38 @@ public:
    * We need to do this synchronously because after a CSS animation/transition
    * is canceled, it will be released by its owning element and may not still
    * exist when we would normally go to queue events on the next tick.
    */
   virtual void MaybeQueueCancelEvent(const StickyTimeDuration& aActiveTime) {};
 
 protected:
   void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
-  void SilentlySetPlaybackRate(double aPlaybackRate);
   void CancelNoUpdate();
   void PlayNoUpdate(ErrorResult& aRv, LimitBehavior aLimitBehavior);
   void PauseNoUpdate(ErrorResult& aRv);
   void ResumeAt(const TimeDuration& aReadyTime);
   void PauseAt(const TimeDuration& aReadyTime);
   void FinishPendingAt(const TimeDuration& aReadyTime)
   {
     if (mPendingState == PendingState::PlayPending) {
       ResumeAt(aReadyTime);
     } else if (mPendingState == PendingState::PausePending) {
       PauseAt(aReadyTime);
     } else {
       NS_NOTREACHED("Can't finish pending if we're not in a pending state");
     }
   }
+  void ApplyPendingPlaybackRate()
+  {
+    if (mPendingPlaybackRate) {
+      mPlaybackRate = *mPendingPlaybackRate;
+      mPendingPlaybackRate.reset();
+    }
+  }
 
   /**
    * Finishing behavior depends on if changes to timing occurred due
    * to a seek or regular playback.
    */
   enum class SeekFlag {
     NoSeek,
     DidSeek
@@ -452,37 +467,47 @@ protected:
 
   /**
    * Returns true if this animation is not only play-pending, but has
    * yet to be given a pending ready time. This roughly corresponds to
    * animations that are waiting to be painted (since we set the pending
    * ready time at the end of painting). Identifying such animations is
    * useful because in some cases animations that are painted together
    * may need to be synchronized.
+   *
+   * We don't, however, want to include animations with a fixed start time such
+   * as animations that are simply having their playbackRate updated or which
+   * are resuming from an aborted pause.
    */
   bool IsNewlyStarted() const {
     return mPendingState == PendingState::PlayPending &&
-           mPendingReadyTime.IsNull();
+           mPendingReadyTime.IsNull() &&
+           mStartTime.IsNull();
   }
   bool IsPossiblyOrphanedPendingAnimation() const;
   StickyTimeDuration EffectEnd() const;
 
   Nullable<TimeDuration> GetCurrentTimeForHoldTime(
     const Nullable<TimeDuration>& aHoldTime) const;
+  Nullable<TimeDuration> GetUnconstrainedCurrentTime() const
+  {
+    return GetCurrentTimeForHoldTime(Nullable<TimeDuration>());
+  }
 
   nsIDocument* GetRenderedDocument() const;
 
   RefPtr<AnimationTimeline> mTimeline;
   RefPtr<AnimationEffectReadOnly> mEffect;
   // The beginning of the delay period.
   Nullable<TimeDuration> mStartTime; // Timeline timescale
   Nullable<TimeDuration> mHoldTime;  // Animation timescale
   Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
   Nullable<TimeDuration> mPreviousCurrentTime; // Animation timescale
   double mPlaybackRate;
+  Maybe<double> mPendingPlaybackRate;
 
   // A Promise that is replaced on each call to Play()
   // and fulfilled when Play() is successfully completed.
   // This object is lazily created by GetReady.
   // See http://drafts.csswg.org/web-animations/#current-ready-promise
   RefPtr<Promise> mReady;
 
   // A Promise that is resolved when we reach the end of the effect, or
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animations/finishing-an-animation.html.ini
+++ /dev/null
@@ -1,13 +0,0 @@
-[finishing-an-animation.html]
-  [A pending playback rate should be applied immediately when an animation is finished]
-    expected: FAIL
-
-  [An exception should be thrown if the effective playback rate is zero]
-    expected: FAIL
-
-  [An exception should be thrown when finishing if the effective playback rate is positive and the target effect end is infinity]
-    expected: FAIL
-
-  [An exception is NOT thrown when finishing if the effective playback rate is negative and the target effect end is infinity]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animations/playing-an-animation.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[playing-an-animation.html]
-  [A pending playback rate is used when determining auto-rewind behavior]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animations/reversing-an-animation.html.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[reversing-an-animation.html]
-  [Reversing an animation inverts the playback rate]
-    expected: FAIL
-
-  [Reversing should use the negative pending playback rate]
-    expected: FAIL
-
-  [When reversing fails, it should restore any previous pending playback rate]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animations/seamlessly-updating-the-playback-rate-of-an-animation.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[seamlessly-updating-the-playback-rate-of-an-animation.html]
-  [Updating the playback rate while running makes the animation pending]
-    expected: FAIL
-
-  [If a pending playback rate is set multiple times, the latest wins]
-    expected: FAIL
-
-  [In the idle state, the playback rate is applied immediately]
-    expected: FAIL
-
-  [In the paused state, the playback rate is applied immediately]
-    expected: FAIL
-
-  [Updating the playback rate on a finished animation maintains the current time]
-    expected: FAIL
-
-  [Updating the playback rate to zero on a finished animation maintains the current time]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animations/setting-the-current-time-of-an-animation.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[setting-the-current-time-of-an-animation.html]
-  [Setting the current time of a pausing animation applies a pending playback rate]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animations/setting-the-start-time-of-an-animation.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[setting-the-start-time-of-an-animation.html]
-  [Setting the start time of a play-pending animation applies a pending playback rate]
-    expected: FAIL
-
-  [Setting the start time of a playing animation applies a pending playback rate]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animations/setting-the-target-effect-of-an-animation.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[setting-the-target-effect-of-an-animation.html]
-  [Setting the target effect to null causes a pending playback rate to be applied]
-    expected: FAIL
-