Bug 1070745 part 8 - Use play/pause from nsAnimationManager; r=dholbert
This patch uses the PlayFromStyle/PauseFromStyle methods on CSSAnimationPlayer
to perform play/pause control. (This allows us to encapsulate mHoldTime and
mPaused. We will encapsulate mStartTime etc. in subsequent bugs.
The override behavior of play()/pause() with regard to animation-play-state is:
* pause()/play() override the current animation-play-state
* pause() causes the player to remain paused until play() is called regardless
of changes to animation-play-state
(* Calling play() will override the animation-play-state but won't "stick". i.e.
subsequently setting animation-play-state: paused will pause the animation.)
These different permutations are tested in the next patch in this series.
This interaction will probably become more complicated once we introduce
finishing behavior (since we might not want animations to restart when
setting animation-play-state: running).
--- a/dom/animation/AnimationPlayer.h
+++ b/dom/animation/AnimationPlayer.h
@@ -29,19 +29,19 @@ namespace dom {
class AnimationPlayer : public nsWrapperCache
{
protected:
virtual ~AnimationPlayer() { }
public:
explicit AnimationPlayer(AnimationTimeline* aTimeline)
- : mIsPaused(false)
- , mIsRunningOnCompositor(false)
+ : mIsRunningOnCompositor(false)
, mTimeline(aTimeline)
+ , mIsPaused(false)
{
}
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationPlayer)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationPlayer)
AnimationTimeline* GetParentObject() const { return mTimeline; }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
@@ -88,26 +88,27 @@ public:
}
// Return the duration since the start time of the player, taking into
// account the pause state. May be negative or null.
Nullable<TimeDuration> GetCurrentTimeDuration() const;
// The beginning of the delay period.
Nullable<TimeDuration> mStartTime; // Timeline timescale
- Nullable<TimeDuration> mHoldTime; // Player timescale
- bool mIsPaused;
bool mIsRunningOnCompositor;
nsRefPtr<AnimationTimeline> mTimeline;
nsRefPtr<Animation> mSource;
protected:
void FlushStyle() const;
void MaybePostRestyle() const;
+
+ Nullable<TimeDuration> mHoldTime; // Player timescale
+ bool mIsPaused;
};
} // namespace dom
class CSSAnimationPlayer MOZ_FINAL : public dom::AnimationPlayer
{
public:
explicit CSSAnimationPlayer(dom::AnimationTimeline* aTimeline)
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -21,16 +21,17 @@
#include "nsIFrame.h"
#include "nsIDocument.h"
#include <math.h>
using namespace mozilla;
using namespace mozilla::css;
using mozilla::dom::Animation;
using mozilla::dom::AnimationPlayer;
+using mozilla::CSSAnimationPlayer;
void
nsAnimationManager::UpdateStyleAndEvents(AnimationPlayerCollection*
aCollection,
TimeStamp aRefreshTime,
EnsureStyleRuleFlags aFlags)
{
aCollection->EnsureStyleRuleFor(aRefreshTime, aFlags);
@@ -270,31 +271,32 @@ nsAnimationManager::CheckAnimationRule(n
// spec says to do, but WebKit seems to honor at least some of
// them. See
// http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html
// In order to honor what the spec said, we'd copy more data over
// (or potentially optimize BuildAnimations to avoid rebuilding it
// in the first place).
if (!collection->mPlayers.IsEmpty()) {
- Nullable<TimeDuration> now = timeline->GetCurrentTimeDuration();
-
for (size_t newIdx = newPlayers.Length(); newIdx-- != 0;) {
AnimationPlayer* newPlayer = newPlayers[newIdx];
// Find the matching animation with this name in the old list
// of animations. We iterate through both lists in a backwards
// direction which means that if there are more animations in
// the new list of animations with a given name than in the old
// list, it will be the animations towards the of the beginning of
// the list that do not match and are treated as new animations.
- nsRefPtr<AnimationPlayer> oldPlayer;
+ nsRefPtr<CSSAnimationPlayer> oldPlayer;
size_t oldIdx = collection->mPlayers.Length();
while (oldIdx-- != 0) {
- AnimationPlayer* a = collection->mPlayers[oldIdx];
+ CSSAnimationPlayer* a =
+ collection->mPlayers[oldIdx]->AsCSSAnimationPlayer();
+ MOZ_ASSERT(a, "All players in the CSS Animation collection should"
+ " be CSSAnimationPlayer objects");
if (a->Name() == newPlayer->Name()) {
oldPlayer = a;
break;
}
}
if (!oldPlayer) {
continue;
}
@@ -307,29 +309,28 @@ nsAnimationManager::CheckAnimationRule(n
oldAnim->Timing() = newAnim->Timing();
oldAnim->Properties() = newAnim->Properties();
}
// Reset compositor state so animation will be re-synchronized.
oldPlayer->mIsRunningOnCompositor = false;
// Handle changes in play state.
- if (!oldPlayer->IsPaused() && newPlayer->IsPaused()) {
- // Start pause at current time.
- oldPlayer->mHoldTime = oldPlayer->GetCurrentTimeDuration();
- } else if (oldPlayer->IsPaused() && !newPlayer->IsPaused()) {
- if (now.IsNull()) {
- oldPlayer->mStartTime.SetNull();
- } else {
- oldPlayer->mStartTime.SetValue(now.Value() -
- oldPlayer->mHoldTime.Value());
- }
- oldPlayer->mHoldTime.SetNull();
+ // CSSAnimationPlayer takes care of override behavior so that,
+ // for example, if the author has called pause(), that will
+ // override the animation-play-state.
+ // (We should check newPlayer->IsStylePaused() but that requires
+ // downcasting to CSSAnimationPlayer and we happen to know that
+ // newPlayer will only ever be paused by calling PauseFromStyle
+ // making IsPaused synonymous in this case.)
+ if (!oldPlayer->IsStylePaused() && newPlayer->IsPaused()) {
+ oldPlayer->PauseFromStyle();
+ } else if (oldPlayer->IsStylePaused() && !newPlayer->IsPaused()) {
+ oldPlayer->PlayFromStyle();
}
- oldPlayer->mIsPaused = newPlayer->mIsPaused;
// Replace new animation with the (updated) old one and remove the
// old one from the array so we don't try to match it any more.
//
// Although we're doing this while iterating this is safe because
// we're not changing the length of newPlayers and we've finished
// iterating over the list of old iterations.
newPlayer = nullptr;
@@ -437,37 +438,35 @@ nsAnimationManager::BuildAnimations(nsSt
src.GetName().IsEmpty()
? nullptr
: mPresContext->StyleSet()->KeyframesRuleForName(mPresContext,
src.GetName());
if (!rule) {
continue;
}
- nsRefPtr<AnimationPlayer> dest =
- *aPlayers.AppendElement(new AnimationPlayer(aTimeline));
+ nsRefPtr<CSSAnimationPlayer> dest = new CSSAnimationPlayer(aTimeline);
+ aPlayers.AppendElement(dest);
AnimationTiming timing;
timing.mIterationDuration =
TimeDuration::FromMilliseconds(src.GetDuration());
timing.mDelay = TimeDuration::FromMilliseconds(src.GetDelay());
timing.mIterationCount = src.GetIterationCount();
timing.mDirection = src.GetDirection();
timing.mFillMode = src.GetFillMode();
nsRefPtr<Animation> destAnim =
new Animation(mPresContext->Document(), aTarget,
aStyleContext->GetPseudoType(), timing, src.GetName());
dest->SetSource(destAnim);
dest->mStartTime = now;
- dest->mIsPaused =
- src.GetPlayState() == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED;
- if (dest->IsPaused()) {
- dest->mHoldTime.SetValue(TimeDuration(0));
+ if (src.GetPlayState() == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED) {
+ dest->PauseFromStyle();
}
// While current drafts of css3-animations say that later keyframes
// with the same key entirely replace earlier ones (no cascading),
// this is a bad idea and contradictory to the rest of CSS. So
// we're going to keep all the keyframes for each key and then do
// the replacement on a per-property basis rather than a per-rule
// basis, just like everything else in CSS.