mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1074630, part 1 - Implement Web Animations finishing behavior. r=birtles, r=smaug
This commit is contained in:
parent
481edeac42
commit
78696876a2
@ -17,7 +17,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline,
|
||||
mSource, mReady)
|
||||
mSource, mReady, mFinished)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationPlayer)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationPlayer)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationPlayer)
|
||||
@ -68,12 +68,8 @@ AnimationPlayer::SetStartTime(const Nullable<TimeDuration>& aNewStartTime)
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
|
||||
UpdateSourceContent();
|
||||
UpdateTiming();
|
||||
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
|
||||
}
|
||||
|
||||
Nullable<TimeDuration>
|
||||
@ -113,8 +109,7 @@ AnimationPlayer::SilentlySetCurrentTime(const TimeDuration& aSeekTime)
|
||||
(aSeekTime / mPlaybackRate));
|
||||
}
|
||||
|
||||
// Once AnimationPlayers store a previous current time, set that to
|
||||
// unresolved.
|
||||
mPreviousCurrentTime.SetNull();
|
||||
}
|
||||
|
||||
// Implements http://w3c.github.io/web-animations/#set-the-current-time
|
||||
@ -130,12 +125,9 @@ AnimationPlayer::SetCurrentTime(const TimeDuration& aSeekTime)
|
||||
}
|
||||
}
|
||||
|
||||
UpdateFinishedState(true);
|
||||
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
|
||||
@ -186,36 +178,56 @@ AnimationPlayer::PlayState() const
|
||||
return AnimationPlayState::Running;
|
||||
}
|
||||
|
||||
static inline already_AddRefed<Promise>
|
||||
CreatePromise(AnimationTimeline* aTimeline, ErrorResult& aRv)
|
||||
{
|
||||
nsIGlobalObject* global = aTimeline->GetParentObject();
|
||||
if (global) {
|
||||
return Promise::Create(global, aRv);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Promise*
|
||||
AnimationPlayer::GetReady(ErrorResult& aRv)
|
||||
{
|
||||
// Lazily create the ready promise if it doesn't exist
|
||||
if (!mReady) {
|
||||
nsIGlobalObject* global = mTimeline->GetParentObject();
|
||||
if (global) {
|
||||
mReady = Promise::Create(global, aRv);
|
||||
if (mReady && PlayState() != AnimationPlayState::Pending) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
mReady = CreatePromise(mTimeline, aRv); // Lazily create on demand
|
||||
}
|
||||
if (!mReady) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
} else if (PlayState() != AnimationPlayState::Pending) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
|
||||
return mReady;
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::Play()
|
||||
Promise*
|
||||
AnimationPlayer::GetFinished(ErrorResult& aRv)
|
||||
{
|
||||
DoPlay();
|
||||
if (!mFinished) {
|
||||
mFinished = CreatePromise(mTimeline, aRv); // Lazily create on demand
|
||||
}
|
||||
if (!mFinished) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
} else if (IsFinished()) {
|
||||
mFinished->MaybeResolve(this);
|
||||
}
|
||||
return mFinished;
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::Play(LimitBehavior aLimitBehavior)
|
||||
{
|
||||
DoPlay(aLimitBehavior);
|
||||
PostUpdate();
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::Pause()
|
||||
{
|
||||
// TODO: The DoPause() call should not be synchronous (bug 1109390). See
|
||||
// http://w3c.github.io/web-animations/#pausing-an-animation-section
|
||||
DoPause();
|
||||
PostUpdate();
|
||||
}
|
||||
@ -285,7 +297,7 @@ AnimationPlayer::Tick()
|
||||
ResumeAt(mTimeline->GetCurrentTime().Value());
|
||||
}
|
||||
|
||||
UpdateSourceContent();
|
||||
UpdateTiming();
|
||||
}
|
||||
|
||||
void
|
||||
@ -345,6 +357,12 @@ AnimationPlayer::Cancel()
|
||||
}
|
||||
}
|
||||
|
||||
if (mFinished) {
|
||||
mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
// Clear finished promise. We'll create a new one lazily.
|
||||
mFinished = nullptr;
|
||||
|
||||
mHoldTime.SetNull();
|
||||
mStartTime.SetNull();
|
||||
|
||||
@ -407,16 +425,12 @@ AnimationPlayer::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
|
||||
|
||||
mSource->ComposeStyle(aStyleRule, aSetProperties);
|
||||
|
||||
mIsPreviousStateFinished = (playState == AnimationPlayState::Finished);
|
||||
//XXXjwatt mIsPreviousStateFinished = (playState == AnimationPlayState::Finished);
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::DoPlay()
|
||||
AnimationPlayer::DoPlay(LimitBehavior aLimitBehavior)
|
||||
{
|
||||
// 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.
|
||||
|
||||
bool reuseReadyPromise = false;
|
||||
if (mPendingState != PendingState::NotPending) {
|
||||
CancelPendingTasks();
|
||||
@ -425,10 +439,16 @@ AnimationPlayer::DoPlay()
|
||||
|
||||
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
||||
if (mPlaybackRate > 0.0 &&
|
||||
(currentTime.IsNull())) {
|
||||
(currentTime.IsNull() ||
|
||||
(aLimitBehavior == LimitBehavior::AutoRewind &&
|
||||
(currentTime.Value().ToMilliseconds() < 0.0 ||
|
||||
currentTime.Value() >= SourceContentEnd())))) {
|
||||
mHoldTime.SetValue(TimeDuration(0));
|
||||
} else if (mPlaybackRate < 0.0 &&
|
||||
(currentTime.IsNull())) {
|
||||
(currentTime.IsNull() ||
|
||||
(aLimitBehavior == LimitBehavior::AutoRewind &&
|
||||
(currentTime.Value().ToMilliseconds() <= 0.0 ||
|
||||
currentTime.Value() > SourceContentEnd())))) {
|
||||
mHoldTime.SetValue(TimeDuration(SourceContentEnd()));
|
||||
} else if (mPlaybackRate == 0.0 && currentTime.IsNull()) {
|
||||
mHoldTime.SetValue(TimeDuration(0));
|
||||
@ -457,9 +477,8 @@ AnimationPlayer::DoPlay()
|
||||
PendingPlayerTracker* tracker = doc->GetOrCreatePendingPlayerTracker();
|
||||
tracker->AddPlayPending(*this);
|
||||
|
||||
// We may have updated the current time when we set the hold time above
|
||||
// so notify source content.
|
||||
UpdateSourceContent();
|
||||
// We may have updated the current time when we set the hold time above.
|
||||
UpdateTiming();
|
||||
}
|
||||
|
||||
void
|
||||
@ -484,6 +503,8 @@ AnimationPlayer::DoPause()
|
||||
// Bug 1109390 - check for null result here and go to pending state
|
||||
mHoldTime = GetCurrentTime();
|
||||
mStartTime.SetNull();
|
||||
|
||||
UpdateFinishedState();
|
||||
}
|
||||
|
||||
void
|
||||
@ -506,13 +527,72 @@ AnimationPlayer::ResumeAt(const TimeDuration& aResumeTime)
|
||||
}
|
||||
mPendingState = PendingState::NotPending;
|
||||
|
||||
UpdateSourceContent();
|
||||
UpdateTiming();
|
||||
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::UpdateTiming()
|
||||
{
|
||||
// We call UpdateFinishedState before UpdateSourceContent because the former
|
||||
// can change the current time, which is used by the latter.
|
||||
UpdateFinishedState();
|
||||
UpdateSourceContent();
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::UpdateFinishedState(bool aSeekFlag)
|
||||
{
|
||||
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
||||
TimeDuration targetEffectEnd = TimeDuration(SourceContentEnd());
|
||||
|
||||
if (!mStartTime.IsNull() &&
|
||||
mPendingState == PendingState::NotPending) {
|
||||
if (mPlaybackRate > 0.0 &&
|
||||
!currentTime.IsNull() &&
|
||||
currentTime.Value() >= targetEffectEnd) {
|
||||
if (aSeekFlag) {
|
||||
mHoldTime = currentTime;
|
||||
} else if (!mPreviousCurrentTime.IsNull()) {
|
||||
mHoldTime.SetValue(std::max(mPreviousCurrentTime.Value(),
|
||||
targetEffectEnd));
|
||||
} else {
|
||||
mHoldTime.SetValue(targetEffectEnd);
|
||||
}
|
||||
} else if (mPlaybackRate < 0.0 &&
|
||||
!currentTime.IsNull() &&
|
||||
currentTime.Value().ToMilliseconds() <= 0.0) {
|
||||
if (aSeekFlag) {
|
||||
mHoldTime = currentTime;
|
||||
} else {
|
||||
mHoldTime.SetValue(0);
|
||||
}
|
||||
} else if (mPlaybackRate != 0.0 &&
|
||||
!currentTime.IsNull()) {
|
||||
if (aSeekFlag && !mHoldTime.IsNull()) {
|
||||
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
|
||||
(mHoldTime.Value() / mPlaybackRate));
|
||||
}
|
||||
mHoldTime.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
bool currentFinishedState = IsFinished();
|
||||
if (currentFinishedState && !mIsPreviousStateFinished) {
|
||||
if (mFinished) {
|
||||
mFinished->MaybeResolve(this);
|
||||
}
|
||||
} else if (!currentFinishedState && mIsPreviousStateFinished) {
|
||||
// Clear finished promise. We'll create a new one lazily.
|
||||
mFinished = nullptr;
|
||||
}
|
||||
mIsPreviousStateFinished = currentFinishedState;
|
||||
mPreviousCurrentTime = currentTime;
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::UpdateSourceContent()
|
||||
{
|
||||
@ -563,6 +643,19 @@ AnimationPlayer::CancelPendingTasks()
|
||||
mPendingReadyTime.SetNull();
|
||||
}
|
||||
|
||||
bool
|
||||
AnimationPlayer::IsFinished() const
|
||||
{
|
||||
// Unfortunately there's some weirdness in the spec at the moment where if
|
||||
// you're finished and paused, the playState is paused. This prevents us
|
||||
// from just checking |PlayState() == AnimationPlayState::Finished| here,
|
||||
// and we need this much more messy check to see if we're finished.
|
||||
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
||||
return !currentTime.IsNull() &&
|
||||
((mPlaybackRate > 0.0 && currentTime.Value() >= SourceContentEnd()) ||
|
||||
(mPlaybackRate < 0.0 && currentTime.Value().ToMilliseconds() <= 0.0));
|
||||
}
|
||||
|
||||
bool
|
||||
AnimationPlayer::IsPossiblyOrphanedPendingPlayer() const
|
||||
{
|
||||
|
@ -70,6 +70,13 @@ public:
|
||||
virtual CSSAnimationPlayer* AsCSSAnimationPlayer() { return nullptr; }
|
||||
virtual CSSTransitionPlayer* AsCSSTransitionPlayer() { return nullptr; }
|
||||
|
||||
// Flag to pass to DoPlay to indicate that it should not carry out finishing
|
||||
// behavior (reset the current time to the beginning of the active duration).
|
||||
enum LimitBehavior {
|
||||
AutoRewind = 0,
|
||||
Continue = 1
|
||||
};
|
||||
|
||||
// AnimationPlayer methods
|
||||
Animation* GetSource() const { return mSource; }
|
||||
AnimationTimeline* Timeline() const { return mTimeline; }
|
||||
@ -83,7 +90,8 @@ public:
|
||||
void SilentlySetPlaybackRate(double aPlaybackRate);
|
||||
AnimationPlayState PlayState() const;
|
||||
virtual Promise* GetReady(ErrorResult& aRv);
|
||||
virtual void Play();
|
||||
virtual Promise* GetFinished(ErrorResult& aRv);
|
||||
virtual void Play(LimitBehavior aLimitBehavior);
|
||||
virtual void Pause();
|
||||
bool IsRunningOnCompositor() const { return mIsRunningOnCompositor; }
|
||||
|
||||
@ -97,7 +105,7 @@ public:
|
||||
void SetCurrentTimeAsDouble(const Nullable<double>& aCurrentTime,
|
||||
ErrorResult& aRv);
|
||||
virtual AnimationPlayState PlayStateFromJS() const { return PlayState(); }
|
||||
virtual void PlayFromJS() { Play(); }
|
||||
virtual void PlayFromJS() { Play(LimitBehavior::AutoRewind); }
|
||||
// PauseFromJS is currently only here for symmetry with PlayFromJS but
|
||||
// in future we will likely have to flush style in
|
||||
// CSSAnimationPlayer::PauseFromJS so we leave it for now.
|
||||
@ -252,10 +260,12 @@ public:
|
||||
bool& aNeedsRefreshes);
|
||||
|
||||
protected:
|
||||
void DoPlay();
|
||||
void DoPlay(LimitBehavior aLimitBehavior);
|
||||
void DoPause();
|
||||
void ResumeAt(const TimeDuration& aResumeTime);
|
||||
|
||||
void UpdateTiming();
|
||||
void UpdateFinishedState(bool aSeekFlag = false);
|
||||
void UpdateSourceContent();
|
||||
void FlushStyle() const;
|
||||
void PostUpdate();
|
||||
@ -266,6 +276,8 @@ protected:
|
||||
*/
|
||||
void CancelPendingTasks();
|
||||
|
||||
bool IsFinished() const;
|
||||
|
||||
bool IsPossiblyOrphanedPendingPlayer() const;
|
||||
StickyTimeDuration SourceContentEnd() const;
|
||||
|
||||
@ -280,13 +292,22 @@ protected:
|
||||
Nullable<TimeDuration> mStartTime; // Timeline timescale
|
||||
Nullable<TimeDuration> mHoldTime; // Player timescale
|
||||
Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
|
||||
Nullable<TimeDuration> mPreviousCurrentTime; // Player 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.
|
||||
// See http://w3c.github.io/web-animations/#current-ready-promise
|
||||
nsRefPtr<Promise> mReady;
|
||||
|
||||
// A Promise that is resolved when we reach the end of the source content, or
|
||||
// 0 when playing backwards. The Promise is replaced if the animation is
|
||||
// finished but then a state change makes it not finished.
|
||||
// This object is lazily created by GetFinished.
|
||||
// See http://w3c.github.io/web-animations/#current-finished-promise
|
||||
nsRefPtr<Promise> mFinished;
|
||||
|
||||
// Indicates if the player is in the pending state (and what state it is
|
||||
// waiting to enter when it finished pending). We use this rather than
|
||||
// checking if this player is tracked by a PendingPlayerTracker because the
|
||||
@ -299,9 +320,6 @@ protected:
|
||||
bool mIsRunningOnCompositor;
|
||||
// Indicates whether we were in the finished state during our
|
||||
// most recent unthrottled sample (our last ComposeStyle call).
|
||||
// FIXME: When we implement the finished promise (bug 1074630) we can
|
||||
// probably remove this and check if the promise has been settled yet
|
||||
// or not instead.
|
||||
bool mIsPreviousStateFinished; // Spec calls this "previous finished state"
|
||||
// Indicates that the player should be exposed in an element's
|
||||
// getAnimationPlayers() list.
|
||||
|
@ -29,8 +29,9 @@ interface AnimationPlayer {
|
||||
readonly attribute AnimationPlayState playState;
|
||||
[Throws]
|
||||
readonly attribute Promise<AnimationPlayer> ready;
|
||||
/*
|
||||
[Throws]
|
||||
readonly attribute Promise<AnimationPlayer> finished;
|
||||
/*
|
||||
void cancel ();
|
||||
void finish ();
|
||||
*/
|
||||
|
@ -35,10 +35,10 @@ CSSAnimationPlayer::GetReady(ErrorResult& aRv)
|
||||
}
|
||||
|
||||
void
|
||||
CSSAnimationPlayer::Play()
|
||||
CSSAnimationPlayer::Play(LimitBehavior aLimitBehavior)
|
||||
{
|
||||
mPauseShouldStick = false;
|
||||
AnimationPlayer::Play();
|
||||
AnimationPlayer::Play(aLimitBehavior);
|
||||
}
|
||||
|
||||
void
|
||||
@ -71,7 +71,7 @@ CSSAnimationPlayer::PlayFromStyle()
|
||||
{
|
||||
mIsStylePaused = false;
|
||||
if (!mPauseShouldStick) {
|
||||
DoPlay();
|
||||
DoPlay(AnimationPlayer::LimitBehavior::Continue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ public:
|
||||
AsCSSAnimationPlayer() override { return this; }
|
||||
|
||||
virtual dom::Promise* GetReady(ErrorResult& aRv) override;
|
||||
virtual void Play() override;
|
||||
virtual void Play(LimitBehavior aLimitBehavior) override;
|
||||
virtual void Pause() override;
|
||||
|
||||
virtual dom::AnimationPlayState PlayStateFromJS() const override;
|
||||
|
@ -81,7 +81,7 @@ public:
|
||||
|
||||
// A variant of Play() that avoids posting style updates since this method
|
||||
// is expected to be called whilst already updating style.
|
||||
void PlayFromStyle() { DoPlay(); }
|
||||
void PlayFromStyle() { DoPlay(AnimationPlayer::LimitBehavior::Continue); }
|
||||
|
||||
protected:
|
||||
virtual ~CSSTransitionPlayer() { }
|
||||
|
Loading…
Reference in New Issue
Block a user