Title: [224472] trunk
Revision
224472
Author
grao...@webkit.org
Date
2017-11-05 14:34:10 -0800 (Sun, 05 Nov 2017)

Log Message

[Web Animations] Schedule animations registered on the document timeline
https://bugs.webkit.org/show_bug.cgi?id=179236
<rdar://problem/35332669>

Reviewed by Dean Jackson.

Source/WebCore:

We now schedule animations contained in the document timeline using a three-step approach.

1. Each time an object that is part of the timing model changes one of its timing properties, we call
   animationTimingModelDidChange() on the document timeline. This schedules performInvalidationTask()
   to be called when the current run loop completes, such that we invalidate the timing model just once
   per run loop.

2. Once performInvalidationTask() is called, the timing model is invalidated in updateAnimationSchedule().
   We iterate over the registered animations on the timineline and identify the shortest interval between
   the current time and the next moment one of the animations requires a tick to update its value. If we
   find a value below 15ms, we schedule animations to be resolved with scheduleAnimationResolution() right
   away. If the value is above 15ms, and not inifinity, we schedule a one-shot timer for that interval to
   call scheduleAnimationResolution().

3. Once scheduleAnimationResolution() is called, we call scheduleAnimation() on the shared DisplayRefreshMonitorManager
   to be notified when the next display refresh occurs to actually resolve animations with resolveAnimations().

Note that, in this patch, resolveAnimations() does nothing, we will add support for interpolating values in
a future patch.

Another important thing to note is that every time the document timeline's current time is requested, we cache
it for the duration of the run loop such that the timing model always uses the same value during a given run loop.

Finally, to support tests where we check the state of the timing model by manually advancing time, we expose a
new pause() method on AnimationTimeline for tests to call to avoid the timeline to self-advance.

* animation/AnimationTimeline.cpp:
(WebCore::AnimationTimeline::addAnimation): Mark that the timing model changed as a result of adding an animation.
(WebCore::AnimationTimeline::removeAnimation): Mark that the timing model changed as a result of removing an animation.
(WebCore::AnimationTimeline::bindingsCurrentTime): Update the method signature to no longer be const and call into
currentTime() instead of reading directly from the m_currentTime member variable since a subclass, like DocumentTimeline,
may have a custom currentTime() implementation.
(WebCore::AnimationTimeline::setCurrentTime): Mark that the timing model changed as a result of the timeline current time
changing.
(WebCore::AnimationTimeline::bindingsCurrentTime const): Deleted.
* animation/AnimationTimeline.h:
(WebCore::AnimationTimeline::currentTime): Change both methods signatures to no longer be const so that DocumentTimeline's
implementation of currentTime() may cache the current time in a member variable, enqueuing a callback when the run loop
completes for this member variable to be reset, and updating some states.
(WebCore::AnimationTimeline::pause): To be implemented by subclasses.
(WebCore::AnimationTimeline::animationTimingModelDidChange): Add a new virtual method to indicate that the timing model
needs invalidating.
(WebCore::AnimationTimeline::animations const): Add an accessor to allow animations to be accessed by a subclass.
* animation/DocumentTimeline.cpp:
(WebCore::DocumentTimeline::create):
(WebCore::DocumentTimeline::DocumentTimeline): Update the constructor signature to receive a Document and a PlatformDisplayID
since we need a reference to the Document to get at the nowTime() and a PlatformDisplayID to create the DisplayRefreshMonitor.
(WebCore::DocumentTimeline::~DocumentTimeline): Close the task queue when the timeline gets destroyed.
(WebCore::DocumentTimeline::currentTime): If we don't have a current cahed current time, compute one and schedule
the invalidation task if needed so that we may reset the cached value as the run loop completes.
(WebCore::DocumentTimeline::pause): Allows the timeline not to self-advance, for testing purposes only.
(WebCore::DocumentTimeline::animationTimingModelDidChange): If we haven't already done so, mark that we need to update our
animation schedule in the invalidation task and schedule that task if not scheduled yet.
(WebCore::DocumentTimeline::scheduleInvalidationTaskIfNeeded): Schedule the invalidation task to run as the run loop completes
if we haven't already done so.
(WebCore::DocumentTimeline::performInvalidationTask): Update the animation schedule if needed and reset the cached current
time value.
(WebCore::DocumentTimeline::updateAnimationSchedule): Iterate over registed animations and find the shortest interval until
one of them needs to update their animation. If the shortest interval is below 15ms, schedule the animation resolution right
away. If the shortest inverval is finite and above 15ms, then schedule a one-shot timer for that interval to perform the
animation resolution then.
(WebCore::DocumentTimeline::animationScheduleTimerFired): The one-shot timer to perform the animation resolution has fired,
we call scheduleAnimationResolution().
(WebCore::DocumentTimeline::scheduleAnimationResolution): We call scheduleAnimation() on the shared DisplayRefreshMonitorManager
so that we may resolve animations on the next display refresh, or start a timer if the DisplayRefreshMonitorManager is not available.
(WebCore::DocumentTimeline::displayRefreshFired): The display is about to refresh, we call resolveAnimations().
(WebCore::DocumentTimeline::animationResolutionTimerFired): The fallback animation resolution timer has fired, we call resolveAnimations().
(WebCore::DocumentTimeline::resolveAnimations): Currently do nothing, this is where we'll iterate over registered animations to
update them with the current time.
(WebCore::DocumentTimeline::windowScreenDidChange): Notify the shared DisplayRefreshMonitorManager that the PlatformDisplayID
changed.
(WebCore::DocumentTimeline::createDisplayRefreshMonitor const): Provide a DisplayRefreshMonitor as part of the
DisplayRefreshMonitorClient protocol.
* animation/DocumentTimeline.h:
* animation/WebAnimation.cpp:
(WebCore::WebAnimation::create): Remove extra white space.
(WebCore::WebAnimation::setStartTime): Mark that the timing model changed as a result of changing this animation's start time.
(WebCore::WebAnimation::timeToNextRequiredTick const): Compute the interval until the next time we need to resolve this animation.
If the provided current time is before this animation's start time, compute the delay until the start time. If the current time
is after the animation's start time but before the animation's end time, indicate that we want to resolve the animation again
right away and return 0ms. In any other case, return an infinite interval to indicate that we don't need to be refreshed after
the provided time.
* animation/WebAnimation.h:
* dom/Document.cpp:
(WebCore::Document::windowScreenDidChange): Notify the document timeline that the PlatformDisplayID changed.
(WebCore::Document::timeline): Provide the Document and the PlatformDisplayID to the DocumentTimeline.
* testing/Internals.cpp:
(WebCore::Internals::pauseTimeline):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

