Bug 1127380, part 1 - Initial implementation of AnimationPlayer.playbackRate. r=birtles, r=smaug
authorJonathan Watt <jwatt@jwatt.org>
Fri, 13 Mar 2015 20:10:45 +0000
changeset 234730 78195e7115a296631c2a292b09590b53c1de6746
parent 234729 63b94f124c880efe217a83646338c6e1ee54b6ad
child 234731 96482314a46440872ad32bfeaa85fd9e1b7f4250
push id28454
push userphilringnalda@gmail.com
push dateSat, 21 Mar 2015 19:32:28 +0000
treeherdermozilla-central@f949be6cd23e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles, smaug
bugs1127380
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1127380, part 1 - Initial implementation of AnimationPlayer.playbackRate. r=birtles, r=smaug
dom/animation/AnimationPlayer.cpp
dom/animation/AnimationPlayer.h
dom/webidl/AnimationPlayer.webidl
--- a/dom/animation/AnimationPlayer.cpp
+++ b/dom/animation/AnimationPlayer.cpp
@@ -49,20 +49,19 @@ AnimationPlayer::SetStartTime(const Null
   }
   if (timelineTime.IsNull() && !aNewStartTime.IsNull()) {
     mHoldTime.SetNull();
   }
 #endif
   Nullable<TimeDuration> previousCurrentTime = GetCurrentTime();
   mStartTime = aNewStartTime;
   if (!aNewStartTime.IsNull()) {
-    // Until bug 1127380 (playbackRate) is implemented, the rate is essentially
-    // one. Once that bug is fixed we should only SetNull() if the rate is not
-    // zero.
-    mHoldTime.SetNull();
+    if (mPlaybackRate != 0.0) {
+      mHoldTime.SetNull();
+    }
   } else {
     mHoldTime = previousCurrentTime;
   }
 
   CancelPendingPlay();
   if (mReady) {
     // We may have already resolved mReady, but in that case calling
     // MaybeResolve is a no-op, so that's okay.
@@ -84,37 +83,39 @@ AnimationPlayer::GetCurrentTime() const
   if (!mHoldTime.IsNull()) {
     result = mHoldTime;
     return result;
   }
 
   if (!mStartTime.IsNull()) {
     Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
     if (!timelineTime.IsNull()) {
-      result.SetValue(timelineTime.Value() - mStartTime.Value());
+      result.SetValue((timelineTime.Value() - mStartTime.Value())
+                        .MultDouble(mPlaybackRate));
     }
   }
   return result;
 }
 
 // Implements http://w3c.github.io/web-animations/#silently-set-the-current-time
 void
 AnimationPlayer::SilentlySetCurrentTime(const TimeDuration& aSeekTime)
 {
   if (!mHoldTime.IsNull() ||
       !mTimeline ||
-      mTimeline->GetCurrentTime().IsNull()
-      /*or, once supported, playback rate is 0, or have pending pause task*/) {
+      mTimeline->GetCurrentTime().IsNull() ||
+      mPlaybackRate == 0.0
+      /*or, once supported, if we have a pending pause task*/) {
     mHoldTime.SetValue(aSeekTime);
     if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
       mStartTime.SetNull();
     }
   } else {
-    // once playback rate is supported, need to account for that here
-    mStartTime.SetValue(mTimeline->GetCurrentTime().Value() - aSeekTime);
+    mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
+                          (aSeekTime / mPlaybackRate));
   }
 
   // Once AnimationPlayers store a previous current time, set that to
   // unresolved.
 }
 
 // Implements http://w3c.github.io/web-animations/#set-the-current-time
 void
@@ -127,33 +128,58 @@ AnimationPlayer::SetCurrentTime(const Ti
   UpdateSourceContent();
   PostUpdate();
 
   // FIXME: Once bug 1074630 is fixed, run the procedure to update a player's
   // finished state for player:
   // http://w3c.github.io/web-animations/#update-a-players-finished-state
 }
 
