From ac47cd0a0271fa68b6c95ae2924ea288c143c2b1 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 9 Jan 2015 07:57:58 +0900 Subject: [PATCH] Bug 1112480 part 6 - Make PendingPlayerTracker call StartOnNextTick; r=jwatt This patch switches on the new, "actually start the player in the next refresh driver tick" behavior. It updates PendingPlayerTracker, adding a StartPendingPlayersOnNextTick method which calls the appropriate method on AnimationPlayer. The existing StartPendingPlayers is renamed to StartPendingPlayersNow and is used for testing only. Furthermore, since we now expect AnimationPlayer::StartOnNextTick to be functional, AnimationPlayer::DoPlay is updated to use it when there is no document available. This should make playing an animation player always asynchronous, that is, always transition to the pending state temporarily (unless we are already playing). --- dom/animation/AnimationPlayer.cpp | 9 +----- dom/animation/PendingPlayerTracker.cpp | 44 ++++++++++++++++---------- dom/animation/PendingPlayerTracker.h | 5 ++- dom/base/nsDOMWindowUtils.cpp | 5 ++- layout/base/nsDisplayList.cpp | 3 +- 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/dom/animation/AnimationPlayer.cpp b/dom/animation/AnimationPlayer.cpp index 2e7588ab91af..3bd4ab361d95 100644 --- a/dom/animation/AnimationPlayer.cpp +++ b/dom/animation/AnimationPlayer.cpp @@ -270,14 +270,7 @@ AnimationPlayer::DoPlay() nsIDocument* doc = GetRenderedDocument(); if (!doc) { - // If we have no rendered document (e.g. because the source content's - // target element is orphaned), then treat the animation as ready and - // start it immediately. It is probably preferable to make playing - // *always* asynchronous (e.g. by setting some additional state that - // marks this player as pending and queueing a runnable to resolve the - // start time). That situation, however, is currently rare enough that - // we don't bother for now. - StartNow(); + StartOnNextTick(Nullable()); return; } diff --git a/dom/animation/PendingPlayerTracker.cpp b/dom/animation/PendingPlayerTracker.cpp index 7377c9e9fc1a..7fb7d872b92b 100644 --- a/dom/animation/PendingPlayerTracker.cpp +++ b/dom/animation/PendingPlayerTracker.cpp @@ -46,30 +46,42 @@ StartPlayerAtTime(nsRefPtrHashKey* aKey, void* aReadyTime) { dom::AnimationPlayer* player = aKey->GetKey(); - - // For animations that are waiting until their first frame has rendered - // before starting, we record the moment when they finish painting - // as the "ready time" and make any pending layer animations start at - // that time. - // - // Here we fast-forward the player's timeline to the same "ready time" and - // then tell the player to start at the timeline's current time. - // - // Redundant calls to FastForward with the same ready time are ignored by - // AnimationTimeline. dom::AnimationTimeline* timeline = player->Timeline(); - timeline->FastForward(*static_cast(aReadyTime)); - player->StartNow(); + // When the timeline's refresh driver is under test control, its values + // have no correspondance to wallclock times so we shouldn't try to convert + // aReadyTime (which is a wallclock time) to a timeline value. Instead, the + // animation player will be started when the refresh driver is next + // advanced since this will trigger a call to StartPendingPlayersNow. + if (timeline->IsUnderTestControl()) { + return PL_DHASH_NEXT; + } + Nullable readyTime = + timeline->ToTimelineTime(*static_cast(aReadyTime)); + player->StartOnNextTick(readyTime); + + return PL_DHASH_REMOVE; +} + +void +PendingPlayerTracker::StartPendingPlayersOnNextTick(const TimeStamp& aReadyTime) +{ + mPlayPendingSet.EnumerateEntries(StartPlayerAtTime, + const_cast(&aReadyTime)); +} + +PLDHashOperator +StartPlayerNow(nsRefPtrHashKey* aKey, void*) +{ + aKey->GetKey()->StartNow(); return PL_DHASH_NEXT; } void -PendingPlayerTracker::StartPendingPlayers(const TimeStamp& aReadyTime) +PendingPlayerTracker::StartPendingPlayersNow() { - mPlayPendingSet.EnumerateEntries(StartPlayerAtTime, - const_cast(&aReadyTime)); + mPlayPendingSet.EnumerateEntries(StartPlayerNow, nullptr); mPlayPendingSet.Clear(); } diff --git a/dom/animation/PendingPlayerTracker.h b/dom/animation/PendingPlayerTracker.h index 692cc8e1bc09..a07738420ee0 100644 --- a/dom/animation/PendingPlayerTracker.h +++ b/dom/animation/PendingPlayerTracker.h @@ -29,9 +29,8 @@ public: void RemovePlayPending(dom::AnimationPlayer& aPlayer); bool IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const; - // Causes any pending players to resume at |aReadyTime| by first - // fast-forwarding their timeline to the corresponding time. - void StartPendingPlayers(const TimeStamp& aReadyTime); + void StartPendingPlayersOnNextTick(const TimeStamp& aReadyTime); + void StartPendingPlayersNow(); bool HasPendingPlayers() const { return mPlayPendingSet.Count() > 0; } private: diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 39502f4184e4..49d088e3ed2d 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2675,8 +2675,6 @@ nsDOMWindowUtils::AdvanceTimeAndRefresh(int64_t aMilliseconds) { MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - nsRefreshDriver* driver = GetPresContext()->RefreshDriver(); - // Before we advance the time, we should trigger any animations that are // waiting to start. This is because there are many tests that call this // which expect animations to start immediately. Ideally, we should make @@ -2689,10 +2687,11 @@ nsDOMWindowUtils::AdvanceTimeAndRefresh(int64_t aMilliseconds) if (doc) { PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker(); if (tracker) { - tracker->StartPendingPlayers(driver->MostRecentRefresh()); + tracker->StartPendingPlayersNow(); } } + nsRefreshDriver* driver = GetPresContext()->RefreshDriver(); driver->AdvanceTimeAndRefresh(aMilliseconds); RefPtr transaction = GetLayerTransaction(); diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 0936d035fe90..e3c7d1fc85ae 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1465,7 +1465,8 @@ StartPendingAnimationsOnSubDocuments(nsIDocument* aDocument, void* aReadyTime) // If paint-suppression is in effect then we haven't finished painting // this document yet so we shouldn't start animations if (!shell || !shell->IsPaintingSuppressed()) { - tracker->StartPendingPlayers(*static_cast(aReadyTime)); + const TimeStamp& readyTime = *static_cast(aReadyTime); + tracker->StartPendingPlayersOnNextTick(readyTime); } } aDocument->EnumerateSubDocuments(StartPendingAnimationsOnSubDocuments,