Adopt the new internals.pauseTimeline() method to ensure that the existing
tests do not have a self-advancing timeline since we're interested in checking
the timing model state based on manually setting the timeline current time.

Also update some WPT expectations with some progressions.

* TestExpectations: Mark two tests as flaky due to the sample time being logged
in the failure.
* http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline-expected.txt:
* http/wpt/web-animations/timing-model/animations/current-time-expected.txt:
* http/wpt/web-animations/timing-model/animations/set-the-animation-start-time-expected.txt:
* http/wpt/wk-web-animations/timing-model/animation-creation-basic.html:
* http/wpt/wk-web-animations/timing-model/animation-current-time.html:
* http/wpt/wk-web-animations/timing-model/animation-effect-timing.html:
* http/wpt/wk-web-animations/timing-model/animation-effect.html:
* http/wpt/wk-web-animations/timing-model/animation-interface-effect-property.html:
* http/wpt/wk-web-animations/timing-model/animation-interface-start-time-property.html:
* http/wpt/wk-web-animations/timing-model/animation-playback-rate.html:
* http/wpt/wk-web-animations/timing-model/document-timeline.html:
* http/wpt/wk-web-animations/timing-model/keyframe-effect-interface-timing-duration.html:
* http/wpt/wk-web-animations/timing-model/keyframe-effect.html:
* http/wpt/wk-web-animations/timing-model/timeline-current-time.html:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (224471 => 224472)


--- trunk/LayoutTests/ChangeLog	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/ChangeLog	2017-11-05 22:34:10 UTC (rev 224472)
@@ -1,3 +1,34 @@
+2017-11-05  Antoine Quint  <grao...@apple.com>
+
+        [Web Animations] Schedule animations registered on the document timeline
+        https://bugs.webkit.org/show_bug.cgi?id=179236
+        <rdar://problem/35332669>
+
+        Reviewed by Dean Jackson.
+
+        Adopt the new internals.pauseTimeline() method to ensure that the existing
+        tests do not have a self-advancing timeline since we're interested in checking
+        the timing model state based on manually setting the timeline current time.
+
+        Also update some WPT expectations with some progressions.
+
+        * TestExpectations: Mark two tests as flaky due to the sample time being logged
+        in the failure.
+        * http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline-expected.txt:
+        * http/wpt/web-animations/timing-model/animations/current-time-expected.txt:
+        * http/wpt/web-animations/timing-model/animations/set-the-animation-start-time-expected.txt:
+        * http/wpt/wk-web-animations/timing-model/animation-creation-basic.html:
+        * http/wpt/wk-web-animations/timing-model/animation-current-time.html:
+        * http/wpt/wk-web-animations/timing-model/animation-effect-timing.html:
+        * http/wpt/wk-web-animations/timing-model/animation-effect.html:
+        * http/wpt/wk-web-animations/timing-model/animation-interface-effect-property.html:
+        * http/wpt/wk-web-animations/timing-model/animation-interface-start-time-property.html:
+        * http/wpt/wk-web-animations/timing-model/animation-playback-rate.html:
+        * http/wpt/wk-web-animations/timing-model/document-timeline.html:
+        * http/wpt/wk-web-animations/timing-model/keyframe-effect-interface-timing-duration.html:
+        * http/wpt/wk-web-animations/timing-model/keyframe-effect.html:
+        * http/wpt/wk-web-animations/timing-model/timeline-current-time.html:
+
 2017-11-05  Per Arne Vollan  <pvol...@apple.com>
 
         Mark http/tests/security/xss-DENIED-xsl-external-entity.xml as a flaky failure on Windows.

