⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 237587 in webkit


Ignore:
Timestamp:
Oct 30, 2018, 2:30:03 AM (8 years ago)
Author:
graouts@webkit.org
Message:

[Web Animations] Implement the update animations and send events procedure
https://bugs.webkit.org/show_bug.cgi?id=191013
<rdar://problem/45620495>

Reviewed by Dean Jackson.

LayoutTests/imported/mozilla:

Progression in a couple of getAnimations() tests for CSS Animations.

  • css-animations/test_document-get-animations-expected.txt:

LayoutTests/imported/w3c:

Progressions in a couple of Web Animations Web Platform Tests.

  • web-platform-tests/web-animations/timing-model/animations/current-time-expected.txt:
  • web-platform-tests/web-animations/timing-model/animations/updating-the-finished-state-expected.txt:

Source/WebCore:

While we implemented the various parts of what the Web Animations specification refers to as the "update animations and send events"
procedure, we did not implement it as one function and in the correct order, specifically updating animations and sending events in
two separate tasks. We now have a single method on DocumentTimeline which runs as the DisplayRefreshMonitor fires to update each
"relevant" animation with the current time, perform a microtask checkpoint and dispatch events.

Implementing this procedure allowed us to make several enhancements:

  1. We introduce the concept of a "relevant" animation, which is essentially an animation that is either pending or playing. All animations

in a different state are no longer owned by the DocumentTimeline and can thus be destroyed if the developer doesn't hold references in JS.
Maintaining such a list guarantees that we're only updating animations that would have changed state since the last time the "update animations
and send events" procedure was run. Note that DeclarativeAnimation instances are also considered to be relevant if they have queued DOM events
to dispatch as they could otherwise be destroyed before they can fully dispatch them.

  1. We no longer conflate the timing model and effects. Until now the way we would update animations was to go through all elements for which

we had a registered animation, invalidate their style and finally forcing a style update on the document. We had a separate data structure where
we help animations without targets so we update these as well in a separate pass, in order to make sure that promises and events would fire for
them as expected. We now let the "update animations and send events" procedure update the timing of all relevant animations and let individual
animation effects invalidate their style as needed, the document style invalidation happening naturally without DocumentTimeline forcing it.

  1. We use a single step to schedule the update of animations, which is to register for a display refresh monitor update provided a "relevant"

animation is known since the previous update. Until now we first had an "timing model invalidation" task scheduled upon any change of an animation's
timing model, which would then create a timer to the earliest moment any listed animation would require an update, finally registering a display
refresh monitor update, which used at least GenericTaskQueue<Timer> and potentially two, whereas we use none right now.

  1. We allow for a display refresh monitor update to be canceled should the number of "relevant" animations since the last update goes back to 0.

To facilitate all of this, we have changed the m_animations ListHashSet to contain only the "relevant" animations, and no longer every animation created
that has this DocumentTimeline set as their "timeline" property. To keep this list current, every single change that changes a given animation's timing
ends up calling AnimationTimeline::animationTimingDidChange() passing the animation as the sole parameter and adding this animation to m_animations. We
immediately schedule a display refresh monitor update if one wasn't already scheduled. Then, when running the "update animations and send events"
procedure, we call a new WebAnimation::tick() method on each of those animations, which updates this animation's effect and relevance, using the newly
computed relevance to identify whether this animation should be kept in the m_animations ListHashSet.

This is only the first step towards a more efficient update and ownership model of animations by the document timeline since animations created as CSS
Animations and CSS Transitions are committed through CSS have dedicated data structures that are not updated in this particular patch, but this will be
addressed in a followup to keep this already significant patch smaller. Another issue that will be addressed later is the ability to not schedule display
refresh monitor udpates when only accelerated animations are running.

  • animation/AnimationTimeline.cpp:

(WebCore::AnimationTimeline::animationTimingDidChange): Called by animations when any aspect of their timing model changes. The provided animation is then
added to the m_animations list unless its timeline is no longer this timeline.
(WebCore::AnimationTimeline::removeAnimation): Remove the provided animation from m_animations and remove any animation registered on the element-specific
animation lists if this animation has an effect with a target.
(WebCore::AnimationTimeline::animationWasAddedToElement): We no longer need to worry about the m_animationsWithoutTarget data structure since we removed it.
(WebCore::removeCSSTransitionFromMap): Fix a bug where we would remove any CSSTransition in the provided map that had a matching transition-property instead
of checking the CSSTransition registered for this transition-property was indeed the provided CSSTransition. The other code changes in this patch made this
code now cause regressions in the Web Platform Tests.
(WebCore::AnimationTimeline::animationWasRemovedFromElement): Stop updating m_animationsWithoutTarget since it no longer exists.
(WebCore::AnimationTimeline::elementWasRemoved):
(WebCore::AnimationTimeline::updateCSSAnimationsForElement): Fix a small error that caused a regression in the Web Platform Tests where we could attempt to
call setBackingAnimation() on a nullptr instead of a valid CSSAnimation.
(WebCore::AnimationTimeline::cancelOrRemoveDeclarativeAnimation):
(WebCore::AnimationTimeline::addAnimation): Deleted.

  • animation/AnimationTimeline.h:

(WebCore::AnimationTimeline::hasElementAnimations const): Deleted.
(WebCore::AnimationTimeline:: const): Deleted.
(WebCore::AnimationTimeline::elementToAnimationsMap): Deleted.
(WebCore::AnimationTimeline::elementToCSSAnimationsMap): Deleted.
(WebCore::AnimationTimeline::elementToCSSTransitionsMap): Deleted.

  • animation/CSSTransition.cpp:

(WebCore::CSSTransition::canBeListed const): Deleted.

  • animation/CSSTransition.h:
  • animation/DeclarativeAnimation.cpp:

(WebCore::DeclarativeAnimation::tick): Call the superclass's method and queue any necessary DOM events reflecting the timing model changes.
(WebCore::DeclarativeAnimation::needsTick const): Call the superclass's method and return true also if we have pending events since otherwise this animation
could be removed from m_animations on its AnimationTimeline and potentially destroyed before the GenericEventQueue had a chance to dispatch all events.
(WebCore::DeclarativeAnimation::startTime const): We removed the custom binding for this IDL property and renamed the method from bindingsStartTime to startTime.
(WebCore::DeclarativeAnimation::setStartTime): We removed the custom binding for this IDL property and renamed the method from setBindingsStartTime to setStartTime.
(WebCore::DeclarativeAnimation::bindingsStartTime const): Deleted.
(WebCore::DeclarativeAnimation::setBindingsStartTime): Deleted.

  • animation/DeclarativeAnimation.h:
  • animation/DocumentAnimationScheduler.cpp:

(WebCore::DocumentAnimationScheduler::unscheduleWebAnimationsResolution): Add a method to mark that we no longer need a display refresh monitor update for this
document's animation timeline. This is called when m_animations becomes empty.

  • animation/DocumentAnimationScheduler.h:
  • animation/DocumentTimeline.cpp:

(WebCore::DocumentTimeline::DocumentTimeline):
(WebCore::DocumentTimeline::detachFromDocument): Stop clearing two task queues and a timer that no longer exist and instead only clear the task queue to clear
the cached current time, which we queue any time we generate a new one (see DocumentTimeline::currentTime).
(WebCore::DocumentTimeline::getAnimations const): Use isRelevant() instead of canBeListed().
(WebCore::DocumentTimeline::updateThrottlingState):
(WebCore::DocumentTimeline::suspendAnimations):
(WebCore::DocumentTimeline::resumeAnimations):
(WebCore::DocumentTimeline::numberOfActiveAnimationsForTesting const):
(WebCore::DocumentTimeline::currentTime): Queue a task in the new m_currentTimeClearingTaskQueue task queue to clear the current time that we've generated and cached
in the next run loop (provided all pending JS execution has also completed).
(WebCore::DocumentTimeline::maybeClearCachedCurrentTime):
(WebCore::DocumentTimeline::scheduleAnimationResolutionIfNeeded): Schedule a display refresh monitor update if we are not suspended and have "relevant" animations.
(WebCore::DocumentTimeline::animationTimingDidChange): Call scheduleAnimationResolutionIfNeeded() after calling the superclass's implementation.
(WebCore::DocumentTimeline::removeAnimation): Call unscheduleAnimationResolution() if the list of "relevant" animations is now empty.
(WebCore::DocumentTimeline::unscheduleAnimationResolution): Unschedule a pending display refresh monitor update.
(WebCore::DocumentTimeline::animationResolutionTimerFired):
(WebCore::DocumentTimeline::updateAnimationsAndSendEvents): Implement the "update animations and send events" procedure as specified by the Web Animations spec.
During this procedure, we call tick() on all animations listed in m_animations and create a list of animations to remove from that list if this animation is no
longer relevant following the call to tick().
(WebCore::DocumentTimeline::enqueueAnimationPlaybackEvent):
(WebCore::DocumentTimeline::timingModelDidChange): Deleted.
(WebCore::DocumentTimeline::scheduleInvalidationTaskIfNeeded): Deleted.
(WebCore::DocumentTimeline::performInvalidationTask): Deleted.
(WebCore::DocumentTimeline::updateAnimationSchedule): Deleted.
(WebCore::DocumentTimeline::animationScheduleTimerFired): Deleted.
(WebCore::DocumentTimeline::updateAnimations): Deleted.
(WebCore::compareAnimationPlaybackEvents): Deleted.
(WebCore::DocumentTimeline::performEventDispatchTask): Deleted.

  • animation/DocumentTimeline.h:
  • animation/WebAnimation.cpp: The majority of the changes to this class is that we call the new timingDidChange() method when any code that modifies the timing model

