Bug 1037321 - Implement playState on AnimationPlayer; r=birtles, r=bz
authorDavid Zbarsky <dzbarsky@gmail.com>
Mon, 20 Oct 2014 13:55:45 +0900
changeset 211084 3e5016be92f475efa67a98e67c6e42ccc52bec97
parent 211083 8e104207af9d99dab949103534239bd8b2d34d92
child 211085 f6866bdaa73dcf0c26604b8c00bb9f8bc8af84c4
push id50643
push userbbirtles@mozilla.com
push dateMon, 20 Oct 2014 04:56:41 +0000
treeherdermozilla-inbound@b3b581cda940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles, bz
bugs1037321
milestone36.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 1037321 - Implement playState on AnimationPlayer; r=birtles, r=bz
dom/animation/AnimationPlayer.cpp
dom/animation/AnimationPlayer.h
dom/animation/test/css-integration/test_animation-player-playstate.html
dom/animation/test/css-integration/test_element-get-animation-players.html
dom/animation/test/mochitest.ini
dom/webidl/AnimationPlayer.webidl
--- a/dom/animation/AnimationPlayer.cpp
+++ b/dom/animation/AnimationPlayer.cpp
@@ -29,16 +29,35 @@ AnimationPlayer::GetStartTime() const
 }
 
 Nullable<double>
 AnimationPlayer::GetCurrentTime() const
 {
   return AnimationUtils::TimeDurationToDouble(GetCurrentTimeDuration());
 }
 
+AnimationPlayState
+AnimationPlayer::PlayState() const
+{
+  Nullable<TimeDuration> currentTime = GetCurrentTimeDuration();
+  if (currentTime.IsNull()) {
+    return AnimationPlayState::Idle;
+  }
+
+  if (mIsPaused) {
+    return AnimationPlayState::Paused;
+  }
+
+  if (currentTime.Value() >= SourceContentEnd()) {
+    return AnimationPlayState::Finished;
+  }
+
+  return AnimationPlayState::Running;
+}
+
 void
 AnimationPlayer::Play(UpdateFlags aFlags)
 {
   // FIXME: When we implement finishing behavior (bug 1074630) we should
   // not return early if mIsPaused is false since we may still need to seek.
   // (However, 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.)
   if (!mIsPaused) {
@@ -76,16 +95,27 @@ AnimationPlayer::Pause(UpdateFlags aFlag
   mHoldTime = GetCurrentTimeDuration();
   mStartTime.SetNull();
 
   if (aFlags == eUpdateStyle) {
     MaybePostRestyle();
   }
 }
 
+AnimationPlayState
+AnimationPlayer::PlayStateFromJS() const
+{
+  // FIXME: Once we introduce CSSTransitionPlayer, this should move to an
+  // override of PlayStateFromJS in CSSAnimationPlayer and CSSTransitionPlayer
+  // and we should skip it in the general case.
+  FlushStyle();
+
+  return PlayState();
+}
+
 void
 AnimationPlayer::PlayFromJS()
 {
   // Flush style to ensure that any properties controlling animation state
   // (e.g. animation-play-state) are fully updated before we proceed.
   //
   // Note that this might trigger PlayFromStyle()/PauseFromStyle() on this
   // object.
@@ -169,16 +199,27 @@ AnimationPlayer::MaybePostRestyle() cons
 
   // FIXME: This is a bit heavy-handed but in bug 1073336 we hope to
   // introduce a better means for players to update style.
   nsLayoutUtils::PostRestyleEvent(mSource->GetTarget(),
                                   eRestyle_Self,
                                   nsChangeHint_AllReflowHints);
 }
 
+StickyTimeDuration
+AnimationPlayer::SourceContentEnd() const
+{
+  if (!mSource) {
+    return StickyTimeDuration(0);
+  }
+
+  return mSource->Timing().mDelay
+         + mSource->GetComputedTiming().mActiveDuration;
+}
+
 } // namespace dom
 
 void
 CSSAnimationPlayer::Play(UpdateFlags aUpdateFlags)
 {
   mPauseShouldStick = false;
   AnimationPlayer::Play(aUpdateFlags);
 }
--- a/dom/animation/AnimationPlayer.h
+++ b/dom/animation/AnimationPlayer.h
@@ -6,16 +6,17 @@
 #ifndef mozilla_dom_AnimationPlayer_h
 #define mozilla_dom_AnimationPlayer_h
 
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
 #include "mozilla/dom/Animation.h" // for Animation
+#include "mozilla/dom/AnimationPlayerBinding.h" // for AnimationPlayState
 #include "mozilla/dom/AnimationTimeline.h" // for AnimationTimeline
 #include "nsCSSProperty.h" // for nsCSSProperty
 
 // X11 has a #define for CurrentTime.
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
@@ -55,22 +56,24 @@ public:
     eUpdateStyle
   };
 
   // AnimationPlayer methods
   Animation* GetSource() const { return mSource; }
   AnimationTimeline* Timeline() const { return mTimeline; }
   Nullable<double> GetStartTime() const;
   Nullable<double> GetCurrentTime() const;
+  AnimationPlayState PlayState() const;
   virtual void Play(UpdateFlags aUpdateFlags);
   virtual void Pause(UpdateFlags aUpdateFlags);
   bool IsRunningOnCompositor() const { return mIsRunningOnCompositor; }
 
   // Wrapper functions for performing extra steps such as flushing
   // style when calling from JS.