Modified: trunk/LayoutTests/TestExpectations (224471 => 224472)


--- trunk/LayoutTests/TestExpectations	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/TestExpectations	2017-11-05 22:34:10 UTC (rev 224472)
@@ -1582,3 +1582,6 @@
 webkit.org/b/177997 webgl/1.0.2/conformance/textures/copy-tex-image-2d-formats.html [ Pass Failure ]
 
 webkit.org/b/179069 imported/w3c/web-platform-tests/html/semantics/embedded-content/the-iframe-element/sandbox_032.htm [ Pass Failure ]
+
+webkit.org/b/179287 http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline.html [ Pass Failure ]
+webkit.org/b/179287 http/wpt/web-animations/timing-model/animations/set-the-animation-start-time.html [ Pass Failure ]

Modified: trunk/LayoutTests/http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline-expected.txt (224471 => 224472)


--- trunk/LayoutTests/http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline-expected.txt	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline-expected.txt	2017-11-05 22:34:10 UTC (rev 224472)
@@ -1,7 +1,7 @@
 
 PASS document.timeline identity tests 
-FAIL document.timeline.currentTime value tests assert_true: document.timeline.currentTime is positive expected true got false
-FAIL document.timeline.currentTime liveness tests assert_greater_than: document.timeline.currentTime increases between script blocks expected a number but got a "object"
-FAIL iframe document.timeline.currentTime liveness tests assert_greater_than: iframe document.timeline.currentTime increases between script blocks expected a number but got a "object"
-FAIL document.timeline.currentTime time should be the same for all RAF callbacks in a frame assert_greater_than_equal: currentTime should have progressed expected a number but got a "object"
- 
+FAIL document.timeline.currentTime value tests assert_equals: document.timeline.currentTime matches requestAnimationFrame time expected 552.3000000000001 but got 0.5538000000000001
+PASS document.timeline.currentTime liveness tests 
+PASS iframe document.timeline.currentTime liveness tests 
+PASS document.timeline.currentTime time should be the same for all RAF callbacks in a frame 
+

Modified: trunk/LayoutTests/http/wpt/web-animations/timing-model/animations/current-time-expected.txt (224471 => 224472)


--- trunk/LayoutTests/http/wpt/web-animations/timing-model/animations/current-time-expected.txt	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/web-animations/timing-model/animations/current-time-expected.txt	2017-11-05 22:34:10 UTC (rev 224472)
@@ -2,6 +2,6 @@
 FAIL The current time returns the hold time when set animation.play is not a function. (In 'animation.play()', 'animation.play' is undefined)
 FAIL The current time is unresolved when there is no associated timeline (and no hold time is set) undefined is not an object (evaluating 'animation.ready.then')
 PASS The current time is unresolved when the start time is unresolved (and no hold time is set) 
-FAIL The current time is calculated from the timeline time, start time and playback rate assert_approx_equals: Animation has a unresolved start time expected a number but got a "object"
+PASS The current time is calculated from the timeline time, start time and playback rate 
 FAIL The current time does not progress if playback rate is 0 createDiv(t).animate is not a function. (In 'createDiv(t).animate(null, 100 * MS_PER_SEC)', 'createDiv(t).animate' is undefined)
 

Modified: trunk/LayoutTests/http/wpt/web-animations/timing-model/animations/set-the-animation-start-time-expected.txt (224471 => 224472)


--- trunk/LayoutTests/http/wpt/web-animations/timing-model/animations/set-the-animation-start-time-expected.txt	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/web-animations/timing-model/animations/set-the-animation-start-time-expected.txt	2017-11-05 22:34:10 UTC (rev 224472)
@@ -1,7 +1,7 @@
 