is run. We also remove methods to set the pending play and pause tasks as well as the animation's start time and hold time since any time we're changing these instance
variables, we later already have a call to update the timing model and we were doing more work than needed. As a result we no longer need an internal method to set the
start time and can stop requiring a custom IDL binding for the "startTime" property.
(WebCore::WebAnimation::effectTimingPropertiesDidChange):
(WebCore::WebAnimation::setEffect):
(WebCore::WebAnimation::setEffectInternal):
(WebCore::WebAnimation::setTimeline):
(WebCore::WebAnimation::setTimelineInternal):
(WebCore::WebAnimation::startTime const):
(WebCore::WebAnimation::setStartTime):
(WebCore::WebAnimation::silentlySetCurrentTime):
(WebCore::WebAnimation::setCurrentTime):
(WebCore::WebAnimation::setPlaybackRate):
(WebCore::WebAnimation::cancel):
(WebCore::WebAnimation::resetPendingTasks):
(WebCore::WebAnimation::finish):
(WebCore::WebAnimation::timingDidChange): New method called any time a timing property changed where we run the "update the finished state" procedure and notify the
animation's timeline that its timing changed so that it can be considered the next time the "update animations and send events" procedure runs.
(WebCore::WebAnimation::invalidateEffect):
(WebCore::WebAnimation::updateFinishedState): Update the animation's relevance after running the procedure as specified.
(WebCore::WebAnimation::play):
(WebCore::WebAnimation::runPendingPlayTask):
(WebCore::WebAnimation::pause):
(WebCore::WebAnimation::runPendingPauseTask):
(WebCore::WebAnimation::needsTick const):
(WebCore::WebAnimation::tick): New method called during the "update animations and send events" procedure where we run the "update the finished state" procedure and run
the pending play and pause tasks.
(WebCore::WebAnimation::resolve):
(WebCore::WebAnimation::updateRelevance):
(WebCore::WebAnimation::computeRelevance):
(WebCore::WebAnimation::timingModelDidChange): Deleted.
(WebCore::WebAnimation::setHoldTime): Deleted.
(WebCore::WebAnimation::bindingsStartTime const): Deleted.
(WebCore::WebAnimation::setBindingsStartTime): Deleted.
(WebCore::WebAnimation::setTimeToRunPendingPlayTask): Deleted.
(WebCore::WebAnimation::setTimeToRunPendingPauseTask): Deleted.
(WebCore::WebAnimation::updatePendingTasks): Deleted.
(WebCore::WebAnimation::timeToNextRequiredTick const): Deleted.
(WebCore::WebAnimation::runPendingTasks): Deleted.
(WebCore::WebAnimation::canBeListed const): Deleted.

  • animation/WebAnimation.h:

(WebCore::WebAnimation::isRelevant const):
(WebCore::WebAnimation::hasPendingPlayTask const):
(WebCore::WebAnimation::isEffectInvalidationSuspended):

  • animation/WebAnimation.idl:
  • dom/Element.cpp:

(WebCore::Element::getAnimations): Use isRelevant() instead of canBeListed().

LayoutTests:

Several tests that broke when turning Web Animations CSS Integration on by default are now passing. In the case of one test, we had to ensure
that the final animation frame had been committed before terminating the test or there would be a tiny image reference issue.

  • TestExpectations:
  • fast/layers/no-clipping-overflow-hidden-added-after-transform.html:
