Bug 1253476 - Make Animation::Tick do finish actions synchronously; r=hiro
authorBrian Birtles <birtles@gmail.com>
Mon, 20 May 2019 05:20:17 +0000
changeset 474499 861caf0b6714f5a119f04ba3e1e9badf44986e60
parent 474498 6e5b24c61e3aa7b01cebef924128bdf3b8019d7c
child 474500 0e25f7b0790b67bcd5614384def4f7158aa3048f
push id85838
push userbbirtles@mozilla.com
push dateMon, 20 May 2019 10:08:11 +0000
treeherderautoland@df36c132c6b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro
bugs1253476
milestone68.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 1253476 - Make Animation::Tick do finish actions synchronously; r=hiro Animation::UpdateTiming takes a SyncNotifyFlag parameter. This is passed to UpdateFinishedState where it determines how we handle finish actions. If it is async we queue a microtask where we re-evaluate if the animation is finished or not before queuing events / resolving promises. That allows code like the following to _not_ trigger finish events: ``` const animation = elem.animate({...}, 1000); animation.currentTime += 1000; animation.effect.updateTiming({ duration: 2000 }); ``` (Since the check that the animation is finished will run in a microtask _after_ the call to updateTiming.) When the flag is "sync" we still don't _actually_ run the finish actions entirely synchronously: the finished promise is resolved synchronously, but resolving a promise actually queues a microtask for each callback. Likewise, the finish event is queued synchronously, but not dispatched. Since there should be no opportunity for script to run between when we call Animation::Tick and when we run the next microtask checkpoint (currently at the end of DocumentTimeline::WillRefresh but that will change slightly in the next patch in this series) there is no need to introduce the extra "async" microtask for re-evaluating an animation's finished state. Instead it should be possible to use the "sync" finishing behavior. Such a change should be unobservable to Web content but will reduce indirection somewhat. Differential Revision: https://phabricator.services.mozilla.com/D30318
dom/animation/Animation.cpp
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -645,17 +645,17 @@ void Animation::Tick() {
   }
 
   if (IsPossiblyOrphanedPendingAnimation()) {
     MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTimeAsDuration().IsNull(),
                "Orphaned pending animations should have an active timeline");
     FinishPendingAt(mTimeline->GetCurrentTimeAsDuration().Value());
   }
 
-  UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
+  UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Sync);
 
   if (!mEffect) {
     return;
   }
 
   // Update layers if we are newly finished.
   KeyframeEffect* keyframeEffect = mEffect->AsKeyframeEffect();
   if (keyframeEffect && !keyframeEffect->Properties().IsEmpty() &&
@@ -1174,17 +1174,17 @@ void Animation::ResumeAt(const TimeDurat
                                            mPlaybackRate);
     if (mPlaybackRate == 0) {
       mHoldTime.SetValue(currentTimeToMatch);
     }
   }
 
   mPendingState = PendingState::NotPending;
 
-  UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
+  UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Sync);
 
   // If we had a pending playback rate, we will have now applied it so we need
   // to notify observers.
   if (hadPendingPlaybackRate && IsRelevant()) {
     nsNodeUtils::AnimationChanged(this);
   }
 
   if (mReady) {