-FAIL Setting the start time of an animation without an active timeline assert_equals: Setting the current time succeeds expected (number) 1000 but got (object) null
-FAIL Setting an unresolved start time an animation without an active timeline does not clear the current time assert_equals: Setting the current time succeeds expected (number) 1000 but got (object) null
-FAIL Setting the start time clears the hold time assert_equals: The current time is calculated from the hold time expected (number) 1000 but got (object) null
+FAIL Setting the start time of an animation without an active timeline assert_equals: Start time remains null after setting current time expected (object) null but got (number) -999.896
+FAIL Setting an unresolved start time an animation without an active timeline does not clear the current time assert_equals: Start time remains null after setting current time expected (object) null but got (number) -999.896
+FAIL Setting the start time clears the hold time assert_equals: Animation reports it is running after setting a resolved start time expected (string) "running" but got (undefined) undefined
 FAIL Setting an unresolved start time sets the hold time assert_equals: expected (string) "running" but got (undefined) undefined
 FAIL Setting the start time resolves a pending ready promise undefined is not an object (evaluating 'animation.ready.then')
 FAIL Setting the start time resolves a pending pause task undefined is not an object (evaluating 'animation.ready.then')

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-creation-basic.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-creation-basic.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-creation-basic.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     assert_own_property(window, "Animation");
 }, "The Animation interface is defined.");

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-current-time.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-current-time.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-current-time.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     const animation = new Animation;
     assert_equals(animation.currentTime, null);

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-effect-timing.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-effect-timing.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-effect-timing.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     assert_own_property(window, "AnimationEffectTiming");
 }, "The AnimationEffectTiming interface is defined.");

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-effect.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-effect.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-effect.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     assert_own_property(window, "AnimationEffect");
 }, "The AnimationEffect interface is defined.");

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-interface-effect-property.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-interface-effect-property.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-interface-effect-property.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     const animation = new Animation;
     assert_equals(animation.effect, null);

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-interface-start-time-property.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-interface-start-time-property.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-interface-start-time-property.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     const animation = new Animation;
     assert_equals(animation.startTime, null);

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -9,6 +9,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
   const animation = new Animation;
 

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/document-timeline.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/document-timeline.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/document-timeline.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     assert_own_property(window, "DocumentTimeline");
 }, "The DocumentTimeline interface is defined.");

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect-interface-timing-duration.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect-interface-timing-duration.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect-interface-timing-duration.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     const keyframeEffect = new KeyframeEffect(document.body);
     assert_inherits(keyframeEffect, "timing");

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     assert_own_property(window, "KeyframeEffect");
 }, "The KeyframeEffect interface is defined.");

Modified: trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/timeline-current-time.html (224471 => 224472)


--- trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/timeline-current-time.html	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/LayoutTests/http/wpt/wk-web-animations/timing-model/timeline-current-time.html	2017-11-05 22:34:10 UTC (rev 224472)
@@ -8,6 +8,8 @@
 <script>
 'use strict';
 
+internals.pauseTimeline(document.timeline);
+
 test(t => {
     assert_equals(document.timeline.currentTime, null);
 }, 'The document timeline currentTime is null by default.');

Modified: trunk/Source/WebCore/ChangeLog (224471 => 224472)