Location:
trunk
Files:
23 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r237585 r237587  
     12018-10-28  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Implement the update animations and send events procedure
     4        https://bugs.webkit.org/show_bug.cgi?id=191013
     5        <rdar://problem/45620495>
     6
     7        Reviewed by Dean Jackson.
     8
     9        Several tests that broke when turning Web Animations CSS Integration on by default are now passing. In the case of one test, we had to ensure
     10        that the final animation frame had been committed before terminating the test or there would be a tiny image reference issue.
     11
     12        * TestExpectations:
     13        * fast/layers/no-clipping-overflow-hidden-added-after-transform.html:
     14
    1152018-10-30  Youenn Fablet  <youenn@apple.com>
    216
  • trunk/LayoutTests/TestExpectations

    r237568 r237587  
    28982898webkit.org/b/190032 compositing/layer-creation/translate-scale-transition-overlap.html [ Failure ]
    28992899webkit.org/b/190032 compositing/layer-creation/translate-transition-overlap.html [ Failure ]
    2900 webkit.org/b/190032 compositing/visible-rect/animated-from-none.html [ Failure ]
    2901 webkit.org/b/190032 fast/animation/css-animation-resuming-when-visible-with-style-change2.html [ Failure ]
    29022900webkit.org/b/190032 imported/w3c/web-platform-tests/css/css-logical/animation-003.tentative.html [ Failure ]
    29032901webkit.org/b/190032 imported/w3c/web-platform-tests/css/css-scoping/keyframes-001.html [ Failure ]
    2904 webkit.org/b/190032 imported/w3c/web-platform-tests/web-animations/animation-model/keyframe-effects/effect-value-context.html [ Failure ]
    2905 webkit.org/b/190032 imported/w3c/web-platform-tests/web-animations/interfaces/Animatable/animate.html [ Failure ]
    2906 webkit.org/b/190032 imported/w3c/web-platform-tests/web-animations/timing-model/animations/current-time.html [ Failure ]
    29072902
    29082903# FIXME: Need to implement MediaRecorder dataavailable event to support these testcases
  • trunk/LayoutTests/fast/layers/no-clipping-overflow-hidden-added-after-transform.html

    r171226 r237587  
    3333    function transitionFinished() {
    3434        if (window.testRunner)
    35             window.testRunner.notifyDone();
     35            requestAnimationFrame(() => window.testRunner.notifyDone());
    3636    }
    3737
  • trunk/LayoutTests/imported/mozilla/ChangeLog

    r237475 r237587  
     12018-10-28  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Implement the update animations and send events procedure
     4        https://bugs.webkit.org/show_bug.cgi?id=191013
     5        <rdar://problem/45620495>
     6
     7        Reviewed by Dean Jackson.
     8
     9        Progression in a couple of getAnimations() tests for CSS Animations.
     10
     11        * css-animations/test_document-get-animations-expected.txt:
     12
    1132018-10-26  Antoine Quint  <graouts@apple.com>
    214
  • trunk/LayoutTests/imported/mozilla/css-animations/test_document-get-animations-expected.txt

    r237475 r237587  
    55FAIL Order of CSS Animations - across elements assert_equals: Order of second animation returned after tree surgery expected Element node <div style="animation: animLeft 100s"></div> but got Element node <div style="animation: animLeft 100s"></div>
    66PASS Order of CSS Animations - across and within elements
    7 FAIL Order of CSS Animations - markup-bound vs free animations assert_equals: getAnimations returns markup-bound and free animations expected 2 but got 1
    8 FAIL Order of CSS Animations - free animations assert_equals: getAnimations returns free animations expected 2 but got 0
     7PASS Order of CSS Animations - markup-bound vs free animations
     8PASS Order of CSS Animations - free animations
    99FAIL Order of CSS Animations and CSS Transitions assert_equals: Transition comes first expected "[object CSSTransition]" but got "[object CSSAnimation]"
    1010PASS Finished but filling CSS Animations are returned
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r237570 r237587  
     12018-10-28  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Implement the update animations and send events procedure
     4        https://bugs.webkit.org/show_bug.cgi?id=191013
     5        <rdar://problem/45620495>
     6
     7        Reviewed by Dean Jackson.
     8
     9        Progressions in a couple of Web Animations Web Platform Tests.
     10
     11        * web-platform-tests/web-animations/timing-model/animations/current-time-expected.txt:
     12        * web-platform-tests/web-animations/timing-model/animations/updating-the-finished-state-expected.txt:
     13
    1142018-10-29  Justin Michaud  <justin_michaud@apple.com>
    215
  • trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/timing-model/animations/current-time-expected.txt

    r237475 r237587  
    1 
    2 Harness Error (TIMEOUT), message = null
    31
    42PASS The current time returns the hold time when set
     
    64PASS The current time is unresolved when the start time is unresolved (and no hold time is set)
    75PASS The current time is calculated from the timeline time, start time and playback rate
    8 TIMEOUT The current time does not progress if playback rate is 0 Test timed out
     6PASS The current time does not progress if playback rate is 0
    97
  • trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/timing-model/animations/updating-the-finished-state-expected.txt

    r237475 r237587  
    1111PASS Updating the finished state when seeking before end
    1212PASS Updating the finished state when seeking a reversed animation before end
    13 TIMEOUT Updating the finished state when playback rate is zero and the current time is less than zero Test timed out
    14 NOTRUN Updating the finished state when playback rate is zero and the current time is less than end
    15 NOTRUN Updating the finished state when playback rate is zero and the current time is greater than end
    16 NOTRUN Updating the finished state when current time is unresolved
     13PASS Updating the finished state when playback rate is zero and the current time is less than zero
     14PASS Updating the finished state when playback rate is zero and the current time is less than end
     15PASS Updating the finished state when playback rate is zero and the current time is greater than end
     16PASS Updating the finished state when current time is unresolved
    1717PASS Updating the finished state when there is a pending task
    18 NOTRUN Updating the finished state when start time is unresolved and did seek = false
     18PASS Updating the finished state when start time is unresolved and did seek = false
    1919PASS Updating the finished state when start time is unresolved and did seek = true
    20 NOTRUN Finish notification steps don't run when the animation seeks to finish and then seeks back again
    21 NOTRUN Finish notification steps run when the animation completes normally
    22 NOTRUN Finish notification steps run when the animation seeks past finish
    23 NOTRUN Finish notification steps run when the animation completes with .finish(), even if we then seek away
    24 NOTRUN Animation finished promise is replaced after seeking back to start
    25 NOTRUN Animation finished promise is replaced after replaying from start
    26 PASS Animation finish event is fired again after seeking back to start
    27 PASS Animation finish event is fired again after replaying from start
     20PASS Finish notification steps don't run when the animation seeks to finish and then seeks back again
     21PASS Finish notification steps run when the animation completes normally
     22PASS Finish notification steps run when the animation seeks past finish
     23PASS Finish notification steps run when the animation completes with .finish(), even if we then seek away
     24PASS Animation finished promise is replaced after seeking back to start
     25PASS Animation finished promise is replaced after replaying from start
     26TIMEOUT Animation finish event is fired again after seeking back to start Test timed out
     27TIMEOUT Animation finish event is fired again after replaying from start Test timed out
    2828
  • trunk/Source/WebCore/ChangeLog

    r237585 r237587  
     12018-10-28  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Implement the update animations and send events procedure
     4        https://bugs.webkit.org/show_bug.cgi?id=191013
     5        <rdar://problem/45620495>
     6
     7        Reviewed by Dean Jackson.
     8
     9        While we implemented the various parts of what the Web Animations specification refers to as the "update animations and send events"
     10        procedure, we did not implement it as one function and in the correct order, specifically updating animations and sending events in
     11        two separate tasks. We now have a single method on DocumentTimeline which runs as the DisplayRefreshMonitor fires to update each
     12        "relevant" animation with the current time, perform a microtask checkpoint and dispatch events.
     13
     14        Implementing this procedure allowed us to make several enhancements:
     15
     16        1. We introduce the concept of a "relevant" animation, which is essentially an animation that is either pending or playing. All animations
     17        in a different state are no longer owned by the DocumentTimeline and can thus be destroyed if the developer doesn't hold references in JS.
     18        Maintaining such a list guarantees that we're only updating animations that would have changed state since the last time the "update animations
     19        and send events" procedure was run. Note that DeclarativeAnimation instances are also considered to be relevant if they have queued DOM events
     20        to dispatch as they could otherwise be destroyed before they can fully dispatch them.
     21
     22        2. We no longer conflate the timing model and effects. Until now the way we would update animations was to go through all elements for which
     23        we had a registered animation, invalidate their style and finally forcing a style update on the document. We had a separate data structure where
     24        we help animations without targets so we update these as well in a separate pass, in order to make sure that promises and events would fire for
     25        them as expected. We now let the "update animations and send events" procedure update the timing of all relevant animations and let individual
     26        animation effects invalidate their style as needed, the document style invalidation happening naturally without DocumentTimeline forcing it.
     27
     28        3. We use a single step to schedule the update of animations, which is to register for a display refresh monitor update provided a "relevant"
     29        animation is known since the previous update. Until now we first had an "timing model invalidation" task scheduled upon any change of an animation's
     30        timing model, which would then create a timer to the earliest moment any listed animation would require an update, finally registering a display
     31        refresh monitor update, which used at least GenericTaskQueue<Timer> and potentially two, whereas we use none right now.
     32
     33        4. We allow for a display refresh monitor update to be canceled should the number of "relevant" animations since the last update goes back to 0.
     34
     35        To facilitate all of this, we have changed the m_animations ListHashSet to contain only the "relevant" animations, and no longer every animation created
     36        that has this DocumentTimeline set as their "timeline" property. To keep this list current, every single change that changes a given animation's timing
     37        ends up calling AnimationTimeline::animationTimingDidChange() passing the animation as the sole parameter and adding this animation to m_animations. We
     38        immediately schedule a display refresh monitor update if one wasn't already scheduled. Then, when running the "update animations and send events"
     39        procedure, we call a new WebAnimation::tick() method on each of those animations, which updates this animation's effect and relevance, using the newly
     40        computed relevance to identify whether this animation should be kept in the m_animations ListHashSet.
     41
     42        This is only the first step towards a more efficient update and ownership model of animations by the document timeline since animations created as CSS
     43        Animations and CSS Transitions are committed through CSS have dedicated data structures that are not updated in this particular patch, but this will be
     44        addressed in a followup to keep this already significant patch smaller. Another issue that will be addressed later is the ability to not schedule display
     45        refresh monitor udpates when only accelerated animations are running.
     46
     47        * animation/AnimationTimeline.cpp:
     48        (WebCore::AnimationTimeline::animationTimingDidChange): Called by animations when any aspect of their timing model changes. The provided animation is then
     49        added to the m_animations list unless its timeline is no longer this timeline.
     50        (WebCore::AnimationTimeline::removeAnimation): Remove the provided animation from m_animations and remove any animation registered on the element-specific
     51        animation lists if this animation has an effect with a target.
     52        (WebCore::AnimationTimeline::animationWasAddedToElement): We no longer need to worry about the m_animationsWithoutTarget data structure since we removed it.
     53        (WebCore::removeCSSTransitionFromMap): Fix a bug where we would remove any CSSTransition in the provided map that had a matching transition-property instead
     54        of checking the CSSTransition registered for this transition-property was indeed the provided CSSTransition. The other code changes in this patch made this
     55        code now cause regressions in the Web Platform Tests.
     56        (WebCore::AnimationTimeline::animationWasRemovedFromElement): Stop updating m_animationsWithoutTarget since it no longer exists.
     57        (WebCore::AnimationTimeline::elementWasRemoved):
     58        (WebCore::AnimationTimeline::updateCSSAnimationsForElement): Fix a small error that caused a regression in the Web Platform Tests where we could attempt to
     59        call setBackingAnimation() on a nullptr instead of a valid CSSAnimation.
     60        (WebCore::AnimationTimeline::cancelOrRemoveDeclarativeAnimation):
     61        (WebCore::AnimationTimeline::addAnimation): Deleted.
     62        * animation/AnimationTimeline.h:
     63        (WebCore::AnimationTimeline::hasElementAnimations const): Deleted.
     64        (WebCore::AnimationTimeline:: const): Deleted.
     65        (WebCore::AnimationTimeline::elementToAnimationsMap): Deleted.
     66        (WebCore::AnimationTimeline::elementToCSSAnimationsMap): Deleted.
     67        (WebCore::AnimationTimeline::elementToCSSTransitionsMap): Deleted.
     68        * animation/CSSTransition.cpp:
     69        (WebCore::CSSTransition::canBeListed const): Deleted.
     70        * animation/CSSTransition.h:
     71        * animation/DeclarativeAnimation.cpp:
     72        (WebCore::DeclarativeAnimation::tick): Call the superclass's method and queue any necessary DOM events reflecting the timing model changes.
     73        (WebCore::DeclarativeAnimation::needsTick const): Call the superclass's method and return true also if we have pending events since otherwise this animation
     74        could be removed from m_animations on its AnimationTimeline and potentially destroyed before the GenericEventQueue had a chance to dispatch all events.
     75        (WebCore::DeclarativeAnimation::startTime const): We removed the custom binding for this IDL property and renamed the method from bindingsStartTime to startTime.
     76        (WebCore::DeclarativeAnimation::setStartTime): We removed the custom binding for this IDL property and renamed the method from setBindingsStartTime to setStartTime.
     77        (WebCore::DeclarativeAnimation::bindingsStartTime const): Deleted.
     78        (WebCore::DeclarativeAnimation::setBindingsStartTime): Deleted.
     79        * animation/DeclarativeAnimation.h:
     80        * animation/DocumentAnimationScheduler.cpp:
     81        (WebCore::DocumentAnimationScheduler::unscheduleWebAnimationsResolution): Add a method to mark that we no longer need a display refresh monitor update for this
     82        document's animation timeline. This is called when m_animations becomes empty.
     83        * animation/DocumentAnimationScheduler.h:
     84        * animation/DocumentTimeline.cpp:
     85        (WebCore::DocumentTimeline::DocumentTimeline):
     86        (WebCore::DocumentTimeline::detachFromDocument): Stop clearing two task queues and a timer that no longer exist and instead only clear the task queue to clear
     87        the cached current time, which we queue any time we generate a new one (see DocumentTimeline::currentTime).
     88        (WebCore::DocumentTimeline::getAnimations const): Use isRelevant() instead of canBeListed().
     89        (WebCore::DocumentTimeline::updateThrottlingState):
     90        (WebCore::DocumentTimeline::suspendAnimations):
     91        (WebCore::DocumentTimeline::resumeAnimations):
     92        (WebCore::DocumentTimeline::numberOfActiveAnimationsForTesting const):
     93        (WebCore::DocumentTimeline::currentTime): Queue a task in the new m_currentTimeClearingTaskQueue task queue to clear the current time that we've generated and cached
     94        in the next run loop (provided all pending JS execution has also completed).
     95        (WebCore::DocumentTimeline::maybeClearCachedCurrentTime):
     96        (WebCore::DocumentTimeline::scheduleAnimationResolutionIfNeeded): Schedule a display refresh monitor update if we are not suspended and have "relevant" animations.
     97        (WebCore::DocumentTimeline::animationTimingDidChange): Call scheduleAnimationResolutionIfNeeded() after calling the superclass's implementation.
     98        (WebCore::DocumentTimeline::removeAnimation): Call unscheduleAnimationResolution() if the list of "relevant" animations is now empty.
     99        (WebCore::DocumentTimeline::unscheduleAnimationResolution): Unschedule a pending display refresh monitor update.
     100        (WebCore::DocumentTimeline::animationResolutionTimerFired):
     101        (WebCore::DocumentTimeline::updateAnimationsAndSendEvents): Implement the "update animations and send events" procedure as specified by the Web Animations spec.
     102        During this procedure, we call tick() on all animations listed in m_animations and create a list of animations to remove from that list if this animation is no
     103        longer relevant following the call to tick().
     104        (WebCore::DocumentTimeline::enqueueAnimationPlaybackEvent):
     105        (WebCore::DocumentTimeline::timingModelDidChange): Deleted.
     106        (WebCore::DocumentTimeline::scheduleInvalidationTaskIfNeeded): Deleted.
     107        (WebCore::DocumentTimeline::performInvalidationTask): Deleted.
     108        (WebCore::DocumentTimeline::updateAnimationSchedule): Deleted.
     109        (WebCore::DocumentTimeline::animationScheduleTimerFired): Deleted.
     110        (WebCore::DocumentTimeline::updateAnimations): Deleted.
     111        (WebCore::compareAnimationPlaybackEvents): Deleted.
     112        (WebCore::DocumentTimeline::performEventDispatchTask): Deleted.
     113        * animation/DocumentTimeline.h:
     114        * animation/WebAnimation.cpp: The majority of the changes to this class is that we call the new timingDidChange() method when any code that modifies the timing model
     115        is run. We also remove methods to set the pending play and pause tasks as well as the animation's start time and hold time since any time we're changing these instance
     116        variables, we later already have a call to update the timing model and we were doing more work than needed. As a result we no longer need an internal method to set the
     117        start time and can stop requiring a custom IDL binding for the "startTime" property.
     118        (WebCore::WebAnimation::effectTimingPropertiesDidChange):
     119        (WebCore::WebAnimation::setEffect):
     120        (WebCore::WebAnimation::setEffectInternal):
     121        (WebCore::WebAnimation::setTimeline):
     122        (WebCore::WebAnimation::setTimelineInternal):
     123        (WebCore::WebAnimation::startTime const):
     124        (WebCore::WebAnimation::setStartTime):
     125        (WebCore::WebAnimation::silentlySetCurrentTime):
     126        (WebCore::WebAnimation::setCurrentTime):
     127        (WebCore::WebAnimation::setPlaybackRate):
     128        (WebCore::WebAnimation::cancel):
     129        (WebCore::WebAnimation::resetPendingTasks):
     130        (WebCore::WebAnimation::finish):
     131        (WebCore::WebAnimation::timingDidChange): New method called any time a timing property changed where we run the "update the finished state" procedure and notify the
     132        animation's timeline that its timing changed so that it can be considered the next time the "update animations and send events" procedure runs.
     133        (WebCore::WebAnimation::invalidateEffect):
     134        (WebCore::WebAnimation::updateFinishedState): Update the animation's relevance after running the procedure as specified.
     135        (WebCore::WebAnimation::play):
     136        (WebCore::WebAnimation::runPendingPlayTask):
     137        (WebCore::WebAnimation::pause):
     138        (WebCore::WebAnimation::runPendingPauseTask):
     139        (WebCore::WebAnimation::needsTick const):
     140        (WebCore::WebAnimation::tick): New method called during the "update animations and send events" procedure where we run the "update the finished state" procedure and run
     141        the pending play and pause tasks.
     142        (WebCore::WebAnimation::resolve):
     143        (WebCore::WebAnimation::updateRelevance):
     144        (WebCore::WebAnimation::computeRelevance):
     145        (WebCore::WebAnimation::timingModelDidChange): Deleted.
     146        (WebCore::WebAnimation::setHoldTime): Deleted.
     147        (WebCore::WebAnimation::bindingsStartTime const): Deleted.
     148        (WebCore::WebAnimation::setBindingsStartTime): Deleted.
     149        (WebCore::WebAnimation::setTimeToRunPendingPlayTask): Deleted.
     150        (WebCore::WebAnimation::setTimeToRunPendingPauseTask): Deleted.
     151        (WebCore::WebAnimation::updatePendingTasks): Deleted.
     152        (WebCore::WebAnimation::timeToNextRequiredTick const): Deleted.
     153        (WebCore::WebAnimation::runPendingTasks): Deleted.
     154        (WebCore::WebAnimation::canBeListed const): Deleted.
     155        * animation/WebAnimation.h:
     156        (WebCore::WebAnimation::isRelevant const):
     157        (WebCore::WebAnimation::hasPendingPlayTask const):
     158        (WebCore::WebAnimation::isEffectInvalidationSuspended):
     159        * animation/WebAnimation.idl:
     160        * dom/Element.cpp:
     161        (WebCore::Element::getAnimations): Use isRelevant() instead of canBeListed().
     162
    11632018-10-30  Youenn Fablet  <youenn@apple.com>
    2164
  • trunk/Source/WebCore/animation/AnimationTimeline.cpp

    r237474 r237587  
    5656}
    5757
    58 void AnimationTimeline::addAnimation(Ref<WebAnimation>&& animation)
    59 {
    60     m_animationsWithoutTarget.add(animation.ptr());
    61     m_animations.add(WTFMove(animation));
    62     timingModelDidChange();
    63 }
    64 
    65 void AnimationTimeline::removeAnimation(Ref<WebAnimation>&& animation)
    66 {
    67     m_animationsWithoutTarget.remove(animation.ptr());
    68     m_animations.remove(WTFMove(animation));
    69     timingModelDidChange();
     58void AnimationTimeline::animationTimingDidChange(WebAnimation& animation)
     59{
     60    if (m_animations.add(&animation)) {
     61        auto* timeline = animation.timeline();
     62        if (timeline && timeline != this)
     63            timeline->removeAnimation(animation);
     64    }
     65}
     66
     67void AnimationTimeline::removeAnimation(WebAnimation& animation)
     68{
     69    ASSERT(!animation.timeline() || animation.timeline() == this);
     70    m_animations.remove(&animation);
     71    if (is<KeyframeEffectReadOnly>(animation.effect())) {
     72        if (auto* target = downcast<KeyframeEffectReadOnly>(animation.effect())->target())
     73            animationWasRemovedFromElement(animation, *target);
     74    }
    7075}
    7176
     
    8994void AnimationTimeline::animationWasAddedToElement(WebAnimation& animation, Element& element)
    9095{
    91     m_animationsWithoutTarget.remove(&animation);
    92 
    9396    relevantMapForAnimation(animation).ensure(&element, [] {
    9497        return ListHashSet<RefPtr<WebAnimation>> { };
     
    103106
    104107    auto& cssTransitionsByProperty = iterator->value;
    105     cssTransitionsByProperty.remove(transition.property());
     108
     109    auto transitionIterator = cssTransitionsByProperty.find(transition.property());
     110    if (transitionIterator == cssTransitionsByProperty.end() || transitionIterator->value != &transition)
     111        return false;
     112
     113    cssTransitionsByProperty.remove(transitionIterator);
     114
    106115    if (cssTransitionsByProperty.isEmpty())
    107116        map.remove(&element);
     
    111120void AnimationTimeline::animationWasRemovedFromElement(WebAnimation& animation, Element& element)
    112121{
    113     // This animation doesn't have a target for now.
    114     m_animationsWithoutTarget.add(&animation);
    115 
    116122    // First, we clear this animation from one of the m_elementToCSSAnimationsMap, m_elementToCSSTransitionsMap,
    117123    // m_elementToAnimationsMap or m_elementToCompletedCSSTransitionByCSSPropertyID map, whichever is relevant to
     
    255261                // created a CSSAnimation object for it and need to ensure that this CSSAnimation is backed by the current
    256262                // animation object for this animation name.
    257                 cssAnimationsByName.get(name)->setBackingAnimation(currentAnimation);
     263                if (auto cssAnimation = cssAnimationsByName.get(name))
     264                    cssAnimation->setBackingAnimation(currentAnimation);
    258265            } else if (shouldConsiderAnimation(element, currentAnimation)) {
    259266                // Otherwise we are dealing with a new animation name and must create a CSSAnimation for it.
     
    470477    animation->cancel();
    471478    animationWasRemovedFromElement(*animation, animation->target());
    472     removeAnimation(animation.releaseNonNull());
     479    removeAnimation(*animation);
    473480}
    474481
  • trunk/Source/WebCore/animation/AnimationTimeline.h

    r237474 r237587  
    4848public:
    4949    bool isDocumentTimeline() const { return m_classType == DocumentTimelineClass; }
    50     void addAnimation(Ref<WebAnimation>&&);
    51     void removeAnimation(Ref<WebAnimation>&&);
     50
     51    virtual void animationTimingDidChange(WebAnimation&);
     52    virtual void removeAnimation(WebAnimation&);
     53
    5254    std::optional<double> bindingsCurrentTime();
    5355    virtual std::optional<Seconds> currentTime() { return m_currentTime; }
     
    7880    explicit AnimationTimeline(ClassType);
    7981
    80     bool hasElementAnimations() const { return !m_elementToAnimationsMap.isEmpty() || !m_elementToCSSAnimationsMap.isEmpty() || !m_elementToCSSTransitionsMap.isEmpty(); }
    81 
    82     const ListHashSet<WebAnimation*>& animationsWithoutTarget() const { return m_animationsWithoutTarget; }
    83     const HashMap<Element*, ListHashSet<RefPtr<WebAnimation>>>& elementToAnimationsMap() { return m_elementToAnimationsMap; }
    84     const HashMap<Element*, ListHashSet<RefPtr<WebAnimation>>>& elementToCSSAnimationsMap() { return m_elementToCSSAnimationsMap; }
    85     const HashMap<Element*, ListHashSet<RefPtr<WebAnimation>>>& elementToCSSTransitionsMap() { return m_elementToCSSTransitionsMap; }
     82    ListHashSet<RefPtr<WebAnimation>> m_animations;
    8683
    8784private:
     
    9592    HashMap<Element*, ListHashSet<RefPtr<WebAnimation>>> m_elementToCSSAnimationsMap;
    9693    HashMap<Element*, ListHashSet<RefPtr<WebAnimation>>> m_elementToCSSTransitionsMap;
    97     ListHashSet<RefPtr<WebAnimation>> m_animations;
    98 
    99     ListHashSet<WebAnimation*> m_animationsWithoutTarget;
    10094    HashMap<Element*, HashMap<String, RefPtr<CSSAnimation>>> m_elementToCSSAnimationByName;
    10195    HashMap<Element*, HashMap<CSSPropertyID, RefPtr<CSSTransition>>> m_elementToRunningCSSTransitionByCSSPropertyID;
  • trunk/Source/WebCore/animation/CSSTransition.cpp

    r234166 r237587  
    7575}
    7676
    77 bool CSSTransition::canBeListed() const
    78 {
    79     if (auto* transitionEffect = effect()) {
    80         if (is<KeyframeEffectReadOnly>(transitionEffect)) {
    81             if (!downcast<KeyframeEffectReadOnly>(effect())->hasBlendingKeyframes())
    82                 return false;
    83         }
    84     }
    85     return WebAnimation::canBeListed();
    86 }
    87 
    8877} // namespace WebCore
  • trunk/Source/WebCore/animation/CSSTransition.h

    r233004 r237587  
    5050    double reversingShorteningFactor() const { return m_reversingShorteningFactor; }
    5151
    52     bool canBeListed() const final;
    5352    void resolve(RenderStyle&) final;
    5453
  • trunk/Source/WebCore/animation/DeclarativeAnimation.cpp

    r237498 r237587  
    5050}
    5151
     52void DeclarativeAnimation::tick()
     53{
     54    WebAnimation::tick();
     55    invalidateDOMEvents();
     56}
     57
     58bool DeclarativeAnimation::needsTick() const
     59{
     60    return WebAnimation::needsTick() || m_eventQueue.hasPendingEvents();
     61}
     62
    5263void DeclarativeAnimation::remove()
    5364{
     
    8596}
    8697
    87 std::optional<double> DeclarativeAnimation::bindingsStartTime() const
    88 {
    89     flushPendingStyleChanges();
    90     return WebAnimation::bindingsStartTime();
    91 }
    92 
    93 void DeclarativeAnimation::setBindingsStartTime(std::optional<double> startTime)
    94 {
    95     flushPendingStyleChanges();
    96     return WebAnimation::setBindingsStartTime(startTime);
     98std::optional<double> DeclarativeAnimation::startTime() const
     99{
     100    flushPendingStyleChanges();
     101    return WebAnimation::startTime();
     102}
     103
     104void DeclarativeAnimation::setStartTime(std::optional<double> startTime)
     105{
     106    flushPendingStyleChanges();
     107    return WebAnimation::setStartTime(startTime);
    97108}
    98109
  • trunk/Source/WebCore/animation/DeclarativeAnimation.h

    r237498 r237587  
    4646    const Animation& backingAnimation() const { return m_backingAnimation; }
    4747    void setBackingAnimation(const Animation&);
    48     void invalidateDOMEvents(Seconds elapsedTime = 0_s);
    4948
    50     std::optional<double> bindingsStartTime() const final;
    51     void setBindingsStartTime(std::optional<double>) final;
     49    std::optional<double> startTime() const final;
     50    void setStartTime(std::optional<double>) final;
    5251    std::optional<double> bindingsCurrentTime() const final;
    5352    ExceptionOr<void> setBindingsCurrentTime(std::optional<double>) final;
     
    6261    void cancel() final;
    6362
     63    bool needsTick() const override;
     64    void tick() override;
     65
    6466protected:
    6567    DeclarativeAnimation(Element&, const Animation&);
     
    6769    virtual void initialize(const Element&, const RenderStyle* oldStyle, const RenderStyle& newStyle);
    6870    virtual void syncPropertiesWithBackingAnimation();
     71    void invalidateDOMEvents(Seconds elapsedTime = 0_s);
    6972
    7073private:
  • trunk/Source/WebCore/animation/DocumentAnimationScheduler.cpp

    r233583 r237587  
    6565}
    6666
     67void DocumentAnimationScheduler::unscheduleWebAnimationsResolution()
     68{
     69    m_scheduledWebAnimationsResolution = false;
     70
     71    if (!m_scheduledScriptedAnimationResolution)
     72        DisplayRefreshMonitorManager::sharedManager().unregisterClient(*this);
     73}
     74
    6775bool DocumentAnimationScheduler::scheduleScriptedAnimationResolution()
    6876{
  • trunk/Source/WebCore/animation/DocumentAnimationScheduler.h

    r233394 r237587  
    4949
    5050    bool scheduleWebAnimationsResolution();
     51    void unscheduleWebAnimationsResolution();
    5152    bool scheduleScriptedAnimationResolution();
    5253
  • trunk/Source/WebCore/animation/DocumentTimeline.cpp

    r237499 r237587  
    5959    , m_document(&document)
    6060    , m_originTime(originTime)
    61     , m_animationScheduleTimer(*this, &DocumentTimeline::animationScheduleTimerFired)
    6261#if !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    6362    , m_animationResolutionTimer(*this, &DocumentTimeline::animationResolutionTimerFired)
     
    7271void DocumentTimeline::detachFromDocument()
    7372{
    74     m_invalidationTaskQueue.close();
    75     m_eventDispatchTaskQueue.close();
    76     m_animationScheduleTimer.stop();
     73    m_currentTimeClearingTaskQueue.close();
    7774    m_elementsWithRunningAcceleratedAnimations.clear();
    7875
    79     auto& animationsToRemove = animations();
     76    auto& animationsToRemove = m_animations;
    8077    while (!animationsToRemove.isEmpty())
    8178        animationsToRemove.first()->remove();
    8279
     80    unscheduleAnimationResolution();
    8381    m_document = nullptr;
    8482}
     
    9088    // FIXME: Filter and order the list as specified (webkit.org/b/179535).
    9189    Vector<RefPtr<WebAnimation>> animations;
    92     for (const auto& animation : this->animations()) {
    93         if (animation->canBeListed() && is<KeyframeEffectReadOnly>(animation->effect())) {
     90    for (const auto& animation : m_animations) {
     91        if (animation->isRelevant() && is<KeyframeEffectReadOnly>(animation->effect())) {
    9492            if (auto* target = downcast<KeyframeEffectReadOnly>(animation->effect())->target()) {
    9593                if (target->isDescendantOf(*m_document))
     
    103101void DocumentTimeline::updateThrottlingState()
    104102{
    105     m_needsUpdateAnimationSchedule = false;
    106     timingModelDidChange();
     103    scheduleAnimationResolutionIfNeeded();
    107104}
    108105
     
    119116        return;
    120117
    121     m_invalidationTaskQueue.cancelAllTasks();
    122     if (m_animationScheduleTimer.isActive())
    123         m_animationScheduleTimer.stop();
    124 
    125     for (const auto& animation : animations())
     118    for (const auto& animation : m_animations)
    126119        animation->setSuspended(true);
    127120
     
    129122
    130123    applyPendingAcceleratedAnimations();
     124
     125    unscheduleAnimationResolution();
    131126}
    132127
     
    138133    m_isSuspended = false;
    139134
    140     for (const auto& animation : animations())
     135    for (const auto& animation : m_animations)
    141136        animation->setSuspended(false);
    142137
    143     m_needsUpdateAnimationSchedule = false;
    144     timingModelDidChange();
     138    scheduleAnimationResolutionIfNeeded();
    145139}
    146140
     
    153147{
    154148    unsigned count = 0;
    155     for (const auto& animation : animations()) {
     149    for (const auto& animation : m_animations) {
    156150        if (!animation->isSuspended())
    157151            ++count;
     
    197191        // animations, so we schedule the invalidation task and register a whenIdle callback on the VM, which will
    198192        // fire syncronously if no JS is running.
    199         scheduleInvalidationTaskIfNeeded();
    200193        m_waitingOnVMIdle = true;
     194        if (!m_currentTimeClearingTaskQueue.hasPendingTasks())
     195            m_currentTimeClearingTaskQueue.enqueueTask(std::bind(&DocumentTimeline::maybeClearCachedCurrentTime, this));
    201196        m_document->vm().whenIdle([this, protectedThis = makeRefPtr(this)]() {
    202197            m_waitingOnVMIdle = false;
     
    207202}
    208203
    209 void DocumentTimeline::timingModelDidChange()
    210 {
    211     if (m_needsUpdateAnimationSchedule || m_isSuspended)
    212         return;
    213 
    214     m_needsUpdateAnimationSchedule = true;
    215 
    216     // We know that we will resolve animations again, so we can cancel the timer right away.
    217     if (m_animationScheduleTimer.isActive())
    218         m_animationScheduleTimer.stop();
    219 
    220     scheduleInvalidationTaskIfNeeded();
    221 }
    222 
    223 void DocumentTimeline::scheduleInvalidationTaskIfNeeded()
    224 {
    225     if (m_invalidationTaskQueue.hasPendingTasks())
    226         return;
    227 
    228     m_invalidationTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performInvalidationTask, this));
    229 }
    230 
    231 void DocumentTimeline::performInvalidationTask()
    232 {
    233     // Now that the timing model has changed we can see if there are DOM events to dispatch for declarative animations.
    234     if (!m_isSuspended) {
    235         for (auto& animation : animations()) {
    236             if (is<DeclarativeAnimation>(animation))
    237                 downcast<DeclarativeAnimation>(*animation).invalidateDOMEvents();
    238         }
    239     }
    240 
    241     applyPendingAcceleratedAnimations();
    242 
    243     updateAnimationSchedule();
    244     maybeClearCachedCurrentTime();
    245 }
    246 
    247204void DocumentTimeline::maybeClearCachedCurrentTime()
    248205{
     
    251208    // we're guaranteed to have a consistent current time reported for all work happening in a given
    252209    // JS frame or throughout updating animations in WebCore.
    253     if (!m_waitingOnVMIdle && !m_invalidationTaskQueue.hasPendingTasks())
     210    if (!m_waitingOnVMIdle && !m_currentTimeClearingTaskQueue.hasPendingTasks())
    254211        m_cachedCurrentTime = std::nullopt;
    255212}
    256213
    257 void DocumentTimeline::updateAnimationSchedule()
    258 {
    259     if (!m_needsUpdateAnimationSchedule)
    260         return;
    261 
    262     m_needsUpdateAnimationSchedule = false;
    263 
    264     if (!m_acceleratedAnimationsPendingRunningStateChange.isEmpty()) {
     214void DocumentTimeline::scheduleAnimationResolutionIfNeeded()
     215{
     216    if (!m_isSuspended && !m_animations.isEmpty())
    265217        scheduleAnimationResolution();
    266         return;
    267     }
    268 
    269     Seconds scheduleDelay = Seconds::infinity();
    270 
    271     for (const auto& animation : animations()) {
    272         auto animationTimeToNextRequiredTick = animation->timeToNextRequiredTick();
    273         if (animationTimeToNextRequiredTick < animationInterval()) {
    274             scheduleAnimationResolution();
    275             return;
    276         }
    277         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
    278     }
    279 
    280     if (scheduleDelay < Seconds::infinity())
    281         m_animationScheduleTimer.startOneShot(scheduleDelay);
    282 }
    283 
    284 void DocumentTimeline::animationScheduleTimerFired()
    285 {
    286     scheduleAnimationResolution();
     218}
     219
     220void DocumentTimeline::animationTimingDidChange(WebAnimation& animation)
     221{
     222    AnimationTimeline::animationTimingDidChange(animation);
     223    scheduleAnimationResolutionIfNeeded();
     224}
     225
     226void DocumentTimeline::removeAnimation(WebAnimation& animation)
     227{
     228    AnimationTimeline::removeAnimation(animation);
     229
     230    if (m_animations.isEmpty())
     231        unscheduleAnimationResolution();
    287232}
    288233
     
    298243}
    299244
     245void DocumentTimeline::unscheduleAnimationResolution()
     246{
     247#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
     248    m_document->animationScheduler().unscheduleWebAnimationsResolution();
     249#else
     250    // FIXME: We need to use the same logic as ScriptedAnimationController here,
     251    // which will be addressed by the refactor tracked by webkit.org/b/179293.
     252    m_animationResolutionTimer.stop();
     253#endif
     254}
     255
    300256#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    301257void DocumentTimeline::documentAnimationSchedulerDidFire()
     
    304260#endif
    305261{
    306     updateAnimations();
    307 }
    308 
    309 void DocumentTimeline::updateAnimations()
     262    updateAnimationsAndSendEvents();
     263}
     264
     265void DocumentTimeline::updateAnimationsAndSendEvents()
    310266{
    311267    m_numberOfAnimationTimelineInvalidationsForTesting++;
    312268
    313     for (const auto& animation : animations())
    314         animation->runPendingTasks();
    315 
    316     // Perform a microtask checkpoint such that all promises that may have resolved while
    317     // running pending tasks can fire right away.
     269    // https://drafts.csswg.org/web-animations/#update-animations-and-send-events
     270
     271    // 1. Update the current time of all timelines associated with doc passing now as the timestamp.
     272
     273    Vector<RefPtr<WebAnimation>> animationsToRemove;
     274
     275    for (auto& animation : m_animations) {
     276        if (animation->timeline() != this) {
     277            ASSERT(!animation->timeline());
     278            animationsToRemove.append(animation);
     279            continue;
     280        }
     281
     282        // This will notify the animation that timing has changed and will call automatically
     283        // schedule invalidation if required for this animation.
     284        animation->tick();
     285
     286        if (!animation->isRelevant() && !animation->needsTick())
     287            animationsToRemove.append(animation);
     288    }
     289
     290    // 2. Perform a microtask checkpoint.
    318291    MicrotaskQueue::mainThreadQueue().performMicrotaskCheckpoint();
    319292
    320     // Let's first resolve any animation that does not have a target.
    321     for (auto* animation : animationsWithoutTarget())
    322         animation->resolve();
    323 
    324     // For the rest of the animations, we will resolve them via TreeResolver::createAnimatedElementUpdate()
    325     // by invalidating their target element's style.
    326     if (m_document && hasElementAnimations()) {
    327         for (const auto& elementToAnimationsMapItem : elementToAnimationsMap())
    328             elementToAnimationsMapItem.key->invalidateStyleAndLayerComposition();
    329         for (const auto& elementToCSSAnimationsMapItem : elementToCSSAnimationsMap())
    330             elementToCSSAnimationsMapItem.key->invalidateStyleAndLayerComposition();
    331         for (const auto& elementToCSSTransitionsMapItem : elementToCSSTransitionsMap())
    332             elementToCSSTransitionsMapItem.key->invalidateStyleAndLayerComposition();
    333         m_document->updateStyleIfNeeded();
    334     }
    335 
    336     // Time has advanced, the timing model requires invalidation now.
    337     timingModelDidChange();
     293    // 3. Let events to dispatch be a copy of doc's pending animation event queue.
     294    // 4. Clear doc's pending animation event queue.
     295    auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
     296
     297    // 5. Perform a stable sort of the animation events in events to dispatch as follows.
     298    std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), [] (const Ref<AnimationPlaybackEvent>& lhs, const Ref<AnimationPlaybackEvent>& rhs) {
     299        // 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
     300        // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
     301        // 2. Within events with equal scheduled event times, sort by their composite order. FIXME: We don't do this.
     302        if (lhs->timelineTime() && !rhs->timelineTime())
     303            return false;
     304        if (!lhs->timelineTime() && rhs->timelineTime())
     305            return true;
     306        if (!lhs->timelineTime() && !rhs->timelineTime())
     307            return true;
     308        return lhs->timelineTime().value() < rhs->timelineTime().value();
     309    });
     310
     311    // 6. Dispatch each of the events in events to dispatch at their corresponding target using the order established in the previous step.
     312    for (auto& pendingEvent : pendingAnimationEvents)
     313        pendingEvent->target()->dispatchEvent(pendingEvent);
     314
     315    // This will cancel any scheduled invalidation if we end up removing all animations.
     316    for (auto& animation : animationsToRemove)
     317        removeAnimation(*animation);
     318
     319    applyPendingAcceleratedAnimations();
    338320}
    339321
     
    504486{
    505487    m_pendingAnimationEvents.append(event);
    506 
    507     if (!m_eventDispatchTaskQueue.hasPendingTasks())
    508         m_eventDispatchTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performEventDispatchTask, this));
    509 }
    510 
    511 static inline bool compareAnimationPlaybackEvents(const Ref<WebCore::AnimationPlaybackEvent>& lhs, const Ref<WebCore::AnimationPlaybackEvent>& rhs)
    512 {
    513     // Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
    514     // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
    515     if (lhs->timelineTime() && !rhs->timelineTime())
    516         return false;
    517     if (!lhs->timelineTime() && rhs->timelineTime())
    518         return true;
    519     if (!lhs->timelineTime() && !rhs->timelineTime())
    520         return true;
    521     return lhs->timelineTime().value() < rhs->timelineTime().value();
    522 }
    523 
    524 void DocumentTimeline::performEventDispatchTask()
    525 {
    526     if (m_pendingAnimationEvents.isEmpty())
    527         return;
    528 
    529     auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
    530 
    531     std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), compareAnimationPlaybackEvents);
    532     for (auto& pendingEvent : pendingAnimationEvents)
    533         pendingEvent->target()->dispatchEvent(pendingEvent);
    534488}
    535489
  • trunk/Source/WebCore/animation/DocumentTimeline.h

    r237499 r237587  
    5050    std::optional<Seconds> currentTime() override;
    5151
    52     void timingModelDidChange() override;
    53 
     52    void animationTimingDidChange(WebAnimation&) override;
     53    void removeAnimation(WebAnimation&) override;
    5454    void animationWasAddedToElement(WebAnimation&, Element&) final;
    5555    void animationWasRemovedFromElement(WebAnimation&, Element&) final;
     
    8686    DocumentTimeline(Document&, Seconds);
    8787
     88    void scheduleAnimationResolutionIfNeeded();
    8889    void scheduleInvalidationTaskIfNeeded();
    8990    void performInvalidationTask();
    90     void updateAnimationSchedule();
    9191    void animationScheduleTimerFired();
    9292    void scheduleAnimationResolution();
    93     void updateAnimations();
     93    void unscheduleAnimationResolution();
     94    void updateAnimationsAndSendEvents();
    9495    void performEventDispatchTask();
    9596    void maybeClearCachedCurrentTime();
     
    101102    bool m_waitingOnVMIdle { false };
    102103    std::optional<Seconds> m_cachedCurrentTime;
    103     GenericTaskQueue<Timer> m_invalidationTaskQueue;
    104     GenericTaskQueue<Timer> m_eventDispatchTaskQueue;
    105     bool m_needsUpdateAnimationSchedule { false };
    106     Timer m_animationScheduleTimer;
     104    GenericTaskQueue<Timer> m_currentTimeClearingTaskQueue;
    107105    HashSet<RefPtr<WebAnimation>> m_acceleratedAnimationsPendingRunningStateChange;
    108106    Vector<Ref<AnimationPlaybackEvent>> m_pendingAnimationEvents;
  • trunk/Source/WebCore/animation/WebAnimation.cpp

    r237500 r237587  
    9393void WebAnimation::effectTimingPropertiesDidChange()
    9494{
    95     updateFinishedState(DidSeek::No, SynchronouslyNotify::Yes);
    96     timingModelDidChange();
    97 }
    98 
    99 void WebAnimation::timingModelDidChange()
    100 {
    101     if (!isEffectInvalidationSuspended() && m_effect)
    102         m_effect->invalidate();
    103     if (m_timeline)
    104         m_timeline->timingModelDidChange();
     95    timingDidChange(DidSeek::No, SynchronouslyNotify::Yes);
    10596}
    10697
     
    123114    // 4. If animation has a pending pause task, reschedule that task to run as soon as animation is ready.
    124115    if (hasPendingPauseTask())
    125         setTimeToRunPendingPauseTask(TimeToRunPendingTask::WhenReady);
     116        m_timeToRunPendingPauseTask = TimeToRunPendingTask::WhenReady;
    126117
    127118    // 5. If animation has a pending play task, reschedule that task to run as soon as animation is ready to play new effect.
    128119    if (hasPendingPlayTask())
    129         setTimeToRunPendingPlayTask(TimeToRunPendingTask::WhenReady);
     120        m_timeToRunPendingPlayTask = TimeToRunPendingTask::WhenReady;
    130121
    131122    // 6. If new effect is not null and if new effect is the target effect of another animation, previous animation, run the
     
    139130    // not break the timeline-to-animation relationship.
    140131
     132    invalidateEffect();
     133
    141134    // This object could be deleted after clearing the effect relationship.
    142135    auto protectedThis = makeRef(*this);
     
    145138    // 8. Run the procedure to update an animation’s finished state for animation with the did seek flag set to false,
    146139    // and the synchronously notify flag set to false.
    147     updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
    148 
    149     timingModelDidChange();
     140    timingDidChange(DidSeek::No, SynchronouslyNotify::No);
     141
     142    invalidateEffect();
    150143}
    151144
     
    170163        if (!doNotRemoveFromTimeline && m_timeline && previousTarget && previousTarget != newTarget)
    171164            m_timeline->animationWasRemovedFromElement(*this, *previousTarget);
     165        updateRelevance();
    172166    }
    173167
     
    190184    // 4. If the animation start time of animation is resolved, make animation’s hold time unresolved.
    191185    if (m_startTime)
    192         setHoldTime(std::nullopt);
     186        m_holdTime = std::nullopt;
    193187
    194188    if (is<KeyframeEffectReadOnly>(m_effect)) {
     
    212206    setSuspended(is<DocumentTimeline>(m_timeline) && downcast<DocumentTimeline>(*m_timeline).animationsAreSuspended());
    213207
    214     updatePendingTasks();
    215 
    216208    // 5. Run the procedure to update an animation’s finished state for animation with the did seek flag set to false,
    217209    // and the synchronously notify flag set to false.
    218     updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
     210    timingDidChange(DidSeek::No, SynchronouslyNotify::No);
     211
     212    invalidateEffect();
    219213}
    220214
     
    228222
    229223    m_timeline = WTFMove(timeline);
    230 
    231     if (m_timeline)
    232         m_timeline->addAnimation(*this);
    233224}
    234225
     
    245236}
    246237
    247 void WebAnimation::setHoldTime(std::optional<Seconds> holdTime)
    248 {
    249     if (m_holdTime == holdTime)
    250         return;
    251 
    252     m_holdTime = holdTime;
    253     timingModelDidChange();
    254 }
    255 
    256 std::optional<double> WebAnimation::bindingsStartTime() const
     238std::optional<double> WebAnimation::startTime() const
    257239{
    258240    if (!m_startTime)
     
    261243}
    262244
    263 void WebAnimation::setBindingsStartTime(std::optional<double> startTime)
     245void WebAnimation::setStartTime(std::optional<double> startTime)
    264246{
    265247    // 3.4.6 The procedure to set the start time of animation, animation, to new start time, is as follows:
     
    279261    // 2. If timeline time is unresolved and new start time is resolved, make animation's hold time unresolved.
    280262    if (!timelineTime && newStartTime)
    281         setHoldTime(std::nullopt);
     263        m_holdTime = std::nullopt;
    282264
    283265    // 3. Let previous current time be animation's current time.
     
    285267
    286268    // 4. Set animation's start time to new start time.
    287     setStartTime(newStartTime);
     269    m_startTime = newStartTime;
    288270
    289271    // 5. Update animation's hold time based on the first matching condition from the following,
     
    292274        // If animation’s playback rate is not zero, make animation’s hold time unresolved.
    293275        if (m_playbackRate)
    294             setHoldTime(std::nullopt);
     276            m_holdTime = std::nullopt;
    295277    } else {
    296278        // Otherwise (new start time is unresolved),
    297279        // Set animation's hold time to previous current time even if previous current time is unresolved.
    298         setHoldTime(previousCurrentTime);
     280        m_holdTime = previousCurrentTime;
    299281    }
    300282
    301283    // 6. If animation has a pending play task or a pending pause task, cancel that task and resolve animation's current ready promise with animation.
    302284    if (pending()) {
    303         setTimeToRunPendingPauseTask(TimeToRunPendingTask::NotScheduled);
    304         setTimeToRunPendingPlayTask(TimeToRunPendingTask::NotScheduled);
     285        m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
     286        m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
    305287        m_readyPromise->resolve(*this);
    306288    }
    307289
    308290    // 7. Run the procedure to update an animation’s finished state for animation with the did seek flag set to true, and the synchronously notify flag set to false.
    309     updateFinishedState(DidSeek::Yes, SynchronouslyNotify::No);
    310 
    311     timingModelDidChange();
    312 }
    313 
    314 std::optional<Seconds> WebAnimation::startTime() const
    315 {
    316     return m_startTime;
    317 }
    318 
    319 void WebAnimation::setStartTime(std::optional<Seconds> newStartTime)
    320 {
    321     if (m_startTime == newStartTime)
    322         return;
    323 
    324     m_startTime = newStartTime;
    325     timingModelDidChange();
     291    timingDidChange(DidSeek::Yes, SynchronouslyNotify::No);
     292
     293    invalidateEffect();
    326294}
    327295
     
    393361    // where timeline time is the current time value of timeline associated with animation.
    394362    if (m_holdTime || !m_startTime || !m_timeline || !m_timeline->currentTime() || !m_playbackRate)
    395         setHoldTime(seekTime);
     363        m_holdTime = seekTime;
    396364    else
    397         setStartTime(m_timeline->currentTime().value() - (seekTime.value() / m_playbackRate));
     365        m_startTime = m_timeline->currentTime().value() - (seekTime.value() / m_playbackRate);
    398366
    399367    // 3. If animation has no associated timeline or the associated timeline is inactive, make animation's start time unresolved.
    400368    if (!m_timeline || !m_timeline->currentTime())
    401         setStartTime(std::nullopt);
     369        m_startTime = std::nullopt;
    402370
    403371    // 4. Make animation's previous current time unresolved.
     
    420388    if (hasPendingPauseTask()) {
    421389        // 1. Set animation's hold time to seek time.
    422         setHoldTime(seekTime);
     390        m_holdTime = seekTime;
    423391        // 2. Make animation's start time unresolved.
    424         setStartTime(std::nullopt);
     392        m_startTime = std::nullopt;
    425393        // 3. Cancel the pending pause task.
    426         setTimeToRunPendingPauseTask(TimeToRunPendingTask::NotScheduled);
     394        m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
    427395        // 4. Resolve animation's current ready promise with animation.
    428396        m_readyPromise->resolve(*this);
     
    430398
    431399    // 3. Run the procedure to update an animation's finished state for animation with the did seek flag set to true, and the synchronously notify flag set to false.
    432     updateFinishedState(DidSeek::Yes, SynchronouslyNotify::No);
     400    timingDidChange(DidSeek::Yes, SynchronouslyNotify::No);
    433401
    434402    if (m_effect)
    435403        m_effect->animationDidSeek();
     404
     405    invalidateEffect();
    436406
    437407    return { };
     
    467437    else
    468438        setCurrentTime(previousTime);
     439
     440    invalidateEffect();
    469441}
    470442
     
    511483{
    512484    cancel(Silently::No);
     485    invalidateEffect();
    513486}
    514487
     
    550523
    551524    // 2. Make animation's hold time unresolved.
    552     setHoldTime(std::nullopt);
     525    m_holdTime = std::nullopt;
    553526
    554527    // 3. Make animation's start time unresolved.
    555     setStartTime(std::nullopt);
     528    m_startTime = std::nullopt;
     529
     530    timingDidChange(DidSeek::No, SynchronouslyNotify::No);
     531
     532    invalidateEffect();
    556533}
    557534
     
    587564    // 2. If animation has a pending play task, cancel that task.
    588565    if (hasPendingPlayTask())
    589         setTimeToRunPendingPlayTask(TimeToRunPendingTask::NotScheduled);
     566        m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
    590567
    591568    // 3. If animation has a pending pause task, cancel that task.
    592569    if (hasPendingPauseTask())
    593         setTimeToRunPendingPauseTask(TimeToRunPendingTask::NotScheduled);
     570        m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
    594571
    595572    // 4. Reject animation's current ready promise with a DOMException named "AbortError".
     
    624601    //    evaluating timeline time - (limit / playback rate) where timeline time is the current time value of the associated timeline.
    625602    if (!m_startTime && m_timeline && m_timeline->currentTime())
    626         setStartTime(m_timeline->currentTime().value() - (limit / m_playbackRate));
     603        m_startTime = m_timeline->currentTime().value() - (limit / m_playbackRate);
    627604
    628605    // 5. If there is a pending pause task and start time is resolved,
    629606    if (hasPendingPauseTask() && m_startTime) {
    630607        // 1. Let the hold time be unresolved.
    631         setHoldTime(std::nullopt);
     608        m_holdTime = std::nullopt;
    632609        // 2. Cancel the pending pause task.
    633         setTimeToRunPendingPauseTask(TimeToRunPendingTask::NotScheduled);
     610        m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
    634611        // 3. Resolve the current ready promise of animation with animation.
    635612        m_readyPromise->resolve(*this);
     
    638615    // 6. If there is a pending play task and start time is resolved, cancel that task and resolve the current ready promise of animation with animation.
    639616    if (hasPendingPlayTask() && m_startTime) {
    640         setTimeToRunPendingPlayTask(TimeToRunPendingTask::NotScheduled);
     617        m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
    641618        m_readyPromise->resolve(*this);
    642619    }
    643620
    644621    // 7. Run the procedure to update an animation's finished state animation with the did seek flag set to true, and the synchronously notify flag set to true.
    645     updateFinishedState(DidSeek::Yes, SynchronouslyNotify::Yes);
     622    timingDidChange(DidSeek::Yes, SynchronouslyNotify::Yes);
     623
     624    invalidateEffect();
    646625
    647626    return { };
     627}
     628
     629void WebAnimation::timingDidChange(DidSeek didSeek, SynchronouslyNotify synchronouslyNotify)
     630{
     631    updateFinishedState(didSeek, synchronouslyNotify);
     632    if (m_timeline)
     633        m_timeline->animationTimingDidChange(*this);
     634};
     635
     636void WebAnimation::invalidateEffect()
     637{
     638    if (!isEffectInvalidationSuspended() && m_effect)
     639        m_effect->invalidate();
    648640}
    649641
     
    668660            // If did seek is true, let the hold time be the value of unconstrained current time.
    669661            if (didSeek == DidSeek::Yes)
    670                 setHoldTime(unconstrainedCurrentTime);
     662                m_holdTime = unconstrainedCurrentTime;
    671663            // If did seek is false, let the hold time be the maximum value of previous current time and target effect end. If the previous current time is unresolved, let the hold time be target effect end.
    672664            else if (!m_previousCurrentTime)
    673                 setHoldTime(endTime);
     665                m_holdTime = endTime;
    674666            else
    675                 setHoldTime(std::max(m_previousCurrentTime.value(), endTime));
     667                m_holdTime = std::max(m_previousCurrentTime.value(), endTime);
    676668        } else if (m_playbackRate < 0 && unconstrainedCurrentTime <= 0_s) {
    677669            // If animation playback rate < 0 and unconstrained current time is less than or equal to 0,
    678670            // If did seek is true, let the hold time be the value of unconstrained current time.
    679671            if (didSeek == DidSeek::Yes)
    680                 setHoldTime(unconstrainedCurrentTime);
     672                m_holdTime = unconstrainedCurrentTime;
    681673            // If did seek is false, let the hold time be the minimum value of previous current time and zero. If the previous current time is unresolved, let the hold time be zero.
    682674            else if (!m_previousCurrentTime)
    683                 setHoldTime(0_s);
     675                m_holdTime = 0_s;
    684676            else
    685                 setHoldTime(std::min(m_previousCurrentTime.value(), 0_s));
     677                m_holdTime = std::min(m_previousCurrentTime.value(), 0_s);
    686678        } else if (m_playbackRate && m_timeline && m_timeline->currentTime()) {
    687679            // If animation playback rate ≠ 0, and animation is associated with an active timeline,
     
    690682            //    where timeline time is the current time value of timeline associated with animation.
    691683            if (didSeek == DidSeek::Yes && m_holdTime)
    692                 setStartTime(m_timeline->currentTime().value() - (m_holdTime.value() / m_playbackRate));
     684                m_startTime = m_timeline->currentTime().value() - (m_holdTime.value() / m_playbackRate);
    693685            // 2. Let the hold time be unresolved.
    694             setHoldTime(std::nullopt);
     686            m_holdTime = std::nullopt;
    695687        }
    696688    }
     
    721713    if (!currentFinishedState && m_finishedPromise->isFulfilled())
    722714        m_finishedPromise = makeUniqueRef<FinishedPromise>(*this, &WebAnimation::finishedPromiseResolve);
     715
     716    updateRelevance();
    723717}
    724718
     
    795789        //     - current time ≥ target effect end,
    796790        // Set animation's hold time to zero.
    797         setHoldTime(0_s);
     791        m_holdTime = 0_s;
    798792    } else if (m_playbackRate < 0 && autoRewind == AutoRewind::Yes && (!localTime || localTime.value() <= 0_s || localTime.value() > endTime)) {
    799793        // If animation playback rate < 0, the auto-rewind flag is true and either animation's:
     
    804798        if (endTime == Seconds::infinity())
    805799            return Exception { InvalidStateError };
    806         setHoldTime(endTime);
     800        m_holdTime = endTime;
    807801    } else if (!m_playbackRate && !localTime) {
    808802        // If animation playback rate = 0 and animation's current time is unresolved,
    809803        // Set animation's hold time to zero.
    810         setHoldTime(0_s);
     804        m_holdTime = 0_s;
    811805    }
    812806
     
    814808    if (pending()) {
    815809        // 1. Cancel that task.
    816         setTimeToRunPendingPauseTask(TimeToRunPendingTask::NotScheduled);
    817         setTimeToRunPendingPlayTask(TimeToRunPendingTask::NotScheduled);
     810        m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
     811        m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
    818812        // 2. Set has pending ready promise to true.
    819813        hasPendingReadyPromise = true;
     
    826820    // 6. If animation's hold time is resolved, let its start time be unresolved.
    827821    if (m_holdTime)
    828         setStartTime(std::nullopt);
     822        m_startTime = std::nullopt;
    829823
    830824    // 7. If has pending ready promise is false, let animation's current ready promise be a new (pending) Promise object.
     
    833827
    834828    // 8. Schedule a task to run as soon as animation is ready.
    835     setTimeToRunPendingPlayTask(TimeToRunPendingTask::WhenReady);
     829    m_timeToRunPendingPlayTask = TimeToRunPendingTask::WhenReady;
    836830
    837831    // 9. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the synchronously notify flag set to false.
    838     updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
     832    timingDidChange(DidSeek::No, SynchronouslyNotify::No);
     833
     834    invalidateEffect();
    839835
    840836    return { };
    841 }
    842 
    843 void WebAnimation::setTimeToRunPendingPlayTask(TimeToRunPendingTask timeToRunPendingTask)
    844 {
    845     if (m_timeToRunPendingPlayTask == timeToRunPendingTask)
    846         return;
    847 
    848     m_timeToRunPendingPlayTask = timeToRunPendingTask;
    849     updatePendingTasks();
    850837}
    851838
     
    876863        // 2. If animation's playback rate is not 0, make animation's hold time unresolved.
    877864        if (m_playbackRate)
    878             setHoldTime(std::nullopt);
     865            m_holdTime = std::nullopt;
    879866        // 3. Set the animation start time of animation to new start time.
    880         setStartTime(newStartTime);
     867        m_startTime = newStartTime;
    881868    }
    882869
     
    886873
    887874    // 5. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the synchronously notify flag set to false.
    888     updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
     875    timingDidChange(DidSeek::No, SynchronouslyNotify::No);
     876
     877    invalidateEffect();
    889878}
    890879
     
    908897        if (m_playbackRate >= 0) {
    909898            // If animation's playback rate is ≥ 0, let animation's hold time be zero.
    910             setHoldTime(0_s);
     899            m_holdTime = 0_s;
    911900        } else if (effectEndTime() == Seconds::infinity()) {
    912901            // Otherwise, if target effect end for animation is positive infinity, throw an InvalidStateError and abort these steps.
     
    914903        } else {
    915904            // Otherwise, let animation's hold time be target effect end.
    916             setHoldTime(effectEndTime());
     905            m_holdTime = effectEndTime();
    917906        }
    918907    }
     
    923912    // 5. If animation has a pending play task, cancel that task and let has pending ready promise be true.
    924913    if (hasPendingPlayTask()) {
    925         setTimeToRunPendingPlayTask(TimeToRunPendingTask::NotScheduled);
     914        m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
    926915        hasPendingReadyPromise = true;
    927916    }
     
    933922    // 7. Schedule a task to be executed at the first possible moment after the user agent has performed any processing necessary
    934923    //    to suspend the playback of animation's target effect, if any.
    935     setTimeToRunPendingPauseTask(TimeToRunPendingTask::ASAP);
     924    m_timeToRunPendingPauseTask = TimeToRunPendingTask::ASAP;
    936925
    937926    // 8. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the synchronously notify flag set to false.
    938     updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
     927    timingDidChange(DidSeek::No, SynchronouslyNotify::No);
     928
     929    invalidateEffect();
    939930
    940931    return { };
     
    971962
    972963    return { };
    973 }
    974 
    975 void WebAnimation::setTimeToRunPendingPauseTask(TimeToRunPendingTask timeToRunPendingTask)
    976 {
    977     if (m_timeToRunPendingPauseTask == timeToRunPendingTask)
    978         return;
    979 
    980     m_timeToRunPendingPauseTask = timeToRunPendingTask;
    981     updatePendingTasks();
    982964}
    983965
     
    1003985        // C++14 builds (the latter using WTF's std::optional) and avoid null std::optional dereferencing
    1004986        // by defaulting to a Seconds(0) value. See https://bugs.webkit.org/show_bug.cgi?id=186189.
    1005         setHoldTime((readyTime.value_or(0_s) - animationStartTime.value()) * m_playbackRate);
     987        m_holdTime = (readyTime.value_or(0_s) - animationStartTime.value()) * m_playbackRate;
    1006988    }
    1007989
    1008990    // 3. Make animation's start time unresolved.
    1009     setStartTime(std::nullopt);
     991    m_startTime = std::nullopt;
    1010992
    1011993    // 4. Resolve animation's current ready promise with animation.
     
    1015997    // 5. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the
    1016998    //    synchronously notify flag set to false.
    1017     updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
    1018 }
    1019 
    1020 void WebAnimation::updatePendingTasks()
    1021 {
    1022     timingModelDidChange();
    1023 }
    1024 
    1025 Seconds WebAnimation::timeToNextRequiredTick() const
    1026 {
    1027     // If we don't have a timeline, an effect, a start time or a playback rate other than 0,
    1028     // there is no value to apply so we don't need to schedule invalidation.
    1029     if (!m_timeline || !m_effect || !m_playbackRate)
    1030         return Seconds::infinity();
    1031 
    1032     if (pending())
    1033         return 0_s;
    1034 
    1035     if (!m_startTime)
    1036         return Seconds::infinity();
    1037 
    1038     // If we're in or expected to be in the running state, we need to schedule invalidation as soon as possible.
    1039     if (hasPendingPlayTask() || playState() == PlayState::Running)
    1040         return 0_s;
    1041 
    1042     if (auto animationCurrentTime = currentTime()) {
    1043         // If our current time is negative, we need to be scheduled to be resolved at the inverse
    1044         // of our current time, unless we fill backwards, in which case we want to invalidate as
    1045         // soon as possible.
    1046         auto localTime = animationCurrentTime.value();
    1047         if (localTime < 0_s)
    1048             return -localTime;
    1049     }
    1050 
    1051     // In any other case, we're idle or already outside our active duration and have no need
    1052     // to schedule an invalidation.
    1053     return Seconds::infinity();
    1054 }
    1055 
    1056 void WebAnimation::runPendingTasks()
    1057 {
     999    timingDidChange(DidSeek::No, SynchronouslyNotify::No);
     1000
     1001    invalidateEffect();
     1002}
     1003
     1004bool WebAnimation::needsTick() const
     1005{
     1006    return pending() || playState() == PlayState::Running;
     1007}
     1008
     1009void WebAnimation::tick()
     1010{
     1011    updateFinishedState(DidSeek::No, SynchronouslyNotify::Yes);
     1012
     1013    // Run pending tasks, if any.
    10581014    if (hasPendingPauseTask())
    10591015        runPendingPauseTask();
    1060 
    10611016    if (hasPendingPlayTask())
    10621017        runPendingPlayTask();
    1063 }
    1064 
    1065 void WebAnimation::resolve()
    1066 {
    1067     updateFinishedState(DidSeek::No, SynchronouslyNotify::Yes);
     1018
     1019    invalidateEffect();
    10681020}
    10691021
    10701022void WebAnimation::resolve(RenderStyle& targetStyle)
    10711023{
    1072     resolve();
     1024    timingDidChange(DidSeek::No, SynchronouslyNotify::Yes);
    10731025    if (m_effect)
    10741026        m_effect->apply(targetStyle);
     
    11241076}
    11251077
    1126 bool WebAnimation::canBeListed() const
     1078void WebAnimation::updateRelevance()
     1079{
     1080    m_isRelevant = computeRelevance();
     1081}
     1082
     1083bool WebAnimation::computeRelevance()
    11271084{
    11281085    // To be listed in getAnimations() an animation needs a target effect which is current or in effect.
  • trunk/Source/WebCore/animation/WebAnimation.h

    r237500 r237587  
    5858    virtual bool isCSSTransition() const { return false; }
    5959
    60     virtual bool canBeListed() const;
    61 
    6260    const String& id() const { return m_id; }
    6361    void setId(const String& id) { m_id = id; }
     
    6765    AnimationTimeline* timeline() const { return m_timeline.get(); }
    6866    virtual void setTimeline(RefPtr<AnimationTimeline>&&);
    69 
    70     std::optional<Seconds> startTime() const;
    71     void setStartTime(std::optional<Seconds>);
    7267
    7368    std::optional<Seconds> currentTime() const;
     
    9691    ExceptionOr<void> reverse();
    9792
    98     virtual std::optional<double> bindingsStartTime() const;
    99     virtual void setBindingsStartTime(std::optional<double>);
     93    virtual std::optional<double> startTime() const;
     94    virtual void setStartTime(std::optional<double>);
    10095    virtual std::optional<double> bindingsCurrentTime() const;
    10196    virtual ExceptionOr<void> setBindingsCurrentTime(std::optional<double>);
     
    107102    virtual ExceptionOr<void> bindingsPause() { return pause(); }
    108103
    109     Seconds timeToNextRequiredTick() const;
    110     void resolve();
     104    virtual bool needsTick() const;
     105    virtual void tick();
    111106    virtual void resolve(RenderStyle&);
    112     void runPendingTasks();
    113107    void effectTargetDidChange(Element* previousTarget, Element* newTarget);
    114108    void acceleratedStateDidChange();
    115109    void applyPendingAcceleratedActions();
    116110
    117     void timingModelDidChange();
     111    bool isRelevant() const { return m_isRelevant; }
    118112    void effectTimingPropertiesDidChange();
    119113    void suspendEffectInvalidation();
     
    129123    explicit WebAnimation(Document&);
    130124
    131     bool isEffectInvalidationSuspended() { return m_suspendCount; }
    132125    void stop() override;
    133126
     
    139132    enum class TimeToRunPendingTask { NotScheduled, ASAP, WhenReady };
    140133
     134    void timingDidChange(DidSeek, SynchronouslyNotify);
    141135    void updateFinishedState(DidSeek, SynchronouslyNotify);
    142136    void enqueueAnimationPlaybackEvent(const AtomicString&, std::optional<Seconds>, std::optional<Seconds>);
     
    144138    WebAnimation& readyPromiseResolve();
    145139    WebAnimation& finishedPromiseResolve();
    146     void setHoldTime(std::optional<Seconds>);
    147140    std::optional<Seconds> currentTime(RespectHoldTime) const;
    148141    ExceptionOr<void> silentlySetCurrentTime(std::optional<Seconds>);
     
    150143    void scheduleMicrotaskIfNeeded();
    151144    void performMicrotask();
    152     void setTimeToRunPendingPauseTask(TimeToRunPendingTask);
    153     void setTimeToRunPendingPlayTask(TimeToRunPendingTask);
    154145    bool hasPendingPauseTask() const { return m_timeToRunPendingPauseTask != TimeToRunPendingTask::NotScheduled; }
    155146    bool hasPendingPlayTask() const { return m_timeToRunPendingPlayTask != TimeToRunPendingTask::NotScheduled; }
    156     void updatePendingTasks();
    157147    ExceptionOr<void> play(AutoRewind);
    158148    void runPendingPauseTask();
     
    161151    void setEffectInternal(RefPtr<AnimationEffectReadOnly>&&, bool = false);
    162152    void setTimelineInternal(RefPtr<AnimationTimeline>&&);
     153    bool isEffectInvalidationSuspended() { return m_suspendCount; }
     154    bool computeRelevance();
     155    void updateRelevance();
     156    void invalidateEffect();
    163157
    164158    String m_id;
     
    174168    bool m_finishNotificationStepsMicrotaskPending;
    175169    bool m_scheduledMicrotask;
     170    bool m_isRelevant;
    176171    UniqueRef<ReadyPromise> m_readyPromise;
    177172    UniqueRef<FinishedPromise> m_finishedPromise;
  • trunk/Source/WebCore/animation/WebAnimation.idl

    r233051 r237587  
    4141    attribute AnimationEffectReadOnly? effect;
    4242    attribute AnimationTimeline? timeline;
    43     [ImplementedAs=bindingsStartTime] attribute double? startTime;
     43    attribute double? startTime;
    4444    [MayThrowException, ImplementedAs=bindingsCurrentTime] attribute double? currentTime;
    4545    attribute double playbackRate;
  • trunk/Source/WebCore/dom/Element.cpp

    r237468 r237587  
    40324032    if (auto timeline = document().existingTimeline()) {
    40334033        for (auto& animation : timeline->animationsForElement(*this, AnimationTimeline::Ordering::Sorted)) {
    4034             if (animation->canBeListed())
     4034            if (animation->isRelevant())
    40354035                animations.append(animation);
    40364036        }
Note: See TracChangeset for help on using the changeset viewer.