+  AnimationPlayState PlayStateFromJS() const;
   void PlayFromJS();
   void PauseFromJS();
 
   void SetSource(Animation* aSource);
   void Tick();
 
   const nsString& Name() const {
     return mSource ? mSource->Name() : EmptyString();
@@ -96,16 +99,17 @@ public:
   bool mIsRunningOnCompositor;
 
   nsRefPtr<AnimationTimeline> mTimeline;
   nsRefPtr<Animation> mSource;
 
 protected:
   void FlushStyle() const;
   void MaybePostRestyle() const;
+  StickyTimeDuration SourceContentEnd() const;
 
   Nullable<TimeDuration> mHoldTime;  // Player timescale
   bool mIsPaused;
 };
 
 } // namespace dom
 
 class CSSAnimationPlayer MOZ_FINAL : public dom::AnimationPlayer
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-integration/test_animation-player-playstate.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<style>
+@keyframes anim { }
+</style>
+<script>
+'use strict';
+
+function addDiv() {
+  var div = document.createElement('div');
+  document.body.appendChild(div);
+  return div;
+}
+
+test(function() {
+  var div = addDiv();
+  var cs = window.getComputedStyle(div);
+  div.style.animation = 'anim 1000s';
+
+  var player = div.getAnimationPlayers()[0];
+  assert_equals(player.playState, 'running');
+}, 'Player returns correct playState when running');
+
+test(function() {
+  var div = addDiv();
+  var cs = window.getComputedStyle(div);
+  div.style.animation = 'anim 1000s paused';
+
+  var player = div.getAnimationPlayers()[0];
+  assert_equals(player.playState, 'paused');
+}, 'Player returns correct playState when paused');
+
+test(function() {
+  var div = addDiv();
+  var cs = window.getComputedStyle(div);
+  div.style.animation = 'anim 1000s';
+
+  var player = div.getAnimationPlayers()[0];
+  player.pause();
+  assert_equals(player.playState, 'paused');
+}, 'Player.playState updates when paused by script');
+
+test(function() {
+  var div = addDiv();
+  var cs = window.getComputedStyle(div);
+  div.style.animation = 'anim 1000s paused';
+
+  var player = div.getAnimationPlayers()[0];
+  div.style.animationPlayState = 'running';
+  // This test also checks that calling playState flushes style
+  assert_equals(player.playState, 'running');
+}, 'Player.playState updates when resumed by setting style');
+
+</script>
--- a/dom/animation/test/css-integration/test_element-get-animation-players.html
+++ b/dom/animation/test/css-integration/test_element-get-animation-players.html
@@ -318,18 +318,18 @@ test(function() {
 test(function() {
   var div = addDiv();
   div.style.animation = 'anim1 100s';
   var originalPlayer = div.getAnimationPlayers()[0];
 
   // Update pause state (an AnimationPlayer change)
   div.style.animationPlayState = 'paused';
   var pausedPlayer = div.getAnimationPlayers()[0];
-  // FIXME: Check pausedPlayer.playState has changed once the API is available
-  // (bug 1037321)
+  assert_equals(pausedPlayer.playState, 'paused',
+                'player\'s paused state is updated');
   assert_equals(originalPlayer, pausedPlayer,
                 'getAnimationPlayers returns the same objects even when their'
                 + ' play state changes');
 
   // Update duration (an Animation change)
   div.style.animationDuration = '200s';
   var extendedPlayer = div.getAnimationPlayers()[0];
   // FIXME: Check extendedPlayer.source.timing.duration has changed once the
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -1,8 +1,9 @@
 [animation-timeline/test_animation-timeline.html]
 skip-if = buildapp == 'mulet'
 [css-integration/test_element-get-animation-players.html]
 skip-if = buildapp == 'mulet'
 [css-integration/test_animations-dynamic-changes.html]
 [css-integration/test_animation-effect-name.html]
 [css-integration/test_animation-pausing.html]
+[css-integration/test_animation-player-playstate.html]
 [css-integration/test_animation-target.html]
--- a/dom/webidl/AnimationPlayer.webidl
+++ b/dom/webidl/AnimationPlayer.webidl
@@ -5,30 +5,34 @@
  *
  * The origin of this IDL file is
  * http://dev.w3.org/fxtf/web-animations/#idl-def-AnimationPlayer
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" };
+
 [Pref="dom.animations-api.core.enabled"]
 interface AnimationPlayer {
   // Bug 1049975
   //           attribute AnimationNode?     source;
   [Pure]
   readonly attribute Animation? source;
   readonly attribute AnimationTimeline timeline;
   [Pure]
   readonly attribute double? startTime;
   readonly attribute double? currentTime;
 
   /* Not yet implemented
-           attribute double             playbackRate;
+           attribute double             playbackRate; */
+  [BinaryName="playStateFromJS"]
   readonly attribute AnimationPlayState playState;
+  /*
   readonly attribute Promise            ready;
   readonly attribute Promise            finished;
   void cancel ();
   void finish ();
   */
   [BinaryName="playFromJS"]
   void play ();
   [BinaryName="pauseFromJS"]