<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[260525] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/260525">260525</a></dd>
<dt>Author</dt> <dd>graouts@webkit.org</dd>
<dt>Date</dt> <dd>2020-04-22 11:47:36 -0700 (Wed, 22 Apr 2020)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Web Animations] Coordinate "update animations and send events" procedure across multiple timelines
https://bugs.webkit.org/show_bug.cgi?id=202109
<rdar://problem/59470821>

Reviewed by Dean Jackson.

LayoutTests/imported/w3c:

Mark a new test as PASS which shows that we correctly perform a single microstask checkpoint when updating multiple timelines.

* web-platform-tests/web-animations/timing-model/timelines/update-and-send-events-expected.txt:

Source/WebCore:

So far, although we did manage multiple animation timelines per document, we mostly operated
under the assumption that there really was a single timeline. In this patch we make the
"update animations and send events" procedure, which is central to the lifecycle of animations,
work with multiple timelines such that a single microtask checkpoint is performed even with multiple
timelines, whereas we would perform one per timeline before. To do this, we move much of the logic
DocumentTimeline::updateAnimationsAndSendEvents() to DocumentTimelinesController where each step is
run across each timeline, rather than running all steps for each timeline one after the other,
respecting the single microtask checkpoint in the middle of the process.

To minimize code churn at this stage, we still keep a fair bit of logic in DocumentTimeline and,
while we remove updateAnimationsAndSendEvents(), internalUpdateAnimationsAndSendEvents() and
updateCurrentTime(), we expose three methods that allow to run the pre-flight sequence in
documentWillUpdateAnimationsAndSendEvents(), collect pending events in
prepareForPendingAnimationEventsDispatch() and run the post-flight sequence
in documentDidUpdateAnimationsAndSendEvents().

None of the logic changes, this is just moving code around. In the future, more patches will move
code from DocumentTimeline up to DocumentTimelinesController such that events are enqueued there,
and animation scheduling as well. But this already lets us pass a new test that used to flakily
reject promises in the WPT test web-animations/timing-model/timelines/update-and-send-events.html.

* animation/AnimationTimeline.h:
(WebCore::AnimationTimeline::relevantAnimations const):
(WebCore::AnimationTimeline::allAnimations const):
* animation/DocumentTimeline.cpp:
(WebCore::DocumentTimeline::documentWillUpdateAnimationsAndSendEvents):
(WebCore::DocumentTimeline::documentDidUpdateAnimationsAndSendEvents):
(WebCore::DocumentTimeline::prepareForPendingAnimationEventsDispatch):
(WebCore::DocumentTimeline::updateCurrentTime): Deleted.
(WebCore::DocumentTimeline::updateAnimationsAndSendEvents): Deleted.
(WebCore::DocumentTimeline::internalUpdateAnimationsAndSendEvents): Deleted.
* animation/DocumentTimeline.h:
* animation/DocumentTimelinesController.cpp:
(WebCore::DocumentTimelinesController::DocumentTimelinesController):
(WebCore::DocumentTimelinesController::updateAnimationsAndSendEvents):
* animation/DocumentTimelinesController.h:
* animation/WebAnimationTypes.h:
* dom/Document.cpp:
(WebCore::Document::ensureTimelinesController):

LayoutTests:

Remove the flaky expectation for the improved test.

