e6dea9611f9b4880a5a19b9ad1e1bde71c184784
[WebKit-https.git] / Source / WebCore / animation / WebAnimation.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebAnimation.h"
28
29 #include "AnimationEffect.h"
30 #include "AnimationPlaybackEvent.h"
31 #include "AnimationTimeline.h"
32 #include "Document.h"
33 #include "DocumentTimeline.h"
34 #include "EventNames.h"
35 #include "JSWebAnimation.h"
36 #include "KeyframeEffect.h"
37 #include "Microtasks.h"
38 #include "WebAnimationUtilities.h"
39 #include <wtf/IsoMallocInlines.h>
40 #include <wtf/Optional.h>
41 #include <wtf/text/WTFString.h>
42
43 namespace WebCore {
44
45 WTF_MAKE_ISO_ALLOCATED_IMPL(WebAnimation);
46
47 Ref<WebAnimation> WebAnimation::create(Document& document, AnimationEffect* effect)
48 {
49     auto result = adoptRef(*new WebAnimation(document));
50     result->setEffect(effect);
51     result->setTimeline(&document.timeline());
52     return result;
53 }
54
55 Ref<WebAnimation> WebAnimation::create(Document& document, AnimationEffect* effect, AnimationTimeline* timeline)
56 {
57     auto result = adoptRef(*new WebAnimation(document));
58     result->setEffect(effect);
59     if (timeline)
60         result->setTimeline(timeline);
61     return result;
62 }
63
64 WebAnimation::WebAnimation(Document& document)
65     : ActiveDOMObject(document)
66     , m_readyPromise(makeUniqueRef<ReadyPromise>(*this, &WebAnimation::readyPromiseResolve))
67     , m_finishedPromise(makeUniqueRef<FinishedPromise>(*this, &WebAnimation::finishedPromiseResolve))
68 {
69     m_readyPromise->resolve(*this);
70     suspendIfNeeded();
71 }
72
73 WebAnimation::~WebAnimation()
74 {
75     if (m_timeline)
76         m_timeline->forgetAnimation(this);
77 }
78
79 void WebAnimation::remove()
80 {
81     // This object could be deleted after either clearing the effect or timeline relationship.
82     auto protectedThis = makeRef(*this);
83     setEffectInternal(nullptr);
84     setTimelineInternal(nullptr);
85 }
86
87 void WebAnimation::suspendEffectInvalidation()
88 {
89     ++m_suspendCount;
90 }
91
92 void WebAnimation::unsuspendEffectInvalidation()
93 {
94     ASSERT(m_suspendCount > 0);
95     --m_suspendCount;
96 }
97
98 void WebAnimation::effectTimingDidChange()
99 {
100     timingDidChange(DidSeek::No, SynchronouslyNotify::Yes);
101 }
102
103 void WebAnimation::setEffect(RefPtr<AnimationEffect>&& newEffect)
104 {
105     // 3.4.3. Setting the target effect of an animation
106     // https://drafts.csswg.org/web-animations-1/#setting-the-target-effect
107
108     // 1. Let old effect be the current target effect of animation, if any.
109     auto oldEffect = m_effect;
110
111     // 2. If new effect is the same object as old effect, abort this procedure.
112     if (newEffect == oldEffect)
113         return;
114
115     // 3. If animation has a pending pause task, reschedule that task to run as soon as animation is ready.
116     if (hasPendingPauseTask())
117         m_timeToRunPendingPauseTask = TimeToRunPendingTask::WhenReady;
118
119     // 4. If animation has a pending play task, reschedule that task to run as soon as animation is ready to play new effect.
120     if (hasPendingPlayTask())
121         m_timeToRunPendingPlayTask = TimeToRunPendingTask::WhenReady;
122
123     // 5. If new effect is not null and if new effect is the target effect of another animation, previous animation, run the
124     // procedure to set the target effect of an animation (this procedure) on previous animation passing null as new effect.
125     if (newEffect && newEffect->animation())
126         newEffect->animation()->setEffect(nullptr);
127
128     // 6. Let the target effect of animation be new effect.
129     // In the case of a declarative animation, we don't want to remove the animation from the relevant maps because
130     // while the effect was set via the API, the element still has a transition or animation set up and we must
131     // not break the timeline-to-animation relationship.
132
133     invalidateEffect();
134
135     // This object could be deleted after clearing the effect relationship.
136     auto protectedThis = makeRef(*this);
137     setEffectInternal(WTFMove(newEffect), isDeclarativeAnimation());
138
139     // 7. Run the procedure to update an animation's finished state for animation with the did seek flag set to false,
140     // and the synchronously notify flag set to false.
141     timingDidChange(DidSeek::No, SynchronouslyNotify::No);
142
143     invalidateEffect();
144 }
145
146 void WebAnimation::setEffectInternal(RefPtr<AnimationEffect>&& newEffect, bool doNotRemoveFromTimeline)
147 {
148     if (m_effect == newEffect)
149         return;
150
151     auto oldEffect = std::exchange(m_effect, WTFMove(newEffect));
152
153     Element* previousTarget = nullptr;
154     if (is<KeyframeEffect>(oldEffect))
155         previousTarget = downcast<KeyframeEffect>(oldEffect.get())->target();
156
157     Element* newTarget = nullptr;
158     if (is<KeyframeEffect>(m_effect))
159         newTarget = downcast<KeyframeEffect>(m_effect.get())->target();
160
161     // Update the effect-to-animation relationships and the timeline's animation map.
162     if (oldEffect) {
163         oldEffect->setAnimation(nullptr);
164         if (!doNotRemoveFromTimeline && m_timeline && previousTarget && previousTarget != newTarget)
165             m_timeline->animationWasRemovedFromElement(*this, *previousTarget);
166         updateRelevance();
167     }
168
169     if (m_effect) {
170         m_effect->setAnimation(this);
171         if (m_timeline && newTarget && previousTarget != newTarget)
172             m_timeline->animationWasAddedToElement(*this, *newTarget);
173     }
174 }
175
176 void WebAnimation::setTimeline(RefPtr<AnimationTimeline>&& timeline)
177 {
178     // 3.4.1. Setting the timeline of an animation
179     // https://drafts.csswg.org/web-animations-1/#setting-the-timeline
180
181     // 2. If new timeline is the same object as old timeline, abort this procedure.
182     if (timeline == m_timeline)
183         return;
184
185     // 4. If the animation start time of animation is resolved, make animation's hold time unresolved.
186     if (m_startTime)
187         m_holdTime = WTF::nullopt;
188
189     if (is<KeyframeEffect>(m_effect)) {
190         auto* keyframeEffect = downcast<KeyframeEffect>(m_effect.get());
191         auto* target = keyframeEffect->target();
192         if (target) {
193             // In the case of a declarative animation, we don't want to remove the animation from the relevant maps because
194             // while the timeline was set via the API, the element still has a transition or animation set up and we must
195             // not break the relationship.
196             if (m_timeline && !isDeclarativeAnimation())
197                 m_timeline->animationWasRemovedFromElement(*this, *target);
198             if (timeline)
199                 timeline->animationWasAddedToElement(*this, *target);
200         }
201     }
202
203     // This object could be deleted after clearing the timeline relationship.
204     auto protectedThis = makeRef(*this);
205     setTimelineInternal(WTFMove(timeline));
206
207     setSuspended(is<DocumentTimeline>(m_timeline) && downcast<DocumentTimeline>(*m_timeline).animationsAreSuspended());
208
209     // 5. Run the procedure to update an animation's finished state for animation with the did seek flag set to false,
210     // and the synchronously notify flag set to false.
211     timingDidChange(DidSeek::No, SynchronouslyNotify::No);
212
213     invalidateEffect();
214 }
215
216 void WebAnimation::setTimelineInternal(RefPtr<AnimationTimeline>&& timeline)
217 {
218     if (m_timeline == timeline)
219         return;
220
221     if (m_timeline)
222         m_timeline->removeAnimation(*this);
223
224     m_timeline = WTFMove(timeline);
225 }
226
227 void WebAnimation::effectTargetDidChange(Element* previousTarget, Element* newTarget)
228 {
229     if (!m_timeline)
230         return;
231
232     if (previousTarget)
233         m_timeline->animationWasRemovedFromElement(*this, *previousTarget);
234
235     if (newTarget)
236         m_timeline->animationWasAddedToElement(*this, *newTarget);
237 }
238
239 Optional<double> WebAnimation::startTime() const
240 {
241     if (!m_startTime)
242         return WTF::nullopt;
243     return secondsToWebAnimationsAPITime(m_startTime.value());
244 }
245
246 void WebAnimation::setStartTime(Optional<double> startTime)
247 {
248     // 3.4.6 The procedure to set the start time of animation, animation, to new start time, is as follows:
249     // https://drafts.csswg.org/web-animations/#setting-the-start-time-of-an-animation
250
251     Optional<Seconds> newStartTime;
252     if (!startTime)
253         newStartTime = WTF::nullopt;
254     else
255         newStartTime = Seconds::fromMilliseconds(startTime.value());
256
257     // 1. Let timeline time be the current time value of the timeline that animation is associated with. If
258     //    there is no timeline associated with animation or the associated timeline is inactive, let the timeline
259     //    time be unresolved.
260     auto timelineTime = m_timeline ? m_timeline->currentTime() : WTF::nullopt;
261
262     // 2. If timeline time is unresolved and new start time is resolved, make animation's hold time unresolved.
263     if (!timelineTime && newStartTime)
264         m_holdTime = WTF::nullopt;
265
266     // 3. Let previous current time be animation's current time.
267     auto previousCurrentTime = currentTime();
268
269     // 4. Apply any pending playback rate on animation.
270     applyPendingPlaybackRate();
271
272     // 5. Set animation's start time to new start time.
273     m_startTime = newStartTime;
274
275     // 6. Update animation's hold time based on the first matching condition from the following,
276     if (newStartTime) {
277         // If new start time is resolved,
278         // If animation's playback rate is not zero, make animation's hold time unresolved.
279         if (m_playbackRate)
280             m_holdTime = WTF::nullopt;
281     } else {
282         // Otherwise (new start time is unresolved),
283         // Set animation's hold time to previous current time even if previous current time is unresolved.
284         m_holdTime = previousCurrentTime;
285     }
286
287     // 7. If animation has a pending play task or a pending pause task, cancel that task and resolve animation's current ready promise with animation.
288     if (pending()) {
289         m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
290         m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
291         m_readyPromise->resolve(*this);
292     }
293
294     // 8. 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.
295     timingDidChange(DidSeek::Yes, SynchronouslyNotify::No);
296
297     invalidateEffect();
298 }
299
300 Optional<double> WebAnimation::bindingsCurrentTime() const
301 {
302     auto time = currentTime();
303     if (!time)
304         return WTF::nullopt;
305     return secondsToWebAnimationsAPITime(time.value());
306 }
307
308 ExceptionOr<void> WebAnimation::setBindingsCurrentTime(Optional<double> currentTime)
309 {
310     if (!currentTime)
311         return setCurrentTime(WTF::nullopt);
312     return setCurrentTime(Seconds::fromMilliseconds(currentTime.value()));
313 }
314
315 Optional<Seconds> WebAnimation::currentTime() const
316 {
317     return currentTime(RespectHoldTime::Yes);
318 }
319
320 Optional<Seconds> WebAnimation::currentTime(RespectHoldTime respectHoldTime) const
321 {
322     // 3.4.4. The current time of an animation
323     // https://drafts.csswg.org/web-animations-1/#the-current-time-of-an-animation
324
325     // The current time is calculated from the first matching condition from below:
326
327     // If the animation's hold time is resolved, the current time is the animation's hold time.
328     if (respectHoldTime == RespectHoldTime::Yes && m_holdTime)
329         return m_holdTime;
330
331     // If any of the following are true:
332     //     1. the animation has no associated timeline, or
333     //     2. the associated timeline is inactive, or
334     //     3. the animation's start time is unresolved.
335     // The current time is an unresolved time value.
336     if (!m_timeline || !m_timeline->currentTime() || !m_startTime)
337         return WTF::nullopt;
338
339     // Otherwise, current time = (timeline time - start time) * playback rate
340     return (m_timeline->currentTime().value() - m_startTime.value()) * m_playbackRate;
341 }
342
343 ExceptionOr<void> WebAnimation::silentlySetCurrentTime(Optional<Seconds> seekTime)
344 {
345     // 3.4.5. Setting the current time of an animation
346     // https://drafts.csswg.org/web-animations-1/#setting-the-current-time-of-an-animation
347
348     // 1. If seek time is an unresolved time value, then perform the following steps.
349     if (!seekTime) {
350         // 1. If the current time is resolved, then throw a TypeError.
351         if (currentTime())
352             return Exception { TypeError };
353         // 2. Abort these steps.
354         return { };
355     }
356
357     // 2. Update either animation's hold time or start time as follows:
358     // If any of the following conditions are true:
359     //     - animation's hold time is resolved, or
360     //     - animation's start time is unresolved, or
361     //     - animation has no associated timeline or the associated timeline is inactive, or
362     //     - animation's playback rate is 0,
363     // Set animation's hold time to seek time.
364     // Otherwise, set animation's start time to the result of evaluating timeline time - (seek time / playback rate)
365     // where timeline time is the current time value of timeline associated with animation.
366     if (m_holdTime || !m_startTime || !m_timeline || !m_timeline->currentTime() || !m_playbackRate)
367         m_holdTime = seekTime;
368     else
369         m_startTime = m_timeline->currentTime().value() - (seekTime.value() / m_playbackRate);
370
371     // 3. If animation has no associated timeline or the associated timeline is inactive, make animation's start time unresolved.
372     if (!m_timeline || !m_timeline->currentTime())
373         m_startTime = WTF::nullopt;
374
375     // 4. Make animation's previous current time unresolved.
376     m_previousCurrentTime = WTF::nullopt;
377
378     return { };
379 }
380
381 ExceptionOr<void> WebAnimation::setCurrentTime(Optional<Seconds> seekTime)
382 {
383     // 3.4.5. Setting the current time of an animation
384     // https://drafts.csswg.org/web-animations-1/#setting-the-current-time-of-an-animation
385
386     // 1. Run the steps to silently set the current time of animation to seek time.
387     auto silentResult = silentlySetCurrentTime(seekTime);
388     if (silentResult.hasException())
389         return silentResult.releaseException();
390
391     // 2. If animation has a pending pause task, synchronously complete the pause operation by performing the following steps:
392     if (hasPendingPauseTask()) {
393         // 1. Set animation's hold time to seek time.
394         m_holdTime = seekTime;
395         // 2. Apply any pending playback rate to animation.
396         applyPendingPlaybackRate();
397         // 3. Make animation's start time unresolved.
398         m_startTime = WTF::nullopt;
399         // 4. Cancel the pending pause task.
400         m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
401         // 5. Resolve animation's current ready promise with animation.
402         m_readyPromise->resolve(*this);
403     }
404
405     // 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.
406     timingDidChange(DidSeek::Yes, SynchronouslyNotify::No);
407
408     if (m_effect)
409         m_effect->animationDidSeek();
410
411     invalidateEffect();
412
413     return { };
414 }
415
416 double WebAnimation::effectivePlaybackRate() const
417 {
418     // https://drafts.csswg.org/web-animations/#effective-playback-rate
419     // The effective playback rate of an animation is its pending playback rate, if set, otherwise it is the animation's playback rate.
420     return (m_pendingPlaybackRate ? m_pendingPlaybackRate.value() : m_playbackRate);
421 }
422
423 void WebAnimation::setPlaybackRate(double newPlaybackRate)
424 {
425     // 3.4.17.1. Updating the playback rate of an animation
426     // https://drafts.csswg.org/web-animations-1/#updating-the-playback-rate-of-an-animation
427
428     // 1. Clear any pending playback rate on animation.
429     m_pendingPlaybackRate = WTF::nullopt;
430     
431     // 2. Let previous time be the value of the current time of animation before changing the playback rate.
432     auto previousTime = currentTime();
433
434     // 3. Set the playback rate to new playback rate.
435     m_playbackRate = newPlaybackRate;
436
437     // 4. If previous time is resolved, set the current time of animation to previous time.
438     if (previousTime)
439         setCurrentTime(previousTime);
440 }
441
442 void WebAnimation::updatePlaybackRate(double newPlaybackRate)
443 {
444     // https://drafts.csswg.org/web-animations/#seamlessly-update-the-playback-rate
445
446     // The procedure to seamlessly update the playback rate an animation, animation, to new playback rate preserving its current time is as follows:
447
448     // 1. Let previous play state be animation's play state.
449     //    Note: It is necessary to record the play state before updating animation's effective playback rate since, in the following logic,
450     //    we want to immediately apply the pending playback rate of animation if it is currently finished regardless of whether or not it will
451     //    still be finished after we apply the pending playback rate.
452     auto previousPlayState = playState();
453
454     // 2. Let animation's pending playback rate be new playback rate.
455     m_pendingPlaybackRate = newPlaybackRate;
456
457     // 3. Perform the steps corresponding to the first matching condition from below:
458     if (pending()) {
459         // If animation has a pending play task or a pending pause task,
460         // Abort these steps.
461         // Note: The different types of pending tasks will apply the pending playback rate when they run so there is no further action required in this case.
462         return;
463     }
464
465     if (previousPlayState == PlayState::Idle || previousPlayState == PlayState::Paused) {
466         // If previous play state is idle or paused,
467         // Apply any pending playback rate on animation.
468         applyPendingPlaybackRate();
469     } else if (previousPlayState == PlayState::Finished) {
470         // If previous play state is finished,
471         // 1. Let the unconstrained current time be the result of calculating the current time of animation substituting an unresolved time value for the hold time.
472         auto unconstrainedCurrentTime = currentTime(RespectHoldTime::No);
473         // 2. Let animation's start time be the result of evaluating the following expression:
474         // timeline time - (unconstrained current time / pending playback rate)
475         // Where timeline time is the current time value of the timeline associated with animation.
476         // If pending playback rate is zero, let animation's start time be timeline time.
477         auto newStartTime = m_timeline->currentTime().value();
478         if (m_pendingPlaybackRate)
479             newStartTime -= (unconstrainedCurrentTime.value() / m_pendingPlaybackRate.value());
480         m_startTime = newStartTime;
481         // 3. Apply any pending playback rate on animation.
482         applyPendingPlaybackRate();
483         // 4. 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.
484         timingDidChange(DidSeek::No, SynchronouslyNotify::No);
485
486         invalidateEffect();
487     } else {
488         // Otherwise,
489         // Run the procedure to play an animation for animation with the auto-rewind flag set to false.
490         play(AutoRewind::No);
491     }
492 }
493
494 void WebAnimation::applyPendingPlaybackRate()
495 {
496     // https://drafts.csswg.org/web-animations/#apply-any-pending-playback-rate
497
498     // 1. If animation does not have a pending playback rate, abort these steps.
499     if (!m_pendingPlaybackRate)
500         return;
501
502     // 2. Set animation's playback rate to its pending playback rate.
503     m_playbackRate = m_pendingPlaybackRate.value();
504
505     // 3. Clear animation's pending playback rate.
506     m_pendingPlaybackRate = WTF::nullopt;
507 }
508
509 auto WebAnimation::playState() const -> PlayState
510 {
511     // 3.5.19 Play states
512     // https://drafts.csswg.org/web-animations/#play-states
513
514     // The play state of animation, animation, at a given moment is the state corresponding to the
515     // first matching condition from the following:
516
517     // The current time of animation is unresolved, and animation does not have either a pending
518     // play task or a pending pause task,
519     // → idle
520     auto animationCurrentTime = currentTime();
521     if (!animationCurrentTime && !pending())
522         return PlayState::Idle;
523
524     // Animation has a pending pause task, or both the start time of animation is unresolved and it does not
525     // have a pending play task,
526     // → paused
527     if (hasPendingPauseTask() || (!m_startTime && !hasPendingPlayTask()))
528         return PlayState::Paused;
529
530     // For animation, current time is resolved and either of the following conditions are true:
531     // animation's effective playback rate > 0 and current time ≥ target effect end; or
532     // animation's effective playback rate < 0 and current time ≤ 0,
533     // → finished
534     if (animationCurrentTime && ((effectivePlaybackRate() > 0 && (*animationCurrentTime + timeEpsilon) >= effectEndTime()) || (effectivePlaybackRate() < 0 && (*animationCurrentTime - timeEpsilon) <= 0_s)))
535         return PlayState::Finished;
536
537     // Otherwise → running
538     return PlayState::Running;
539 }
540
541 Seconds WebAnimation::effectEndTime() const
542 {
543     // The target effect end of an animation is equal to the end time of the animation's target effect.
544     // If the animation has no target effect, the target effect end is zero.
545     return m_effect ? m_effect->getBasicTiming().endTime : 0_s;
546 }
547
548 void WebAnimation::cancel()
549 {
550     cancel(Silently::No);
551     invalidateEffect();
552 }
553
554 void WebAnimation::cancel(Silently silently)
555 {
556     // 3.4.16. Canceling an animation
557     // https://drafts.csswg.org/web-animations-1/#canceling-an-animation-section
558     //
559     // An animation can be canceled which causes the current time to become unresolved hence removing any effects caused by the target effect.
560     //
561     // The procedure to cancel an animation for animation is as follows:
562     //
563     // 1. If animation's play state is not idle, perform the following steps:
564     if (playState() != PlayState::Idle) {
565         // 1. Run the procedure to reset an animation's pending tasks on animation.
566         resetPendingTasks(silently);
567
568         // 2. Reject the current finished promise with a DOMException named "AbortError".
569         if (silently == Silently::No && !m_finishedPromise->isFulfilled())
570             m_finishedPromise->reject(Exception { AbortError });
571
572         // 3. Let current finished promise be a new (pending) Promise object.
573         m_finishedPromise = makeUniqueRef<FinishedPromise>(*this, &WebAnimation::finishedPromiseResolve);
574
575         // 4. Create an AnimationPlaybackEvent, cancelEvent.
576         // 5. Set cancelEvent's type attribute to cancel.
577         // 6. Set cancelEvent's currentTime to null.
578         // 7. Let timeline time be the current time of the timeline with which animation is associated. If animation is not associated with an
579         //    active timeline, let timeline time be n unresolved time value.
580         // 8. Set cancelEvent's timelineTime to timeline time. If timeline time is unresolved, set it to null.
581         // 9. If animation has a document for timing, then append cancelEvent to its document for timing's pending animation event queue along
582         //    with its target, animation. If animation is associated with an active timeline that defines a procedure to convert timeline times
583         //    to origin-relative time, let the scheduled event time be the result of applying that procedure to timeline time. Otherwise, the
584         //    scheduled event time is an unresolved time value.
585         // Otherwise, queue a task to dispatch cancelEvent at animation. The task source for this task is the DOM manipulation task source.
586         if (silently == Silently::No)
587             enqueueAnimationPlaybackEvent(eventNames().cancelEvent, WTF::nullopt, m_timeline ? m_timeline->currentTime() : WTF::nullopt);
588     }
589
590     // 2. Make animation's hold time unresolved.
591     m_holdTime = WTF::nullopt;
592
593     // 3. Make animation's start time unresolved.
594     m_startTime = WTF::nullopt;
595
596     timingDidChange(DidSeek::No, SynchronouslyNotify::No);
597
598     invalidateEffect();
599 }
600
601 void WebAnimation::enqueueAnimationPlaybackEvent(const AtomString& type, Optional<Seconds> currentTime, Optional<Seconds> timelineTime)
602 {
603     auto event = AnimationPlaybackEvent::create(type, currentTime, timelineTime);
604     event->setTarget(this);
605
606     if (is<DocumentTimeline>(m_timeline)) {
607         // If animation has a document for timing, then append event to its document for timing's pending animation event queue along
608         // with its target, animation. If animation is associated with an active timeline that defines a procedure to convert timeline times
609         // to origin-relative time, let the scheduled event time be the result of applying that procedure to timeline time. Otherwise, the
610         // scheduled event time is an unresolved time value.
611         downcast<DocumentTimeline>(*m_timeline).enqueueAnimationPlaybackEvent(WTFMove(event));
612     } else {
613         // Otherwise, queue a task to dispatch event at animation. The task source for this task is the DOM manipulation task source.
614         callOnMainThread([this, pendingActivity = makePendingActivity(*this), event = WTFMove(event)]() {
615             if (!m_isStopped)
616                 this->dispatchEvent(event);
617         });
618     }
619 }
620
621 void WebAnimation::resetPendingTasks(Silently silently)
622 {
623     // The procedure to reset an animation's pending tasks for animation is as follows:
624     // https://drafts.csswg.org/web-animations-1/#reset-an-animations-pending-tasks
625     //
626     // 1. If animation does not have a pending play task or a pending pause task, abort this procedure.
627     if (!pending())
628         return;
629
630     // 2. If animation has a pending play task, cancel that task.
631     if (hasPendingPlayTask())
632         m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
633
634     // 3. If animation has a pending pause task, cancel that task.
635     if (hasPendingPauseTask())
636         m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
637
638     // 4. Apply any pending playback rate on animation.
639     applyPendingPlaybackRate();
640
641     // 5. Reject animation's current ready promise with a DOMException named "AbortError".
642     if (silently == Silently::No)
643         m_readyPromise->reject(Exception { AbortError });
644
645     // 6. Let animation's current ready promise be the result of creating a new resolved Promise object.
646     m_readyPromise = makeUniqueRef<ReadyPromise>(*this, &WebAnimation::readyPromiseResolve);
647     m_readyPromise->resolve(*this);
648 }
649
650 ExceptionOr<void> WebAnimation::finish()
651 {
652     // 3.4.15. Finishing an animation
653     // https://drafts.csswg.org/web-animations-1/#finishing-an-animation-section
654
655     // An animation can be advanced to the natural end of its current playback direction by using the procedure to finish an animation for animation defined below:
656     //
657     // 1. If animation's effective playback rate is zero, or if animation's effective playback rate > 0 and target effect end is infinity, throw an InvalidStateError and abort these steps.
658     if (!effectivePlaybackRate() || (effectivePlaybackRate() > 0 && effectEndTime() == Seconds::infinity()))
659         return Exception { InvalidStateError };
660
661     // 2. Apply any pending playback rate to animation.
662     applyPendingPlaybackRate();
663
664     // 3. Set limit as follows:
665     // If animation playback rate > 0, let limit be target effect end.
666     // Otherwise, let limit be zero.
667     auto limit = m_playbackRate > 0 ? effectEndTime() : 0_s;
668
669     // 4. Silently set the current time to limit.
670     silentlySetCurrentTime(limit);
671
672     // 5. If animation's start time is unresolved and animation has an associated active timeline, let the start time be the result of
673     //    evaluating timeline time - (limit / playback rate) where timeline time is the current time value of the associated timeline.
674     if (!m_startTime && m_timeline && m_timeline->currentTime())
675         m_startTime = m_timeline->currentTime().value() - (limit / m_playbackRate);
676
677     // 6. If there is a pending pause task and start time is resolved,
678     if (hasPendingPauseTask() && m_startTime) {
679         // 1. Let the hold time be unresolved.
680         m_holdTime = WTF::nullopt;
681         // 2. Cancel the pending pause task.
682         m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
683         // 3. Resolve the current ready promise of animation with animation.
684         m_readyPromise->resolve(*this);
685     }
686
687     // 7. 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.
688     if (hasPendingPlayTask() && m_startTime) {
689         m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
690         m_readyPromise->resolve(*this);
691     }
692
693     // 8. 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.
694     timingDidChange(DidSeek::Yes, SynchronouslyNotify::Yes);
695
696     invalidateEffect();
697
698     return { };
699 }
700
701 void WebAnimation::timingDidChange(DidSeek didSeek, SynchronouslyNotify synchronouslyNotify)
702 {
703     m_shouldSkipUpdatingFinishedStateWhenResolving = false;
704     updateFinishedState(didSeek, synchronouslyNotify);
705     if (m_timeline)
706         m_timeline->animationTimingDidChange(*this);
707 };
708
709 void WebAnimation::invalidateEffect()
710 {
711     if (!isEffectInvalidationSuspended() && m_effect)
712         m_effect->invalidate();
713 }
714
715 void WebAnimation::updateFinishedState(DidSeek didSeek, SynchronouslyNotify synchronouslyNotify)
716 {
717     // 3.4.14. Updating the finished state
718     // https://drafts.csswg.org/web-animations-1/#updating-the-finished-state
719
720     // 1. Let the unconstrained current time be the result of calculating the current time substituting an unresolved time value
721     // for the hold time if did seek is false. If did seek is true, the unconstrained current time is equal to the current time.
722     auto unconstrainedCurrentTime = currentTime(didSeek == DidSeek::Yes ? RespectHoldTime::Yes : RespectHoldTime::No);
723     auto endTime = effectEndTime();
724
725     // 2. If all three of the following conditions are true,
726     //    - the unconstrained current time is resolved, and
727     //    - animation's start time is resolved, and
728     //    - animation does not have a pending play task or a pending pause task,
729     if (unconstrainedCurrentTime && m_startTime && !pending()) {
730         // then update animation's hold time based on the first matching condition for animation from below, if any:
731         if (m_playbackRate > 0 && unconstrainedCurrentTime >= endTime) {
732             // If animation playback rate > 0 and unconstrained current time is greater than or equal to target effect end,
733             // If did seek is true, let the hold time be the value of unconstrained current time.
734             if (didSeek == DidSeek::Yes)
735                 m_holdTime = unconstrainedCurrentTime;
736             // 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.
737             else if (!m_previousCurrentTime)
738                 m_holdTime = endTime;
739             else
740                 m_holdTime = std::max(m_previousCurrentTime.value(), endTime);
741         } else if (m_playbackRate < 0 && unconstrainedCurrentTime <= 0_s) {
742             // If animation playback rate < 0 and unconstrained current time is less than or equal to 0,
743             // If did seek is true, let the hold time be the value of unconstrained current time.
744             if (didSeek == DidSeek::Yes)
745                 m_holdTime = unconstrainedCurrentTime;
746             // 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.
747             else if (!m_previousCurrentTime)
748                 m_holdTime = 0_s;
749             else
750                 m_holdTime = std::min(m_previousCurrentTime.value(), 0_s);
751         } else if (m_playbackRate && m_timeline && m_timeline->currentTime()) {
752             // If animation playback rate ≠ 0, and animation is associated with an active timeline,
753             // Perform the following steps:
754             // 1. If did seek is true and the hold time is resolved, let animation's start time be equal to the result of evaluating timeline time - (hold time / playback rate)
755             //    where timeline time is the current time value of timeline associated with animation.
756             if (didSeek == DidSeek::Yes && m_holdTime)
757                 m_startTime = m_timeline->currentTime().value() - (m_holdTime.value() / m_playbackRate);
758             // 2. Let the hold time be unresolved.
759             m_holdTime = WTF::nullopt;
760         }
761     }
762
763     // 3. Set the previous current time of animation be the result of calculating its current time.
764     m_previousCurrentTime = currentTime();
765
766     // 4. Let current finished state be true if the play state of animation is finished. Otherwise, let it be false.
767     auto currentFinishedState = playState() == PlayState::Finished;
768
769     // 5. If current finished state is true and the current finished promise is not yet resolved, perform the following steps:
770     if (currentFinishedState && !m_finishedPromise->isFulfilled()) {
771         if (synchronouslyNotify == SynchronouslyNotify::Yes) {
772             // If synchronously notify is true, cancel any queued microtask to run the finish notification steps for this animation,
773             // and run the finish notification steps immediately.
774             m_finishNotificationStepsMicrotaskPending = false;
775             finishNotificationSteps();
776         } else if (!m_finishNotificationStepsMicrotaskPending) {
777             // Otherwise, if synchronously notify is false, queue a microtask to run finish notification steps for animation unless there
778             // is already a microtask queued to run those steps for animation.
779             m_finishNotificationStepsMicrotaskPending = true;
780             MicrotaskQueue::mainThreadQueue().append(makeUnique<VoidMicrotask>([this, protectedThis = makeRef(*this)] () {
781                 if (m_finishNotificationStepsMicrotaskPending) {
782                     m_finishNotificationStepsMicrotaskPending = false;
783                     finishNotificationSteps();
784                 }
785             }));
786         }
787     }
788
789     // 6. If current finished state is false and animation's current finished promise is already resolved, set animation's current
790     // finished promise to a new (pending) Promise object.
791     if (!currentFinishedState && m_finishedPromise->isFulfilled())
792         m_finishedPromise = makeUniqueRef<FinishedPromise>(*this, &WebAnimation::finishedPromiseResolve);
793
794     updateRelevance();
795 }
796
797 void WebAnimation::finishNotificationSteps()
798 {
799     // 3.4.14. Updating the finished state
800     // https://drafts.csswg.org/web-animations-1/#finish-notification-steps
801
802     // Let finish notification steps refer to the following procedure:
803     // 1. If animation's play state is not equal to finished, abort these steps.
804     if (playState() != PlayState::Finished)
805         return;
806
807     // 2. Resolve animation's current finished promise object with animation.
808     m_finishedPromise->resolve(*this);
809
810     // 3. Create an AnimationPlaybackEvent, finishEvent.
811     // 4. Set finishEvent's type attribute to finish.
812     // 5. Set finishEvent's currentTime attribute to the current time of animation.
813     // 6. Set finishEvent's timelineTime attribute to the current time of the timeline with which animation is associated.
814     //    If animation is not associated with a timeline, or the timeline is inactive, let timelineTime be null.
815     // 7. If animation has a document for timing, then append finishEvent to its document for timing's pending animation event
816     //    queue along with its target, animation. For the scheduled event time, use the result of converting animation's target
817     //    effect end to an origin-relative time.
818     //    Otherwise, queue a task to dispatch finishEvent at animation. The task source for this task is the DOM manipulation task source.
819     enqueueAnimationPlaybackEvent(eventNames().finishEvent, currentTime(), m_timeline ? m_timeline->currentTime() : WTF::nullopt);
820 }
821
822 ExceptionOr<void> WebAnimation::play()
823 {
824     return play(AutoRewind::Yes);
825 }
826
827 ExceptionOr<void> WebAnimation::play(AutoRewind autoRewind)
828 {
829     // 3.4.10. Playing an animation
830     // https://drafts.csswg.org/web-animations-1/#play-an-animation
831
832     auto localTime = currentTime();
833     auto endTime = effectEndTime();
834
835     // 1. Let aborted pause be a boolean flag that is true if animation has a pending pause task, and false otherwise.
836     bool abortedPause = hasPendingPauseTask();
837
838     // 2. Let has pending ready promise be a boolean flag that is initially false.
839     bool hasPendingReadyPromise = false;
840
841     // 3. Perform the steps corresponding to the first matching condition from the following, if any:
842     if (effectivePlaybackRate() > 0 && autoRewind == AutoRewind::Yes && (!localTime || localTime.value() < 0_s || localTime.value() >= endTime)) {
843         // If animation's effective playback rate > 0, the auto-rewind flag is true and either animation's:
844         //     - current time is unresolved, or
845         //     - current time < zero, or
846         //     - current time ≥ target effect end,
847         // Set animation's hold time to zero.
848         m_holdTime = 0_s;
849     } else if (effectivePlaybackRate() < 0 && autoRewind == AutoRewind::Yes && (!localTime || localTime.value() <= 0_s || localTime.value() > endTime)) {
850         // If animation's effective playback rate < 0, the auto-rewind flag is true and either animation's:
851         //     - current time is unresolved, or
852         //     - current time ≤ zero, or
853         //     - current time > target effect end
854         // If target effect end is positive infinity, throw an InvalidStateError and abort these steps.
855         if (endTime == Seconds::infinity())
856             return Exception { InvalidStateError };
857         m_holdTime = endTime;
858     } else if (!effectivePlaybackRate() && !localTime) {
859         // If animation's effective playback rate = 0 and animation's current time is unresolved,
860         // Set animation's hold time to zero.
861         m_holdTime = 0_s;
862     }
863
864     // 4. If animation has a pending play task or a pending pause task,
865     if (pending()) {
866         // 1. Cancel that task.
867         m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
868         m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
869         // 2. Set has pending ready promise to true.
870         hasPendingReadyPromise = true;
871     }
872
873     // 5. If the following three conditions are all satisfied:
874     //    - animation's hold time is unresolved, and
875     //    - aborted pause is false, and
876     //    - animation does not have a pending playback rate,
877     // abort this procedure.
878     if (!m_holdTime && !abortedPause && !m_pendingPlaybackRate)
879         return { };
880
881     // 6. If animation's hold time is resolved, let its start time be unresolved.
882     if (m_holdTime)
883         m_startTime = WTF::nullopt;
884
885     // 7. If has pending ready promise is false, let animation's current ready promise be a new (pending) Promise object.
886     if (!hasPendingReadyPromise)
887         m_readyPromise = makeUniqueRef<ReadyPromise>(*this, &WebAnimation::readyPromiseResolve);
888
889     // 8. Schedule a task to run as soon as animation is ready.
890     m_timeToRunPendingPlayTask = TimeToRunPendingTask::WhenReady;
891
892     // 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.
893     timingDidChange(DidSeek::No, SynchronouslyNotify::No);
894
895     invalidateEffect();
896
897     return { };
898 }
899
900 void WebAnimation::runPendingPlayTask()
901 {
902     // 3.4.10. Playing an animation, step 8.
903     // https://drafts.csswg.org/web-animations-1/#play-an-animation
904
905     m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
906
907     // 1. Assert that at least one of animation's start time or hold time is resolved.
908     ASSERT(m_startTime || m_holdTime);
909
910     // 2. Let ready time be the time value of the timeline associated with animation at the moment when animation became ready.
911     auto readyTime = m_timeline->currentTime();
912
913     // 3. Perform the steps corresponding to the first matching condition below, if any:
914     if (m_holdTime) {
915         // If animation's hold time is resolved,
916         // 1. Apply any pending playback rate on animation.
917         applyPendingPlaybackRate();
918         // 2. Let new start time be the result of evaluating ready time - hold time / animation playback rate for animation.
919         // If the animation playback rate is zero, let new start time be simply ready time.
920         // FIXME: Implementation cannot guarantee an active timeline at the point of this async dispatch.
921         // Subsequently, the resulting readyTime value can be null. Unify behavior between C++17 and
922         // C++14 builds (the latter using WTF's Optional) and avoid null Optional dereferencing
923         // by defaulting to a Seconds(0) value. See https://bugs.webkit.org/show_bug.cgi?id=186189.
924         auto newStartTime = readyTime.valueOr(0_s);
925         if (m_playbackRate)
926             newStartTime -= m_holdTime.value() / m_playbackRate;
927         // 3. Set the start time of animation to new start time.
928         m_startTime = newStartTime;
929         // 4. If animation's playback rate is not 0, make animation's hold time unresolved.
930         if (m_playbackRate)
931             m_holdTime = WTF::nullopt;
932     } else if (m_startTime && m_pendingPlaybackRate) {
933         // If animation's start time is resolved and animation has a pending playback rate,
934         // 1. Let current time to match be the result of evaluating (ready time - start time) × playback rate for animation.
935         auto currentTimeToMatch = (readyTime.valueOr(0_s) - m_startTime.value()) * m_playbackRate;
936         // 2. Apply any pending playback rate on animation.
937         applyPendingPlaybackRate();
938         // 3. If animation's playback rate is zero, let animation's hold time be current time to match.
939         if (m_playbackRate)
940             m_holdTime = currentTimeToMatch;
941         // 4. Let new start time be the result of evaluating ready time - current time to match / playback rate for animation.
942         // If the playback rate is zero, let new start time be simply ready time.
943         auto newStartTime = readyTime.valueOr(0_s);
944         if (m_playbackRate)
945             newStartTime -= currentTimeToMatch / m_playbackRate;
946         // 5. Set the start time of animation to new start time.
947         m_startTime = newStartTime;
948     }
949
950     // 4. Resolve animation's current ready promise with animation.
951     if (!m_readyPromise->isFulfilled())
952         m_readyPromise->resolve(*this);
953
954     // 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.
955     timingDidChange(DidSeek::No, SynchronouslyNotify::No);
956
957     invalidateEffect();
958 }
959
960 ExceptionOr<void> WebAnimation::pause()
961 {
962     // 3.4.11. Pausing an animation
963     // https://drafts.csswg.org/web-animations-1/#pause-an-animation
964
965     // 1. If animation has a pending pause task, abort these steps.
966     if (hasPendingPauseTask())
967         return { };
968
969     // 2. If the play state of animation is paused, abort these steps.
970     if (playState() == PlayState::Paused)
971         return { };
972
973     auto localTime = currentTime();
974
975     // 3. If the animation's current time is unresolved, perform the steps according to the first matching condition from below:
976     if (!localTime) {
977         if (m_playbackRate >= 0) {
978             // If animation's playback rate is ≥ 0, let animation's hold time be zero.
979             m_holdTime = 0_s;
980         } else if (effectEndTime() == Seconds::infinity()) {
981             // Otherwise, if target effect end for animation is positive infinity, throw an InvalidStateError and abort these steps.
982             return Exception { InvalidStateError };
983         } else {
984             // Otherwise, let animation's hold time be target effect end.
985             m_holdTime = effectEndTime();
986         }
987     }
988
989     // 4. Let has pending ready promise be a boolean flag that is initially false.
990     bool hasPendingReadyPromise = false;
991
992     // 5. If animation has a pending play task, cancel that task and let has pending ready promise be true.
993     if (hasPendingPlayTask()) {
994         m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
995         hasPendingReadyPromise = true;
996     }
997
998     // 6. If has pending ready promise is false, set animation's current ready promise to a new (pending) Promise object.
999     if (!hasPendingReadyPromise)
1000         m_readyPromise = makeUniqueRef<ReadyPromise>(*this, &WebAnimation::readyPromiseResolve);
1001
1002     // 7. Schedule a task to be executed at the first possible moment after the user agent has performed any processing necessary
1003     //    to suspend the playback of animation's target effect, if any.
1004     m_timeToRunPendingPauseTask = TimeToRunPendingTask::ASAP;
1005
1006     // 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.
1007     timingDidChange(DidSeek::No, SynchronouslyNotify::No);
1008
1009     invalidateEffect();
1010
1011     return { };
1012 }
1013
1014 ExceptionOr<void> WebAnimation::reverse()
1015 {
1016     // 3.4.18. Reversing an animation
1017     // https://drafts.csswg.org/web-animations-1/#reverse-an-animation
1018
1019     // The procedure to reverse an animation of animation animation is as follows:
1020
1021     // 1. If there is no timeline associated with animation, or the associated timeline is inactive
1022     //    throw an InvalidStateError and abort these steps.
1023     if (!m_timeline || !m_timeline->currentTime())
1024         return Exception { InvalidStateError };
1025
1026     // 2. Let original pending playback rate be animation's pending playback rate.
1027     auto originalPendingPlaybackRate = m_pendingPlaybackRate;
1028
1029     // 3. Let animation's pending playback rate be the additive inverse of its effective playback rate (i.e. -effective playback rate).
1030     m_pendingPlaybackRate = -effectivePlaybackRate();
1031
1032     // 4. Run the steps to play an animation for animation with the auto-rewind flag set to true.
1033     auto playResult = play(AutoRewind::Yes);
1034
1035     // If the steps to play an animation throw an exception, set animation's pending playback rate to original
1036     // pending playback rate and propagate the exception.
1037     if (playResult.hasException()) {
1038         m_pendingPlaybackRate = originalPendingPlaybackRate;
1039         return playResult.releaseException();
1040     }
1041
1042     return { };
1043 }
1044
1045 void WebAnimation::runPendingPauseTask()
1046 {
1047     // 3.4.11. Pausing an animation, step 7.
1048     // https://drafts.csswg.org/web-animations-1/#pause-an-animation
1049
1050     m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
1051
1052     // 1. Let ready time be the time value of the timeline associated with animation at the moment when the user agent
1053     //    completed processing necessary to suspend playback of animation's target effect.
1054     auto readyTime = m_timeline->currentTime();
1055     auto animationStartTime = m_startTime;
1056
1057     // 2. If animation's start time is resolved and its hold time is not resolved, let animation's hold time be the result of
1058     //    evaluating (ready time - start time) × playback rate.
1059     //    Note: The hold time might be already set if the animation is finished, or if the animation is pending, waiting to begin
1060     //    playback. In either case we want to preserve the hold time as we enter the paused state.
1061     if (animationStartTime && !m_holdTime) {
1062         // FIXME: Implementation cannot guarantee an active timeline at the point of this async dispatch.
1063         // Subsequently, the resulting readyTime value can be null. Unify behavior between C++17 and
1064         // C++14 builds (the latter using WTF's Optional) and avoid null Optional dereferencing
1065         // by defaulting to a Seconds(0) value. See https://bugs.webkit.org/show_bug.cgi?id=186189.
1066         m_holdTime = (readyTime.valueOr(0_s) - animationStartTime.value()) * m_playbackRate;
1067     }
1068
1069     // 3. Apply any pending playback rate on animation.
1070     applyPendingPlaybackRate();
1071
1072     // 4. Make animation's start time unresolved.
1073     m_startTime = WTF::nullopt;
1074
1075     // 5. Resolve animation's current ready promise with animation.
1076     if (!m_readyPromise->isFulfilled())
1077         m_readyPromise->resolve(*this);
1078
1079     // 6. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the
1080     //    synchronously notify flag set to false.
1081     timingDidChange(DidSeek::No, SynchronouslyNotify::No);
1082
1083     invalidateEffect();
1084 }
1085
1086 bool WebAnimation::isRunningAccelerated() const
1087 {
1088     return is<KeyframeEffect>(m_effect) && downcast<KeyframeEffect>(*m_effect).isRunningAccelerated();
1089 }
1090
1091 bool WebAnimation::needsTick() const
1092 {
1093     return pending() || playState() == PlayState::Running;
1094 }
1095
1096 void WebAnimation::tick()
1097 {
1098     updateFinishedState(DidSeek::No, SynchronouslyNotify::Yes);
1099     m_shouldSkipUpdatingFinishedStateWhenResolving = true;
1100
1101     // Run pending tasks, if any.
1102     if (hasPendingPauseTask())
1103         runPendingPauseTask();
1104     if (hasPendingPlayTask())
1105         runPendingPlayTask();
1106
1107     invalidateEffect();
1108 }
1109
1110 void WebAnimation::resolve(RenderStyle& targetStyle)
1111 {
1112     if (!m_shouldSkipUpdatingFinishedStateWhenResolving)
1113         updateFinishedState(DidSeek::No, SynchronouslyNotify::Yes);
1114     m_shouldSkipUpdatingFinishedStateWhenResolving = false;
1115
1116     if (m_effect)
1117         m_effect->apply(targetStyle);
1118 }
1119
1120 void WebAnimation::setSuspended(bool isSuspended)
1121 {
1122     if (m_isSuspended == isSuspended)
1123         return;
1124
1125     m_isSuspended = isSuspended;
1126
1127     if (m_effect && playState() == PlayState::Running)
1128         m_effect->animationSuspensionStateDidChange(isSuspended);
1129 }
1130
1131 void WebAnimation::acceleratedStateDidChange()
1132 {
1133     if (is<DocumentTimeline>(m_timeline))
1134         downcast<DocumentTimeline>(*m_timeline).animationAcceleratedRunningStateDidChange(*this);
1135 }
1136
1137 void WebAnimation::applyPendingAcceleratedActions()
1138 {
1139     if (is<KeyframeEffect>(m_effect))
1140         downcast<KeyframeEffect>(*m_effect).applyPendingAcceleratedActions();
1141 }
1142
1143 WebAnimation& WebAnimation::readyPromiseResolve()
1144 {
1145     return *this;
1146 }
1147
1148 WebAnimation& WebAnimation::finishedPromiseResolve()
1149 {
1150     return *this;
1151 }
1152
1153 const char* WebAnimation::activeDOMObjectName() const
1154 {
1155     return "Animation";
1156 }
1157
1158 bool WebAnimation::canSuspendForDocumentSuspension() const
1159 {
1160     // Use the base class's implementation of hasPendingActivity() since we wouldn't want the custom implementation
1161     // in this class designed to keep JS wrappers alive to interfere with the ability for a page using animations
1162     // to enter the page cache.
1163     return !ActiveDOMObject::hasPendingActivity();
1164 }
1165
1166 void WebAnimation::stop()
1167 {
1168     ActiveDOMObject::stop();
1169     m_isStopped = true;
1170     removeAllEventListeners();
1171 }
1172
1173 bool WebAnimation::hasPendingActivity() const
1174 {
1175     // Keep the JS wrapper alive if the animation is considered relevant or could become relevant again by virtue of having a timeline.
1176     return m_timeline || m_isRelevant || ActiveDOMObject::hasPendingActivity();
1177 }
1178
1179 void WebAnimation::updateRelevance()
1180 {
1181     m_isRelevant = computeRelevance();
1182 }
1183
1184 bool WebAnimation::computeRelevance()
1185 {
1186     // To be listed in getAnimations() an animation needs a target effect which is current or in effect.
1187     if (!m_effect)
1188         return false;
1189
1190     auto timing = m_effect->getBasicTiming();
1191
1192     // An animation effect is in effect if its active time is not unresolved.
1193     if (timing.activeTime)
1194         return true;
1195
1196     // An animation effect is current if either of the following conditions is true:
1197     // - the animation effect is in the before phase, or
1198     // - the animation effect is in play.
1199
1200     // An animation effect is in play if all of the following conditions are met:
1201     // - the animation effect is in the active phase, and
1202     // - the animation effect is associated with an animation that is not finished.
1203     return timing.phase == AnimationEffectPhase::Before || (timing.phase == AnimationEffectPhase::Active && playState() != PlayState::Finished);
1204 }
1205
1206 Seconds WebAnimation::timeToNextTick() const
1207 {
1208     ASSERT(isRunningAccelerated());
1209
1210     if (pending())
1211         return 0_s;
1212
1213     // If we're not running, there's no telling when we'll end.
1214     if (playState() != PlayState::Running)
1215         return Seconds::infinity();
1216
1217     // CSS Animations dispatch events for each iteration, so compute the time until
1218     // the end of this iteration. Any other animation only cares about remaning total time.
1219     if (isCSSAnimation()) {
1220         auto* animationEffect = effect();
1221         auto timing = animationEffect->getComputedTiming();
1222         // If we're actively running, we need the time until the next iteration.
1223         if (auto iterationProgress = timing.simpleIterationProgress)
1224             return animationEffect->iterationDuration() * (1 - *iterationProgress);
1225
1226         // Otherwise we're probably in the before phase waiting to reach our start time.
1227         if (auto animationCurrentTime = currentTime()) {
1228             // If our current time is negative, we need to be scheduled to be resolved at the inverse
1229             // of our current time, unless we fill backwards, in which case we want to invalidate as
1230             // soon as possible.
1231             auto localTime = animationCurrentTime.value();
1232             if (localTime < 0_s)
1233                 return -localTime;
1234             if (localTime < animationEffect->delay())
1235                 return animationEffect->delay() - localTime;
1236         }
1237     } else if (auto animationCurrentTime = currentTime())
1238         return effect()->getBasicTiming().endTime - *animationCurrentTime;
1239
1240     ASSERT_NOT_REACHED();
1241     return Seconds::infinity();
1242 }
1243
1244 } // namespace WebCore