--- trunk/Source/WebCore/ChangeLog	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/ChangeLog	2017-11-05 22:34:10 UTC (rev 224472)
@@ -1,3 +1,102 @@
+2017-11-05  Antoine Quint  <grao...@apple.com>
+
+        [Web Animations] Schedule animations registered on the document timeline
+        https://bugs.webkit.org/show_bug.cgi?id=179236
+        <rdar://problem/35332669>
+
+        Reviewed by Dean Jackson.
+
+        We now schedule animations contained in the document timeline using a three-step approach.
+
+        1. Each time an object that is part of the timing model changes one of its timing properties, we call
+           animationTimingModelDidChange() on the document timeline. This schedules performInvalidationTask()
+           to be called when the current run loop completes, such that we invalidate the timing model just once
+           per run loop.
+
+        2. Once performInvalidationTask() is called, the timing model is invalidated in updateAnimationSchedule().
+           We iterate over the registered animations on the timineline and identify the shortest interval between
+           the current time and the next moment one of the animations requires a tick to update its value. If we
+           find a value below 15ms, we schedule animations to be resolved with scheduleAnimationResolution() right
+           away. If the value is above 15ms, and not inifinity, we schedule a one-shot timer for that interval to
+           call scheduleAnimationResolution().
+
+        3. Once scheduleAnimationResolution() is called, we call scheduleAnimation() on the shared DisplayRefreshMonitorManager
+           to be notified when the next display refresh occurs to actually resolve animations with resolveAnimations().
+
+        Note that, in this patch, resolveAnimations() does nothing, we will add support for interpolating values in
+        a future patch.
+
+        Another important thing to note is that every time the document timeline's current time is requested, we cache
+        it for the duration of the run loop such that the timing model always uses the same value during a given run loop.
+
+        Finally, to support tests where we check the state of the timing model by manually advancing time, we expose a
+        new pause() method on AnimationTimeline for tests to call to avoid the timeline to self-advance.
+
+        * animation/AnimationTimeline.cpp:
+        (WebCore::AnimationTimeline::addAnimation): Mark that the timing model changed as a result of adding an animation.
+        (WebCore::AnimationTimeline::removeAnimation): Mark that the timing model changed as a result of removing an animation.
+        (WebCore::AnimationTimeline::bindingsCurrentTime): Update the method signature to no longer be const and call into
+        currentTime() instead of reading directly from the m_currentTime member variable since a subclass, like DocumentTimeline,
+        may have a custom currentTime() implementation.
+        (WebCore::AnimationTimeline::setCurrentTime): Mark that the timing model changed as a result of the timeline current time
+        changing.
+        (WebCore::AnimationTimeline::bindingsCurrentTime const): Deleted.
+        * animation/AnimationTimeline.h:
+        (WebCore::AnimationTimeline::currentTime): Change both methods signatures to no longer be const so that DocumentTimeline's
+        implementation of currentTime() may cache the current time in a member variable, enqueuing a callback when the run loop
+        completes for this member variable to be reset, and updating some states.
+        (WebCore::AnimationTimeline::pause): To be implemented by subclasses.
+        (WebCore::AnimationTimeline::animationTimingModelDidChange): Add a new virtual method to indicate that the timing model
+        needs invalidating.
+        (WebCore::AnimationTimeline::animations const): Add an accessor to allow animations to be accessed by a subclass.
+        * animation/DocumentTimeline.cpp:
+        (WebCore::DocumentTimeline::create):
+        (WebCore::DocumentTimeline::DocumentTimeline): Update the constructor signature to receive a Document and a PlatformDisplayID
+        since we need a reference to the Document to get at the nowTime() and a PlatformDisplayID to create the DisplayRefreshMonitor.
+        (WebCore::DocumentTimeline::~DocumentTimeline): Close the task queue when the timeline gets destroyed.
+        (WebCore::DocumentTimeline::currentTime): If we don't have a current cahed current time, compute one and schedule
+        the invalidation task if needed so that we may reset the cached value as the run loop completes.
+        (WebCore::DocumentTimeline::pause): Allows the timeline not to self-advance, for testing purposes only.
+        (WebCore::DocumentTimeline::animationTimingModelDidChange): If we haven't already done so, mark that we need to update our
+        animation schedule in the invalidation task and schedule that task if not scheduled yet.
+        (WebCore::DocumentTimeline::scheduleInvalidationTaskIfNeeded): Schedule the invalidation task to run as the run loop completes
+        if we haven't already done so.
+        (WebCore::DocumentTimeline::performInvalidationTask): Update the animation schedule if needed and reset the cached current
+        time value.
+        (WebCore::DocumentTimeline::updateAnimationSchedule): Iterate over registed animations and find the shortest interval until
+        one of them needs to update their animation. If the shortest interval is below 15ms, schedule the animation resolution right
+        away. If the shortest inverval is finite and above 15ms, then schedule a one-shot timer for that interval to perform the
+        animation resolution then. 
+        (WebCore::DocumentTimeline::animationScheduleTimerFired): The one-shot timer to perform the animation resolution has fired,
+        we call scheduleAnimationResolution().
+        (WebCore::DocumentTimeline::scheduleAnimationResolution): We call scheduleAnimation() on the shared DisplayRefreshMonitorManager
+        so that we may resolve animations on the next display refresh, or start a timer if the DisplayRefreshMonitorManager is not available.
+        (WebCore::DocumentTimeline::displayRefreshFired): The display is about to refresh, we call resolveAnimations().
+        (WebCore::DocumentTimeline::animationResolutionTimerFired): The fallback animation resolution timer has fired, we call resolveAnimations().
+        (WebCore::DocumentTimeline::resolveAnimations): Currently do nothing, this is where we'll iterate over registered animations to
+        update them with the current time.
+        (WebCore::DocumentTimeline::windowScreenDidChange): Notify the shared DisplayRefreshMonitorManager that the PlatformDisplayID
+        changed.
+        (WebCore::DocumentTimeline::createDisplayRefreshMonitor const): Provide a DisplayRefreshMonitor as part of the
+        DisplayRefreshMonitorClient protocol. 
+        * animation/DocumentTimeline.h:
+        * animation/WebAnimation.cpp:
+        (WebCore::WebAnimation::create): Remove extra white space.
+        (WebCore::WebAnimation::setStartTime): Mark that the timing model changed as a result of changing this animation's start time.
+        (WebCore::WebAnimation::timeToNextRequiredTick const): Compute the interval until the next time we need to resolve this animation.
+        If the provided current time is before this animation's start time, compute the delay until the start time. If the current time
+        is after the animation's start time but before the animation's end time, indicate that we want to resolve the animation again
+        right away and return 0ms. In any other case, return an infinite interval to indicate that we don't need to be refreshed after
+        the provided time.
+        * animation/WebAnimation.h:
+        * dom/Document.cpp:
+        (WebCore::Document::windowScreenDidChange): Notify the document timeline that the PlatformDisplayID changed.
+        (WebCore::Document::timeline): Provide the Document and the PlatformDisplayID to the DocumentTimeline.
+        * testing/Internals.cpp:
+        (WebCore::Internals::pauseTimeline):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2017-11-05  Chris Dumez  <cdu...@apple.com>
 
         Implement ServiceWorkerRegistration.update()

Modified: trunk/Source/WebCore/animation/AnimationTimeline.cpp (224471 => 224472)