* TestExpectations:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsTestExpectations">trunk/LayoutTests/TestExpectations</a></li>
<li><a href="#trunkLayoutTestsimportedw3cChangeLog">trunk/LayoutTests/imported/w3c/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestswebanimationstimingmodeltimelinesupdateandsendeventsexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/timing-model/timelines/update-and-send-events-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreanimationAnimationTimelineh">trunk/Source/WebCore/animation/AnimationTimeline.h</a></li>
<li><a href="#trunkSourceWebCoreanimationDocumentTimelinecpp">trunk/Source/WebCore/animation/DocumentTimeline.cpp</a></li>
<li><a href="#trunkSourceWebCoreanimationDocumentTimelineh">trunk/Source/WebCore/animation/DocumentTimeline.h</a></li>
<li><a href="#trunkSourceWebCoreanimationDocumentTimelinesControllercpp">trunk/Source/WebCore/animation/DocumentTimelinesController.cpp</a></li>
<li><a href="#trunkSourceWebCoreanimationDocumentTimelinesControllerh">trunk/Source/WebCore/animation/DocumentTimelinesController.h</a></li>
<li><a href="#trunkSourceWebCoreanimationWebAnimationTypesh">trunk/Source/WebCore/animation/WebAnimationTypes.h</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/LayoutTests/ChangeLog 2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -1,5 +1,17 @@
</span><span class="cx"> 2020-04-22  Antoine Quint  <graouts@apple.com>
</span><span class="cx"> 
</span><ins>+        [Web Animations] Coordinate "update animations and send events" procedure across multiple timelines
+        https://bugs.webkit.org/show_bug.cgi?id=202109
+        <rdar://problem/59470821>
+
+        Reviewed by Dean Jackson.
+
+        Remove the flaky expectation for the improved test.
+
+        * TestExpectations:
+
+2020-04-22  Antoine Quint  <graouts@apple.com>
+
</ins><span class="cx">         [ Mojave wk1 Release ] animations/transition-and-animation-1.html is a flaky failure
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=210051
</span><span class="cx">         <rdar://problem/61345177>
</span></span></pre></div>
<a id="trunkLayoutTestsTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/TestExpectations (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/TestExpectations       2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/LayoutTests/TestExpectations  2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -4064,8 +4064,6 @@
</span><span class="cx"> 
</span><span class="cx"> webkit.org/b/207262 imported/w3c/web-platform-tests/web-animations/timing-model/animations/sync-start-times.html [ Pass ImageOnlyFailure ]
</span><span class="cx"> 
</span><del>-webkit.org/b/202109 imported/w3c/web-platform-tests/web-animations/timing-model/timelines/update-and-send-events.html [ Pass Failure ]
-
</del><span class="cx"> # WebXR is being implemented
</span><span class="cx"> webkit.org/b/208988 imported/w3c/web-platform-tests/webxr [ Skip ]
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/ChangeLog (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/ChangeLog 2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/LayoutTests/imported/w3c/ChangeLog    2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2020-04-22  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Coordinate "update animations and send events" procedure across multiple timelines
+        https://bugs.webkit.org/show_bug.cgi?id=202109
+        <rdar://problem/59470821>
+
+        Reviewed by Dean Jackson.
+
+        Mark a new test as PASS which shows that we correctly perform a single microstask checkpoint when updating multiple timelines.
+
+        * web-platform-tests/web-animations/timing-model/timelines/update-and-send-events-expected.txt:
+
</ins><span class="cx"> 2020-04-21  Sergio Villar Senin  <svillar@igalia.com>
</span><span class="cx"> 
</span><span class="cx">         Import latest changes from web-platform-test/webxr/resources to enable testing in WebKit
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestswebanimationstimingmodeltimelinesupdateandsendeventsexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/timing-model/timelines/update-and-send-events-expected.txt (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/timing-model/timelines/update-and-send-events-expected.txt      2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/timing-model/timelines/update-and-send-events-expected.txt 2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -6,5 +6,5 @@
</span><span class="cx"> PASS Queues a cancel event in transitionstart event callback 
</span><span class="cx"> PASS Sorts events for the same transition 
</span><span class="cx"> PASS Playback events with the same timeline retain the order in which they arequeued 
</span><del>-FAIL All timelines are updated before running microtasks promise_test: Unhandled rejection with value: object "AbortError: The operation was aborted."
</del><ins>+PASS All timelines are updated before running microtasks 
</ins><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/Source/WebCore/ChangeLog      2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -1,3 +1,51 @@
</span><ins>+2020-04-22  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Coordinate "update animations and send events" procedure across multiple timelines
+        https://bugs.webkit.org/show_bug.cgi?id=202109
+        <rdar://problem/59470821>
+
+        Reviewed by Dean Jackson.
+
+        So far, although we did manage multiple animation timelines per document, we mostly operated
+        under the assumption that there really was a single timeline. In this patch we make the
+        "update animations and send events" procedure, which is central to the lifecycle of animations,
+        work with multiple timelines such that a single microtask checkpoint is performed even with multiple
+        timelines, whereas we would perform one per timeline before. To do this, we move much of the logic
+        DocumentTimeline::updateAnimationsAndSendEvents() to DocumentTimelinesController where each step is
+        run across each timeline, rather than running all steps for each timeline one after the other,
+        respecting the single microtask checkpoint in the middle of the process.
+
+        To minimize code churn at this stage, we still keep a fair bit of logic in DocumentTimeline and,
+        while we remove updateAnimationsAndSendEvents(), internalUpdateAnimationsAndSendEvents() and
+        updateCurrentTime(), we expose three methods that allow to run the pre-flight sequence in
+        documentWillUpdateAnimationsAndSendEvents(), collect pending events in
+        prepareForPendingAnimationEventsDispatch() and run the post-flight sequence
+        in documentDidUpdateAnimationsAndSendEvents().
+
+        None of the logic changes, this is just moving code around. In the future, more patches will move
+        code from DocumentTimeline up to DocumentTimelinesController such that events are enqueued there,
+        and animation scheduling as well. But this already lets us pass a new test that used to flakily
+        reject promises in the WPT test web-animations/timing-model/timelines/update-and-send-events.html.
+
+        * animation/AnimationTimeline.h:
+        (WebCore::AnimationTimeline::relevantAnimations const):
+        (WebCore::AnimationTimeline::allAnimations const):
+        * animation/DocumentTimeline.cpp:
+        (WebCore::DocumentTimeline::documentWillUpdateAnimationsAndSendEvents):
+        (WebCore::DocumentTimeline::documentDidUpdateAnimationsAndSendEvents):
+        (WebCore::DocumentTimeline::prepareForPendingAnimationEventsDispatch):
+        (WebCore::DocumentTimeline::updateCurrentTime): Deleted.
+        (WebCore::DocumentTimeline::updateAnimationsAndSendEvents): Deleted.
+        (WebCore::DocumentTimeline::internalUpdateAnimationsAndSendEvents): Deleted.
+        * animation/DocumentTimeline.h:
+        * animation/DocumentTimelinesController.cpp:
+        (WebCore::DocumentTimelinesController::DocumentTimelinesController):
+        (WebCore::DocumentTimelinesController::updateAnimationsAndSendEvents):
+        * animation/DocumentTimelinesController.h:
+        * animation/WebAnimationTypes.h:
+        * dom/Document.cpp:
+        (WebCore::Document::ensureTimelinesController):
+
</ins><span class="cx"> 2020-04-22  Eric Carlson  <eric.carlson@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [iOS] Add a quirk to keep gizmodo videos visible when playing in fullscreen.
</span></span></pre></div>
<a id="trunkSourceWebCoreanimationAnimationTimelineh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/animation/AnimationTimeline.h (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/animation/AnimationTimeline.h       2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/Source/WebCore/animation/AnimationTimeline.h  2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -50,6 +50,9 @@
</span><span class="cx"> public:
</span><span class="cx">     virtual bool isDocumentTimeline() const { return false; }
</span><span class="cx"> 
</span><ins>+    AnimationCollection relevantAnimations() const { return m_animations; }
+    Vector<WeakPtr<WebAnimation>> allAnimations() const { return m_allAnimations; }
+
</ins><span class="cx">     void forgetAnimation(WebAnimation*);
</span><span class="cx">     virtual void animationTimingDidChange(WebAnimation&);
</span><span class="cx">     virtual void removeAnimation(WebAnimation&);
</span></span></pre></div>
<a id="trunkSourceWebCoreanimationDocumentTimelinecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/animation/DocumentTimeline.cpp (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/animation/DocumentTimeline.cpp      2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/Source/WebCore/animation/DocumentTimeline.cpp 2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -33,7 +33,6 @@
</span><span class="cx"> #include "DeclarativeAnimation.h"
</span><span class="cx"> #include "Document.h"
</span><span class="cx"> #include "DocumentTimelinesController.h"
</span><del>-#include "EventLoop.h"
</del><span class="cx"> #include "EventNames.h"
</span><span class="cx"> #include "GraphicsLayer.h"
</span><span class="cx"> #include "KeyframeEffect.h"
</span><span class="lines">@@ -362,7 +361,7 @@
</span><span class="cx">     return !m_animations.isEmpty() || !m_pendingAnimationEvents.isEmpty() || !m_acceleratedAnimationsPendingRunningStateChange.isEmpty();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void DocumentTimeline::updateCurrentTime(DOMHighResTimeStamp timestamp)
</del><ins>+DocumentTimeline::ShouldUpdateAnimationsAndSendEvents DocumentTimeline::documentWillUpdateAnimationsAndSendEvents(DOMHighResTimeStamp timestamp)
</ins><span class="cx"> {
</span><span class="cx">     // We need to freeze the current time even if no animation is running.
</span><span class="cx">     // document.timeline.currentTime may be called from a rAF callback and
</span><span class="lines">@@ -369,29 +368,14 @@
</span><span class="cx">     // it has to match the rAF timestamp.
</span><span class="cx">     if (!m_isSuspended || !shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState())
</span><span class="cx">         cacheCurrentTime(timestamp);
</span><del>-}
</del><span class="cx"> 
</span><del>-void DocumentTimeline::updateAnimationsAndSendEvents()
-{
</del><span class="cx">     // Updating animations and sending events may invalidate the timing of some animations, so we must set the m_animationResolutionScheduled
</span><span class="cx">     // flag to false prior to running that procedure to allow animation with timing model updates to schedule updates.
</span><del>-    m_animationResolutionScheduled = false;
</del><ins>+    bool wasAnimationResolutionScheduled = std::exchange(m_animationResolutionScheduled, false);
</ins><span class="cx"> 
</span><del>-    if (m_isSuspended)
-        return;
</del><ins>+    if (!wasAnimationResolutionScheduled || m_isSuspended || !shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState())
+        return DocumentTimeline::ShouldUpdateAnimationsAndSendEvents::No;
</ins><span class="cx"> 
</span><del>-    if (!shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState())
-        return;
-
-    internalUpdateAnimationsAndSendEvents();
-    applyPendingAcceleratedAnimations();
-
-    if (!m_animationResolutionScheduled)
-        scheduleNextTick();
-}
-
-void DocumentTimeline::internalUpdateAnimationsAndSendEvents()
-{
</del><span class="cx">     m_numberOfAnimationTimelineInvalidationsForTesting++;
</span><span class="cx"> 
</span><span class="cx">     // enqueueAnimationEvent() calls scheduleAnimationResolution() to ensure that the "update animations and send events"
</span><span class="lines">@@ -399,77 +383,15 @@
</span><span class="cx">     // this procedure is running should not schedule animation resolution until the event queue has been cleared.
</span><span class="cx">     m_shouldScheduleAnimationResolutionForNewPendingEvents = false;
</span><span class="cx"> 
</span><del>-    // https://drafts.csswg.org/web-animations/#update-animations-and-send-events
</del><ins>+    return DocumentTimeline::ShouldUpdateAnimationsAndSendEvents::Yes;
+}
</ins><span class="cx"> 
</span><del>-    // 1. Update the current time of all timelines associated with doc passing now as the timestamp.
</del><ins>+void DocumentTimeline::documentDidUpdateAnimationsAndSendEvents()
+{
+    applyPendingAcceleratedAnimations();
</ins><span class="cx"> 
</span><del>-    Vector<RefPtr<WebAnimation>> animationsToRemove;
-    Vector<RefPtr<CSSTransition>> completedTransitions;
-
-    for (auto& animation : m_animations) {
-        if (animation->timeline() != this) {
-            ASSERT(!animation->timeline());
-            animationsToRemove.append(animation);
-            continue;
-        }
-
-        // This will notify the animation that timing has changed and will call automatically
-        // schedule invalidation if required for this animation.
-        animation->tick();
-
-        if (!animation->isRelevant() && !animation->needsTick())
-            animationsToRemove.append(animation);
-
-        if (!animation->needsTick() && is<CSSTransition>(animation) && animation->playState() == WebAnimation::PlayState::Finished) {
-            auto* transition = downcast<CSSTransition>(animation.get());
-            if (transition->owningElement())
-                completedTransitions.append(transition);
-        }
-    }
-
-    // 2. Remove replaced animations for doc.
-    removeReplacedAnimations();
-
-    // 3. Perform a microtask checkpoint.
-    if (auto document = makeRefPtr(this->document()))
-        document->eventLoop().performMicrotaskCheckpoint();
-
-    // 4. Let events to dispatch be a copy of doc's pending animation event queue.
-    // 5. Clear doc's pending animation event queue.
-    auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
-    m_shouldScheduleAnimationResolutionForNewPendingEvents = true;
-
-    // 6. Perform a stable sort of the animation events in events to dispatch as follows.
-    std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), [] (const Ref<AnimationEventBase>& lhs, const Ref<AnimationEventBase>& rhs) {
-        // 1. Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
-        // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
-        // 2. Within events with equal scheduled event times, sort by their composite order. FIXME: We don't do this.
-        if (lhs->timelineTime() && !rhs->timelineTime())
-            return false;
-        if (!lhs->timelineTime() && rhs->timelineTime())
-            return true;
-        if (!lhs->timelineTime() && !rhs->timelineTime())
-            return true;
-        return lhs->timelineTime().value() < rhs->timelineTime().value();
-    });
-
-    // 7. Dispatch each of the events in events to dispatch at their corresponding target using the order established in the previous step.
-    for (auto& pendingAnimationEvent : pendingAnimationEvents)
-        pendingAnimationEvent->target()->dispatchEvent(pendingAnimationEvent);
-
-    // This will cancel any scheduled invalidation if we end up removing all animations.
-    for (auto& animation : animationsToRemove) {
-        // An animation that was initially marked as irrelevant may have changed while we were sending events, so we run the same
-        // check that we ran to add it to animationsToRemove in the first place.
-        if (!animation->isRelevant() && !animation->needsTick())
-            removeAnimation(*animation);
-    }
-
-    // Now that animations that needed removal have been removed, let's update the list of completed transitions.
-    // This needs to happen after dealing with the list of animations to remove as the animation may have been
-    // removed from the list of completed transitions otherwise.
-    for (auto& completedTransition : completedTransitions)
-        transitionDidComplete(completedTransition);
</del><ins>+    if (!m_animationResolutionScheduled)
+        scheduleNextTick();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool DocumentTimeline::animationCanBeRemoved(WebAnimation& animation)
</span><span class="lines">@@ -735,6 +657,12 @@
</span><span class="cx">         scheduleAnimationResolution();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+AnimationEvents DocumentTimeline::prepareForPendingAnimationEventsDispatch()
+{
+    m_shouldScheduleAnimationResolutionForNewPendingEvents = true;
+    return std::exchange(m_pendingAnimationEvents, { });
+}
+
</ins><span class="cx"> Vector<std::pair<String, double>> DocumentTimeline::acceleratedAnimationsForElement(Element& element) const
</span><span class="cx"> {
</span><span class="cx">     auto* renderer = element.renderer();
</span></span></pre></div>
<a id="trunkSourceWebCoreanimationDocumentTimelineh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/animation/DocumentTimeline.h (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/animation/DocumentTimeline.h        2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/Source/WebCore/animation/DocumentTimeline.h   2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -58,6 +58,7 @@
</span><span class="cx">     void removeAnimation(WebAnimation&) override;
</span><span class="cx">     void animationWasAddedToElement(WebAnimation&, Element&) final;
</span><span class="cx">     void animationWasRemovedFromElement(WebAnimation&, Element&) final;
</span><ins>+    void transitionDidComplete(RefPtr<CSSTransition>);
</ins><span class="cx"> 
</span><span class="cx">     // If possible, compute the visual extent of any transform animation on the given renderer
</span><span class="cx">     // using the given rect, returning the result in the rect. Return false if there is some
</span><span class="lines">@@ -72,9 +73,11 @@
</span><span class="cx"> 
</span><span class="cx">     void enqueueAnimationEvent(AnimationEventBase&);
</span><span class="cx">     
</span><del>-    bool scheduledUpdate() const { return m_animationResolutionScheduled; }
-    void updateCurrentTime(DOMHighResTimeStamp timestamp);
-    void updateAnimationsAndSendEvents();
</del><ins>+    enum class ShouldUpdateAnimationsAndSendEvents : uint8_t { Yes, No };
+    ShouldUpdateAnimationsAndSendEvents documentWillUpdateAnimationsAndSendEvents(DOMHighResTimeStamp);
+    void removeReplacedAnimations();
+    AnimationEvents prepareForPendingAnimationEventsDispatch();
+    void documentDidUpdateAnimationsAndSendEvents();
</ins><span class="cx"> 
</span><span class="cx">     void updateThrottlingState();
</span><span class="cx">     WEBCORE_EXPORT Seconds animationInterval() const;
</span><span class="lines">@@ -98,9 +101,7 @@
</span><span class="cx">     void clearTickScheduleTimer();
</span><span class="cx">     void internalUpdateAnimationsAndSendEvents();
</span><span class="cx">     void updateListOfElementsWithRunningAcceleratedAnimationsForElement(Element&);
</span><del>-    void transitionDidComplete(RefPtr<CSSTransition>);
</del><span class="cx">     void scheduleNextTick();
</span><del>-    void removeReplacedAnimations();
</del><span class="cx">     bool animationCanBeRemoved(WebAnimation&);
</span><span class="cx">     bool shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState() const;
</span><span class="cx"> 
</span><span class="lines">@@ -108,7 +109,7 @@
</span><span class="cx">     GenericTaskQueue<Timer> m_currentTimeClearingTaskQueue;
</span><span class="cx">     HashSet<RefPtr<WebAnimation>> m_acceleratedAnimationsPendingRunningStateChange;
</span><span class="cx">     HashSet<Element*> m_elementsWithRunningAcceleratedAnimations;
</span><del>-    Vector<Ref<AnimationEventBase>> m_pendingAnimationEvents;
</del><ins>+    AnimationEvents m_pendingAnimationEvents;
</ins><span class="cx">     WeakPtr<Document> m_document;
</span><span class="cx">     Markable<Seconds, Seconds::MarkableTraits> m_cachedCurrentTime;
</span><span class="cx">     Seconds m_originTime;
</span></span></pre></div>
<a id="trunkSourceWebCoreanimationDocumentTimelinesControllercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/animation/DocumentTimelinesController.cpp (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/animation/DocumentTimelinesController.cpp   2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/Source/WebCore/animation/DocumentTimelinesController.cpp      2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -26,11 +26,18 @@
</span><span class="cx"> #include "config.h"
</span><span class="cx"> #include "DocumentTimelinesController.h"
</span><span class="cx"> 
</span><ins>+#include "AnimationEventBase.h"
+#include "CSSTransition.h"
+#include "Document.h"
</ins><span class="cx"> #include "DocumentTimeline.h"
</span><ins>+#include "EventLoop.h"
+#include "WebAnimation.h"
+#include "WebAnimationTypes.h"
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-DocumentTimelinesController::DocumentTimelinesController()
</del><ins>+DocumentTimelinesController::DocumentTimelinesController(Document& document)
+    : m_document(document)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -58,20 +65,95 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!m_timelines.hasNullReferences());
</span><span class="cx"> 
</span><del>-    // We need to copy m_timelines before iterating over its members since calling updateAnimationsAndSendEvents() may mutate m_timelines.
-    Vector<RefPtr<DocumentTimeline>> timelines;
-    bool shouldUpdateAnimations = false;
-    for (auto& timeline : m_timelines) {
-        if (timeline.scheduledUpdate())
-            shouldUpdateAnimations = true;
-        timelines.append(&timeline);
</del><ins>+    // We need to copy m_timelines before iterating over its members since the steps in this procedure may mutate m_timelines.
+    Vector<RefPtr<DocumentTimeline>> protectedTimelines;
+    for (auto& timeline : m_timelines)
+        protectedTimelines.append(&timeline);
+
+    // 1. Update the current time of all timelines associated with doc passing now as the timestamp.
+    Vector<DocumentTimeline*> timelinesToUpdate;
+    Vector<RefPtr<WebAnimation>> animationsToRemove;
+    Vector<RefPtr<CSSTransition>> completedTransitions;
+    for (auto& timeline : protectedTimelines) {
+        auto shouldUpdateAnimationsAndSendEvents = timeline->documentWillUpdateAnimationsAndSendEvents(timestamp);
+        if (shouldUpdateAnimationsAndSendEvents == DocumentTimeline::ShouldUpdateAnimationsAndSendEvents::No)
+            continue;
+
+        timelinesToUpdate.append(timeline.get());
+
+        for (auto& animation : timeline->relevantAnimations()) {
+            if (animation->timeline() != timeline.get()) {
+                ASSERT(!animation->timeline());
+                animationsToRemove.append(animation);
+                continue;
+            }
+
+            // This will notify the animation that timing has changed and will call automatically
+            // schedule invalidation if required for this animation.
+            animation->tick();
+
+            if (!animation->isRelevant() && !animation->needsTick())
+                animationsToRemove.append(animation);
+
+            if (!animation->needsTick() && is<CSSTransition>(animation) && animation->playState() == WebAnimation::PlayState::Finished) {
+                auto* transition = downcast<CSSTransition>(animation.get());
+                if (transition->owningElement())
+                    completedTransitions.append(transition);
+            }
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    for (auto& timeline : timelines) {
-        timeline->updateCurrentTime(timestamp);
-        if (shouldUpdateAnimations)
-            timeline->updateAnimationsAndSendEvents();
</del><ins>+    if (timelinesToUpdate.isEmpty())
+        return;
+
+    // 2. Remove replaced animations for doc.
+    for (auto& timeline : m_timelines)
+        timeline.removeReplacedAnimations();
+
+    // 3. Perform a microtask checkpoint.
+    auto document = makeRefPtr(m_document);
+    document->eventLoop().performMicrotaskCheckpoint();
+
+    // 4. Let events to dispatch be a copy of doc's pending animation event queue.
+    // 5. Clear doc's pending animation event queue.
+    AnimationEvents events;
+    for (auto& timeline : timelinesToUpdate)
+        events.appendVector(timeline->prepareForPendingAnimationEventsDispatch());
+
+    // 6. Perform a stable sort of the animation events in events to dispatch as follows.
+    std::stable_sort(events.begin(), events.end(), [] (const Ref<AnimationEventBase>& lhs, const Ref<AnimationEventBase>& rhs) {
+        // 1. Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
+        // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
+        // 2. Within events with equal scheduled event times, sort by their composite order. FIXME: We don't do this.
+        if (lhs->timelineTime() && !rhs->timelineTime())
+            return false;
+        if (!lhs->timelineTime() && rhs->timelineTime())
+            return true;
+        if (!lhs->timelineTime() && !rhs->timelineTime())
+            return true;
+        return lhs->timelineTime().value() < rhs->timelineTime().value();
+    });
+
+    // 7. Dispatch each of the events in events to dispatch at their corresponding target using the order established in the previous step.
+    for (auto& event : events)
+        event->target()->dispatchEvent(event);
+
+    // This will cancel any scheduled invalidation if we end up removing all animations.
+    for (auto& animation : animationsToRemove) {
+        // An animation that was initially marked as irrelevant may have changed while we were sending events, so we run the same
+        // check that we ran to add it to animationsToRemove in the first place.
+        if (!animation->isRelevant() && !animation->needsTick())
+            animation->timeline()->removeAnimation(*animation);
</ins><span class="cx">     }
</span><ins>+
+    // Now that animations that needed removal have been removed, let's update the list of completed transitions.
+    // This needs to happen after dealing with the list of animations to remove as the animation may have been
+    // removed from the list of completed transitions otherwise.
+    for (auto& completedTransition : completedTransitions)
+        downcast<DocumentTimeline>(*completedTransition->timeline()).transitionDidComplete(completedTransition);
+
+    for (auto& timeline : timelinesToUpdate)
+        timeline->documentDidUpdateAnimationsAndSendEvents();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreanimationDocumentTimelinesControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/animation/DocumentTimelinesController.h (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/animation/DocumentTimelinesController.h     2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/Source/WebCore/animation/DocumentTimelinesController.h        2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -30,12 +30,15 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+class CSSTransition;
+class Document;
</ins><span class="cx"> class DocumentTimeline;
</span><ins>+class WebAnimation;
</ins><span class="cx"> 
</span><span class="cx"> class DocumentTimelinesController {
</span><span class="cx">     WTF_MAKE_FAST_ALLOCATED;
</span><span class="cx"> public:
</span><del>-    explicit DocumentTimelinesController();
</del><ins>+    explicit DocumentTimelinesController(Document&);
</ins><span class="cx">     ~DocumentTimelinesController();
</span><span class="cx"> 
</span><span class="cx">     void addTimeline(DocumentTimeline&);
</span><span class="lines">@@ -44,8 +47,13 @@
</span><span class="cx">     void updateAnimationsAndSendEvents(DOMHighResTimeStamp);
</span><span class="cx"> 
</span><span class="cx"> private:
</span><ins>+    struct AnimationsToProcess {
+        Vector<RefPtr<WebAnimation>> animationsToRemove;
+        Vector<RefPtr<CSSTransition>> completedTransitions;
+    };
+
</ins><span class="cx">     WeakHashSet<DocumentTimeline> m_timelines;
</span><ins>+    Document& m_document;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span><del>-
</del></span></pre></div>
<a id="trunkSourceWebCoreanimationWebAnimationTypesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/animation/WebAnimationTypes.h (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/animation/WebAnimationTypes.h       2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/Source/WebCore/animation/WebAnimationTypes.h  2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -32,6 +32,7 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+class AnimationEventBase;
</ins><span class="cx"> class AnimationList;
</span><span class="cx"> class CSSAnimation;
</span><span class="cx"> class CSSTransition;
</span><span class="lines">@@ -52,6 +53,7 @@
</span><span class="cx"> using MarkableDouble = Markable<double, WebAnimationsMarkableDoubleTraits>;
</span><span class="cx"> 
</span><span class="cx"> using AnimationCollection = ListHashSet<RefPtr<WebAnimation>>;
</span><ins>+using AnimationEvents = Vector<Ref<AnimationEventBase>>;
</ins><span class="cx"> using PropertyToTransitionMap = HashMap<CSSPropertyID, RefPtr<CSSTransition>>;
</span><span class="cx"> using CSSAnimationCollection = ListHashSet<RefPtr<CSSAnimation>>;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (260524 => 260525)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp    2020-04-22 18:38:36 UTC (rev 260524)
+++ trunk/Source/WebCore/dom/Document.cpp       2020-04-22 18:47:36 UTC (rev 260525)
</span><span class="lines">@@ -8080,7 +8080,7 @@
</span><span class="cx"> DocumentTimelinesController& Document::ensureTimelinesController()
</span><span class="cx"> {
</span><span class="cx">     if (!m_timelinesController)
</span><del>-        m_timelinesController = makeUnique<DocumentTimelinesController>();
</del><ins>+        m_timelinesController = makeUnique<DocumentTimelinesController>(*this);
</ins><span class="cx">     return *m_timelinesController.get();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>