+void
+AnimationPlayer::SetPlaybackRate(double aPlaybackRate)
+{
+  Nullable<TimeDuration> previousTime = GetCurrentTime();
+  mPlaybackRate = aPlaybackRate;
+  if (!previousTime.IsNull()) {
+    ErrorResult rv;
+    SetCurrentTime(previousTime.Value());
+    MOZ_ASSERT(!rv.Failed(), "Should not assert for non-null time");
+  }
+}
+
+void
+AnimationPlayer::SilentlySetPlaybackRate(double aPlaybackRate)
+{
+  Nullable<TimeDuration> previousTime = GetCurrentTime();
+  mPlaybackRate = aPlaybackRate;
+  if (!previousTime.IsNull()) {
+    ErrorResult rv;
+    SilentlySetCurrentTime(previousTime.Value());
+    MOZ_ASSERT(!rv.Failed(), "Should not assert for non-null time");
+  }
+}
+
 AnimationPlayState
 AnimationPlayer::PlayState() const
 {
   if (mIsPending) {
     return AnimationPlayState::Pending;
   }
 
   Nullable<TimeDuration> currentTime = GetCurrentTime();
   if (currentTime.IsNull()) {
     return AnimationPlayState::Idle;
   }
 
   if (mStartTime.IsNull()) {
     return AnimationPlayState::Paused;
   }
 
-  if (currentTime.Value() >= SourceContentEnd()) {
+  if ((mPlaybackRate > 0.0 && currentTime.Value() >= SourceContentEnd()) ||
+      (mPlaybackRate < 0.0 && currentTime.Value().ToMilliseconds() <= 0.0)) {
     return AnimationPlayState::Finished;
   }
 
   return AnimationPlayState::Running;
 }
 
 Promise*
 AnimationPlayer::GetReady(ErrorResult& aRv)
@@ -394,20 +420,27 @@ AnimationPlayer::ComposeStyle(nsRefPtr<c
 void
 AnimationPlayer::DoPlay()
 {
   // FIXME: When we implement finishing behavior (bug 1074630) we will
   // need to pass a flag so that when we start playing due to a change in
   // animation-play-state we *don't* trigger finishing behavior.
 
   Nullable<TimeDuration> currentTime = GetCurrentTime();
-  if (currentTime.IsNull()) {
+  if (mPlaybackRate > 0.0 &&
+      (currentTime.IsNull())) {
     mHoldTime.SetValue(TimeDuration(0));
-  } else if (mHoldTime.IsNull()) {
-    // If the hold time is null, we are already playing normally
+  } else if (mPlaybackRate < 0.0 &&
+             (currentTime.IsNull())) {
+    mHoldTime.SetValue(TimeDuration(SourceContentEnd()));
+  } else if (mPlaybackRate == 0.0 && currentTime.IsNull()) {
+    mHoldTime.SetValue(TimeDuration(0));
+  }
+
+  if (mHoldTime.IsNull()) {
     return;
   }
 
   // Clear ready promise. We'll create a new one lazily.
   mReady = nullptr;
 
   mIsPending = true;
 
@@ -455,18 +488,22 @@ AnimationPlayer::ResumeAt(const TimeDura
   // This method is only expected to be called for a player that is
   // waiting to play. We can easily adapt it to handle other states
   // but it's currently not necessary.
   MOZ_ASSERT(PlayState() == AnimationPlayState::Pending,
              "Expected to resume a pending player");
   MOZ_ASSERT(!mHoldTime.IsNull(),
              "A player in the pending state should have a resolved hold time");
 
-  mStartTime.SetValue(aResumeTime - mHoldTime.Value());
-  mHoldTime.SetNull();
+  if (mPlaybackRate != 0) {
+    mStartTime.SetValue(aResumeTime - (mHoldTime.Value() / mPlaybackRate));
+    mHoldTime.SetNull();
+  } else {
+    mStartTime.SetValue(aResumeTime);
+  }
   mIsPending = false;
 
   UpdateSourceContent();
 
   if (mReady) {
     mReady->MaybeResolve(this);
   }
 }
--- a/dom/animation/AnimationPlayer.h
+++ b/dom/animation/AnimationPlayer.h
@@ -48,16 +48,17 @@ class AnimationPlayer : public nsISuppor
                         public nsWrapperCache
 {
 protected:
   virtual ~AnimationPlayer() {}
 
 public:
   explicit AnimationPlayer(AnimationTimeline* aTimeline)
     : mTimeline(aTimeline)
+    , mPlaybackRate(1.0)
     , mIsPending(false)
     , mIsRunningOnCompositor(false)
     , mIsPreviousStateFinished(false)
     , mIsRelevant(false)
   {
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -72,16 +73,19 @@ public:
   // AnimationPlayer methods
   Animation* GetSource() const { return mSource; }
   AnimationTimeline* Timeline() const { return mTimeline; }
   Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
   void SetStartTime(const Nullable<TimeDuration>& aNewStartTime);
   Nullable<TimeDuration> GetCurrentTime() const;
   void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
   void SetCurrentTime(const TimeDuration& aNewCurrentTime);
+  double PlaybackRate() const { return mPlaybackRate; }
+  void SetPlaybackRate(double aPlaybackRate);
+  void SilentlySetPlaybackRate(double aPlaybackRate);
   AnimationPlayState PlayState() const;
   virtual Promise* GetReady(ErrorResult& aRv);
   virtual void Play();
   virtual void Pause();
   bool IsRunningOnCompositor() const { return mIsRunningOnCompositor; }
 
   // Wrapper functions for AnimationPlayer DOM methods when called
   // from script. We often use the same methods internally and from
@@ -236,16 +240,17 @@ protected:
   AnimationPlayerCollection* GetCollection() const;
 
   nsRefPtr<AnimationTimeline> mTimeline;
   nsRefPtr<Animation> mSource;
   // The beginning of the delay period.
   Nullable<TimeDuration> mStartTime; // Timeline timescale
   Nullable<TimeDuration> mHoldTime;  // Player timescale
   Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
+  double mPlaybackRate;
 
   // A Promise that is replaced on each call to Play() (and in future Pause())
   // and fulfilled when Play() is successfully completed.
   // This object is lazily created by GetReady.
   nsRefPtr<Promise> mReady;
 
   // Indicates if the player is in the pending state. We use this rather
   // than checking if this player is tracked by a PendingPlayerTracker.
--- a/dom/webidl/AnimationPlayer.webidl
+++ b/dom/webidl/AnimationPlayer.webidl
@@ -19,18 +19,17 @@ interface AnimationPlayer {
   [Pure]
   readonly attribute Animation? source;
   readonly attribute AnimationTimeline timeline;
   [BinaryName="startTimeAsDouble"]
   attribute double? startTime;
   [SetterThrows, BinaryName="currentTimeAsDouble"]
   attribute double? currentTime;
 
-  /* Not yet implemented
-           attribute double             playbackRate; */
+           attribute double             playbackRate;
   [BinaryName="playStateFromJS"]
   readonly attribute AnimationPlayState playState;
   [Throws]
   readonly attribute Promise<AnimationPlayer> ready;
   /*
   readonly attribute Promise<AnimationPlayer> finished;
   void cancel ();
   void finish ();