--- trunk/Source/WebCore/animation/AnimationTimeline.cpp	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/animation/AnimationTimeline.cpp	2017-11-05 22:34:10 UTC (rev 224472)
@@ -45,23 +45,27 @@
 void AnimationTimeline::addAnimation(Ref<WebAnimation>&& animation)
 {
     m_animations.add(WTFMove(animation));
+    animationTimingModelDidChange();
 }
 
 void AnimationTimeline::removeAnimation(Ref<WebAnimation>&& animation)
 {
     m_animations.remove(WTFMove(animation));
+    animationTimingModelDidChange();
 }
 
-std::optional<double> AnimationTimeline::bindingsCurrentTime() const
+std::optional<double> AnimationTimeline::bindingsCurrentTime()
 {
-    if (!m_currentTime)
+    auto time = currentTime();
+    if (!time)
         return std::nullopt;
-    return m_currentTime->value();
+    return time->value();
 }
 
 void AnimationTimeline::setCurrentTime(Seconds currentTime)
 {
     m_currentTime = currentTime;
+    animationTimingModelDidChange();
 }
 
 String AnimationTimeline::description()

Modified: trunk/Source/WebCore/animation/AnimationTimeline.h (224471 => 224472)


--- trunk/Source/WebCore/animation/AnimationTimeline.h	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/animation/AnimationTimeline.h	2017-11-05 22:34:10 UTC (rev 224472)
@@ -43,11 +43,14 @@
     bool isDocumentTimeline() const { return m_classType == DocumentTimelineClass; }
     void addAnimation(Ref<WebAnimation>&&);
     void removeAnimation(Ref<WebAnimation>&&);
-    std::optional<double> bindingsCurrentTime() const;
-    std::optional<Seconds> currentTime() const { return m_currentTime; }
+    std::optional<double> bindingsCurrentTime();
+    virtual std::optional<Seconds> currentTime() { return m_currentTime; }
     WEBCORE_EXPORT void setCurrentTime(Seconds);
     WEBCORE_EXPORT String description();
+    WEBCORE_EXPORT virtual void pause() { };
 
+    virtual void animationTimingModelDidChange() { };
+
     virtual ~AnimationTimeline();
 
 protected:
@@ -57,6 +60,8 @@
 
     ClassType classType() const { return m_classType; }
 
+    HashSet<RefPtr<WebAnimation>> animations() const { return m_animations; }
+
     explicit AnimationTimeline(ClassType);
 
 private:

Modified: trunk/Source/WebCore/animation/DocumentTimeline.cpp (224471 => 224472)


--- trunk/Source/WebCore/animation/DocumentTimeline.cpp	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/animation/DocumentTimeline.cpp	2017-11-05 22:34:10 UTC (rev 224472)
@@ -26,16 +26,158 @@
 #include "config.h"
 #include "DocumentTimeline.h"
 
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "DOMWindow.h"
+#include "DisplayRefreshMonitor.h"
+#include "DisplayRefreshMonitorManager.h"
+#include "Document.h"
+#include "Page.h"
+
+static const Seconds animationInterval { 15_ms };
+
 namespace WebCore {
 
-Ref<DocumentTimeline> DocumentTimeline::create()
+Ref<DocumentTimeline> DocumentTimeline::create(Document& document, PlatformDisplayID displayID)
 {
-    return adoptRef(*new DocumentTimeline());
+    return adoptRef(*new DocumentTimeline(document, displayID));
 }
 
-DocumentTimeline::DocumentTimeline()
+DocumentTimeline::DocumentTimeline(Document& document, PlatformDisplayID displayID)
     : AnimationTimeline(DocumentTimelineClass)
+    , m_document(document)
+    , m_animationScheduleTimer(*this, &DocumentTimeline::animationScheduleTimerFired)
+#if !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+    , m_animationResolutionTimer(*this, &DocumentTimeline::animationResolutionTimerFired)
+#endif
 {
+    windowScreenDidChange(displayID);
 }
 
+DocumentTimeline::~DocumentTimeline()
+{
+    m_invalidationTaskQueue.close();
+}
+
+std::optional<Seconds> DocumentTimeline::currentTime()
+{
+    if (m_paused)
+        return AnimationTimeline::currentTime();
+
+    if (!m_cachedCurrentTime) {
+        m_cachedCurrentTime = Seconds(m_document->domWindow()->nowTimestamp());
+        scheduleInvalidationTaskIfNeeded();
+    }
+    return m_cachedCurrentTime;
+}
+
+void DocumentTimeline::pause()
+{
+    m_paused = true;
+}
+
+void DocumentTimeline::animationTimingModelDidChange()
+{
+    if (m_needsUpdateAnimationSchedule)
+        return;
+
+    m_needsUpdateAnimationSchedule = true;
+
+    // We know that we will resolve animations again, so we can cancel the timer right away.
+    if (m_animationScheduleTimer.isActive())
+        m_animationScheduleTimer.stop();
+
+    scheduleInvalidationTaskIfNeeded();
+}
+
+void DocumentTimeline::scheduleInvalidationTaskIfNeeded()
+{
+    if (m_invalidationTaskQueue.hasPendingTasks())
+        return;
+
+    m_invalidationTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performInvalidationTask, this));
+}
+
+void DocumentTimeline::performInvalidationTask()
+{
+    updateAnimationSchedule();
+    m_cachedCurrentTime = std::nullopt;
+}
+
+void DocumentTimeline::updateAnimationSchedule()
+{
+    if (!m_needsUpdateAnimationSchedule)
+        return;
+
+    m_needsUpdateAnimationSchedule = false;
+
+    Seconds now = currentTime().value();
+    Seconds scheduleDelay = Seconds::infinity();
+
+    for (const auto& animation : animations()) {
+        auto animationTimeToNextRequiredTick = animation->timeToNextRequiredTick(now);
+        if (animationTimeToNextRequiredTick < animationInterval) {
+            scheduleAnimationResolution();
+            return;
+        }
+        scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
+    }
+
+    if (scheduleDelay < Seconds::infinity())
+        m_animationScheduleTimer.startOneShot(scheduleDelay);
+}
+
+void DocumentTimeline::animationScheduleTimerFired()
+{
+    scheduleAnimationResolution();
+}
+
+void DocumentTimeline::scheduleAnimationResolution()
+{
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+    DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
+#else
+    // FIXME: We need to use the same logic as ScriptedAnimationController here,
+    // which will be addressed by the refactor tracked by webkit.org/b/179293.
+    m_animationResolutionTimer.startOneShot(animationInterval);
+#endif
+}
+
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+void DocumentTimeline::displayRefreshFired()
+#else
+void DocumentTimeline::animationResolutionTimerFired()
+#endif
+{
+    resolveAnimations();
+}
+
+void DocumentTimeline::resolveAnimations()
+{
+    // Time has advanced, the timing model requires invalidation now.
+    animationTimingModelDidChange();
+}
+
+void DocumentTimeline::windowScreenDidChange(PlatformDisplayID displayID)
+{
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+    DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
+#else
+    UNUSED_PARAM(displayID);
+#endif
+}
+
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+RefPtr<DisplayRefreshMonitor> DocumentTimeline::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
+{
+    if (!m_document->page())
+        return nullptr;
+
+    if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
+        return monitor;
+
+    return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
+}
+#endif
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/animation/DocumentTimeline.h (224471 => 224472)


--- trunk/Source/WebCore/animation/DocumentTimeline.h	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/animation/DocumentTimeline.h	2017-11-05 22:34:10 UTC (rev 224472)
@@ -26,18 +26,57 @@
 #pragma once
 
 #include "AnimationTimeline.h"
+#include "GenericTaskQueue.h"
+#include "PlatformScreen.h"
+#include "Timer.h"
 #include <wtf/Ref.h>
 
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+#include "DisplayRefreshMonitorClient.h"
+#endif
+
 namespace WebCore {
 
-class DocumentTimeline final : public AnimationTimeline {
+class DocumentTimeline final : public AnimationTimeline
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+    , public DisplayRefreshMonitorClient
+#endif
+{
 public:
-    static Ref<DocumentTimeline> create();
-    ~DocumentTimeline() { }
+    static Ref<DocumentTimeline> create(Document&, PlatformDisplayID);
+    ~DocumentTimeline();
 
+    std::optional<Seconds> currentTime() override;
+    void pause() override;
+
+    void animationTimingModelDidChange() override;
+    void windowScreenDidChange(PlatformDisplayID);
+
 private:
-    DocumentTimeline();
+    DocumentTimeline(Document&, PlatformDisplayID);
 
+    void scheduleInvalidationTaskIfNeeded();
+    void performInvalidationTask();
+    void updateAnimationSchedule();
+    void animationScheduleTimerFired();
+    void scheduleAnimationResolution();
+    void resolveAnimations();
+
+    Ref<Document> m_document;
+    bool m_paused { false };
+    std::optional<Seconds> m_cachedCurrentTime;
+    GenericTaskQueue<Timer> m_invalidationTaskQueue;
+    bool m_needsUpdateAnimationSchedule { false };
+    Timer m_animationScheduleTimer;
+
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+    // Override for DisplayRefreshMonitorClient
+    void displayRefreshFired() override;
+    RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const override;
+#else
+    void animationResolutionTimerFired();
+    Timer m_animationResolutionTimer;
+#endif
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/animation/WebAnimation.cpp (224471 => 224472)


--- trunk/Source/WebCore/animation/WebAnimation.cpp	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/animation/WebAnimation.cpp	2017-11-05 22:34:10 UTC (rev 224472)
@@ -42,7 +42,7 @@
     // FIXME: the spec mandates distinguishing between an omitted timeline parameter
     // and an explicit null or undefined value (webkit.org/b/179065).
     result->setTimeline(timeline ? timeline : &document.timeline());
-    
+
     return result;
 }
 
@@ -107,6 +107,9 @@
         return;
 
     m_startTime = startTime;
+    
+    if (m_timeline)
+        m_timeline->animationTimingModelDidChange();
 }
 
 std::optional<double> WebAnimation::bindingsCurrentTime() const
@@ -172,6 +175,29 @@
         setCurrentTime(previousTime);
 }
 
+Seconds WebAnimation::timeToNextRequiredTick(Seconds timelineTime) const
+{
+    if (!m_timeline || !m_startTime || !m_effect || !m_playbackRate)
+        return Seconds::infinity();
+
+    auto startTime = m_startTime.value();
+    auto endTime = startTime + (m_effect->timing()->duration() / m_playbackRate);
+
+    // If we haven't started yet, return the interval until our active start time.
+    auto activeStartTime = std::min(startTime, endTime);
+    if (timelineTime <= activeStartTime)
+        return activeStartTime - timelineTime;
+
+    // If we're in the middle of our active duration, we want to be called as soon as possible.
+    auto activeEndTime = std::max(startTime, endTime);
+    if (timelineTime <= activeEndTime)
+        return 0_ms;
+
+    // If none of the previous cases match, then we're already past our active duration
+    // and do not need scheduling.
+    return Seconds::infinity();
+}
+
 String WebAnimation::description()
 {
     return "Animation";

Modified: trunk/Source/WebCore/animation/WebAnimation.h (224471 => 224472)


--- trunk/Source/WebCore/animation/WebAnimation.h	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/animation/WebAnimation.h	2017-11-05 22:34:10 UTC (rev 224472)
@@ -62,6 +62,8 @@
     double playbackRate() const { return m_playbackRate; }
     void setPlaybackRate(double);
 
+    Seconds timeToNextRequiredTick(Seconds) const;
+
     String description();
 
 private:

Modified: trunk/Source/WebCore/dom/Document.cpp (224471 => 224472)


--- trunk/Source/WebCore/dom/Document.cpp	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/dom/Document.cpp	2017-11-05 22:34:10 UTC (rev 224472)
@@ -5815,6 +5815,9 @@
     if (m_scriptedAnimationController)
         m_scriptedAnimationController->windowScreenDidChange(displayID);
 
+    if (m_timeline)
+        m_timeline->windowScreenDidChange(displayID);
+
     if (RenderView* view = renderView()) {
         if (view->usesCompositing())
             view->compositor().windowScreenDidChange(displayID);
@@ -7451,7 +7454,8 @@
 DocumentTimeline& Document::timeline()
 {
     if (!m_timeline)
-        m_timeline = DocumentTimeline::create();
+        m_timeline = DocumentTimeline::create(*this, page() ? page()->chrome().displayID() : 0);
+
     return *m_timeline;
 }
 

Modified: trunk/Source/WebCore/testing/Internals.cpp (224471 => 224472)


--- trunk/Source/WebCore/testing/Internals.cpp	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/testing/Internals.cpp	2017-11-05 22:34:10 UTC (rev 224472)
@@ -4284,6 +4284,11 @@
     return timeline.description();
 }
 
+void Internals::pauseTimeline(AnimationTimeline& timeline)
+{
+    timeline.pause();
+}
+
 void Internals::setTimelineCurrentTime(AnimationTimeline& timeline, double currentTime)
 {
     timeline.setCurrentTime(Seconds(currentTime));

Modified: trunk/Source/WebCore/testing/Internals.h (224471 => 224472)


--- trunk/Source/WebCore/testing/Internals.h	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/testing/Internals.h	2017-11-05 22:34:10 UTC (rev 224472)
@@ -628,6 +628,7 @@
 #endif
 
     String timelineDescription(AnimationTimeline&);
+    void pauseTimeline(AnimationTimeline&);
     void setTimelineCurrentTime(AnimationTimeline&, double);
 
 private:

Modified: trunk/Source/WebCore/testing/Internals.idl (224471 => 224472)


--- trunk/Source/WebCore/testing/Internals.idl	2017-11-05 22:17:04 UTC (rev 224471)
+++ trunk/Source/WebCore/testing/Internals.idl	2017-11-05 22:34:10 UTC (rev 224472)
@@ -567,6 +567,7 @@
     boolean hasServiceWorkerRegisteredForOrigin(DOMString origin);
 
     [EnabledAtRuntime=WebAnimations] DOMString timelineDescription(AnimationTimeline timeline);
+    [EnabledAtRuntime=WebAnimations] void pauseTimeline(AnimationTimeline timeline);
     [EnabledAtRuntime=WebAnimations] void setTimelineCurrentTime(AnimationTimeline timeline, double currentTime);
     [Conditional=APPLE_PAY] readonly attribute MockPaymentCoordinator mockPaymentCoordinator;
 };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to