[Web Animations] Make WPT test at interfaces/Animation/finish.html pass reliably
[WebKit-https.git] / Source / WebCore / animation / DocumentTimeline.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 "DocumentTimeline.h"
28
29 #include "AnimationPlaybackEvent.h"
30 #include "DOMWindow.h"
31 #include "DeclarativeAnimation.h"
32 #include "Document.h"
33 #include "KeyframeEffect.h"
34 #include "Microtasks.h"
35 #include "Page.h"
36 #include "RenderElement.h"
37
38 static const Seconds defaultAnimationInterval { 15_ms };
39 static const Seconds throttledAnimationInterval { 30_ms };
40
41 namespace WebCore {
42
43 Ref<DocumentTimeline> DocumentTimeline::create(Document& document)
44 {
45     return adoptRef(*new DocumentTimeline(document));
46 }
47
48 DocumentTimeline::DocumentTimeline(Document& document)
49     : AnimationTimeline(DocumentTimelineClass)
50     , m_document(&document)
51     , m_animationScheduleTimer(*this, &DocumentTimeline::animationScheduleTimerFired)
52 #if !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
53     , m_animationResolutionTimer(*this, &DocumentTimeline::animationResolutionTimerFired)
54 #endif
55 {
56 }
57
58 DocumentTimeline::~DocumentTimeline() = default;
59
60 void DocumentTimeline::detachFromDocument()
61 {
62     m_invalidationTaskQueue.close();
63     m_eventDispatchTaskQueue.close();
64     m_animationScheduleTimer.stop();
65
66     auto& animationsToRemove = animations();
67     while (!animationsToRemove.isEmpty())
68         animationsToRemove.first()->remove();
69
70     m_document = nullptr;
71 }
72
73 void DocumentTimeline::updateThrottlingState()
74 {
75     m_needsUpdateAnimationSchedule = false;
76     timingModelDidChange();
77 }
78
79 Seconds DocumentTimeline::animationInterval() const
80 {
81     if (!m_document || !m_document->page())
82         return Seconds::infinity();
83     return m_document->page()->isLowPowerModeEnabled() ? throttledAnimationInterval : defaultAnimationInterval;
84 }
85
86 void DocumentTimeline::suspendAnimations()
87 {
88     if (animationsAreSuspended())
89         return;
90
91     m_isSuspended = true;
92
93     m_invalidationTaskQueue.cancelAllTasks();
94     if (m_animationScheduleTimer.isActive())
95         m_animationScheduleTimer.stop();
96
97     for (const auto& animation : animations())
98         animation->setSuspended(true);
99
100     applyPendingAcceleratedAnimations();
101 }
102
103 void DocumentTimeline::resumeAnimations()
104 {
105     if (!animationsAreSuspended())
106         return;
107
108     m_isSuspended = false;
109
110     for (const auto& animation : animations())
111         animation->setSuspended(false);
112
113     m_needsUpdateAnimationSchedule = false;
114     timingModelDidChange();
115 }
116
117 bool DocumentTimeline::animationsAreSuspended()
118 {
119     return m_isSuspended;
120 }
121
122 unsigned DocumentTimeline::numberOfActiveAnimationsForTesting() const
123 {
124     unsigned count = 0;
125     for (const auto& animation : animations()) {
126         if (!animation->isSuspended())
127             ++count;
128     }
129     return count;
130 }
131
132 std::optional<Seconds> DocumentTimeline::currentTime()
133 {
134     if (m_paused || m_isSuspended || !m_document || !m_document->domWindow())
135         return AnimationTimeline::currentTime();
136
137 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
138     // If we're in the middle of firing a frame, either due to a requestAnimationFrame callback
139     // or scheduling an animation update, we want to ensure we use the same time we're using as
140     // the timestamp for requestAnimationFrame() callbacks.
141     if (m_document->animationScheduler().isFiring())
142         m_cachedCurrentTime = m_document->animationScheduler().lastTimestamp();
143 #endif
144
145     if (!m_cachedCurrentTime) {
146 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
147         // If we're not in the middle of firing a frame, let's make our best guess at what the currentTime should
148         // be since the last time a frame fired by increment of our update interval. This way code using something
149         // like setTimeout() or handling events will get a time that's only updating at around 60fps, or less if
150         // we're throttled.
151         auto lastAnimationSchedulerTimestamp = m_document->animationScheduler().lastTimestamp();
152         auto delta = Seconds(m_document->domWindow()->nowTimestamp()) - lastAnimationSchedulerTimestamp;
153         int frames = std::floor(delta.seconds() / animationInterval().seconds());
154         m_cachedCurrentTime = lastAnimationSchedulerTimestamp + Seconds(frames * animationInterval().seconds());
155 #else
156         m_cachedCurrentTime = Seconds(m_document->domWindow()->nowTimestamp());
157 #endif
158         // We want to be sure to keep this time cached until we've both finished running JS and finished updating
159         // animations, so we schedule the invalidation task and register a whenIdle callback on the VM, which will
160         // fire syncronously if no JS is running.
161         scheduleInvalidationTaskIfNeeded();
162         m_waitingOnVMIdle = true;
163         m_document->vm().whenIdle([this]() {
164             m_waitingOnVMIdle = false;
165             maybeClearCachedCurrentTime();
166         });
167     }
168     return m_cachedCurrentTime;
169 }
170
171 void DocumentTimeline::pause()
172 {
173     m_paused = true;
174 }
175
176 void DocumentTimeline::timingModelDidChange()
177 {
178     if (m_needsUpdateAnimationSchedule || m_isSuspended)
179         return;
180
181     m_needsUpdateAnimationSchedule = true;
182
183     // We know that we will resolve animations again, so we can cancel the timer right away.
184     if (m_animationScheduleTimer.isActive())
185         m_animationScheduleTimer.stop();
186
187     scheduleInvalidationTaskIfNeeded();
188 }
189
190 void DocumentTimeline::scheduleInvalidationTaskIfNeeded()
191 {
192     if (m_invalidationTaskQueue.hasPendingTasks())
193         return;
194
195     m_invalidationTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performInvalidationTask, this));
196 }
197
198 void DocumentTimeline::performInvalidationTask()
199 {
200     // Now that the timing model has changed we can see if there are DOM events to dispatch for declarative animations.
201     for (auto& animation : animations()) {
202         if (is<DeclarativeAnimation>(animation))
203             downcast<DeclarativeAnimation>(*animation).invalidateDOMEvents();
204     }
205
206     applyPendingAcceleratedAnimations();
207
208     updateAnimationSchedule();
209     maybeClearCachedCurrentTime();
210 }
211
212 void DocumentTimeline::maybeClearCachedCurrentTime()
213 {
214     // We want to make sure we only clear the cached current time if we're not currently running
215     // JS or waiting on all current animation updating code to have completed. This is so that
216     // we're guaranteed to have a consistent current time reported for all work happening in a given
217     // JS frame or throughout updating animations in WebCore.
218     if (!m_waitingOnVMIdle && !m_invalidationTaskQueue.hasPendingTasks())
219         m_cachedCurrentTime = std::nullopt;
220 }
221
222 void DocumentTimeline::updateAnimationSchedule()
223 {
224     if (!m_needsUpdateAnimationSchedule)
225         return;
226
227     m_needsUpdateAnimationSchedule = false;
228
229     if (!m_acceleratedAnimationsPendingRunningStateChange.isEmpty()) {
230         scheduleAnimationResolution();
231         return;
232     }
233
234     Seconds scheduleDelay = Seconds::infinity();
235
236     for (const auto& animation : animations()) {
237         auto animationTimeToNextRequiredTick = animation->timeToNextRequiredTick();
238         if (animationTimeToNextRequiredTick < animationInterval()) {
239             scheduleAnimationResolution();
240             return;
241         }
242         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
243     }
244
245     if (scheduleDelay < Seconds::infinity())
246         m_animationScheduleTimer.startOneShot(scheduleDelay);
247 }
248
249 void DocumentTimeline::animationScheduleTimerFired()
250 {
251     scheduleAnimationResolution();
252 }
253
254 void DocumentTimeline::scheduleAnimationResolution()
255 {
256 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
257     m_document->animationScheduler().scheduleWebAnimationsResolution();
258 #else
259     // FIXME: We need to use the same logic as ScriptedAnimationController here,
260     // which will be addressed by the refactor tracked by webkit.org/b/179293.
261     m_animationResolutionTimer.startOneShot(animationInterval());
262 #endif
263 }
264
265 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
266 void DocumentTimeline::documentAnimationSchedulerDidFire()
267 #else
268 void DocumentTimeline::animationResolutionTimerFired()
269 #endif
270 {
271     updateAnimations();
272 }
273
274 void DocumentTimeline::updateAnimations()
275 {
276     for (const auto& animation : animations())
277         animation->runPendingTasks();
278
279     // Perform a microtask checkpoint such that all promises that may have resolved while
280     // running pending tasks can fire right away.
281     MicrotaskQueue::mainThreadQueue().performMicrotaskCheckpoint();
282
283     // Let's first resolve any animation that does not have a target.
284     for (auto* animation : animationsWithoutTarget())
285         animation->resolve();
286
287     // For the rest of the animations, we will resolve them via TreeResolver::createAnimatedElementUpdate()
288     // by invalidating their target element's style.
289     if (m_document && hasElementAnimations()) {
290         for (const auto& elementToAnimationsMapItem : elementToAnimationsMap())
291             elementToAnimationsMapItem.key->invalidateStyleAndLayerComposition();
292         for (const auto& elementToCSSAnimationsMapItem : elementToCSSAnimationsMap())
293             elementToCSSAnimationsMapItem.key->invalidateStyleAndLayerComposition();
294         for (const auto& elementToCSSTransitionsMapItem : elementToCSSTransitionsMap())
295             elementToCSSTransitionsMapItem.key->invalidateStyleAndLayerComposition();
296         m_document->updateStyleIfNeeded();
297     }
298
299     // Time has advanced, the timing model requires invalidation now.
300     timingModelDidChange();
301 }
302
303 bool DocumentTimeline::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const
304 {
305     if (!renderer.element())
306         return true;
307
308     KeyframeEffectReadOnly* matchingEffect = nullptr;
309     for (const auto& animation : animationsForElement(*renderer.element())) {
310         auto* effect = animation->effect();
311         if (is<KeyframeEffectReadOnly>(effect)) {
312             auto* keyframeEffect = downcast<KeyframeEffectReadOnly>(effect);
313             if (keyframeEffect->animatedProperties().contains(CSSPropertyTransform))
314                 matchingEffect = downcast<KeyframeEffectReadOnly>(effect);
315         }
316     }
317
318     if (matchingEffect)
319         return matchingEffect->computeExtentOfTransformAnimation(bounds);
320
321     return true;
322 }
323
324 bool DocumentTimeline::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
325 {
326     if (!renderer.element())
327         return false;
328
329     for (const auto& animation : animationsForElement(*renderer.element())) {
330         auto playState = animation->playState();
331         if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
332             continue;
333         auto* effect = animation->effect();
334         if (is<KeyframeEffectReadOnly>(effect) && downcast<KeyframeEffectReadOnly>(effect)->animatedProperties().contains(property))
335             return true;
336     }
337
338     return false;
339 }
340
341 bool DocumentTimeline::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property) const
342 {
343     if (!renderer.element())
344         return false;
345
346     for (const auto& animation : animationsForElement(*renderer.element())) {
347         auto playState = animation->playState();
348         if (playState != WebAnimation::PlayState::Running && playState != WebAnimation::PlayState::Paused)
349             continue;
350         auto* effect = animation->effect();
351         if (is<KeyframeEffectReadOnly>(effect)) {
352             auto* keyframeEffect = downcast<KeyframeEffectReadOnly>(effect);
353             if (keyframeEffect->isRunningAccelerated() && keyframeEffect->animatedProperties().contains(property))
354                 return true;
355         }
356     }
357
358     return false;
359 }
360
361 std::unique_ptr<RenderStyle> DocumentTimeline::animatedStyleForRenderer(RenderElement& renderer)
362 {
363     std::unique_ptr<RenderStyle> result;
364
365     if (auto* element = renderer.element()) {
366         for (const auto& animation : animationsForElement(*element)) {
367             if (is<KeyframeEffectReadOnly>(animation->effect()))
368                 downcast<KeyframeEffectReadOnly>(animation->effect())->getAnimatedStyle(result);
369         }
370     }
371
372     if (!result)
373         result = RenderStyle::clonePtr(renderer.style());
374
375     return result;
376 }
377
378 void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)
379 {
380     m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
381 }
382
383 void DocumentTimeline::applyPendingAcceleratedAnimations()
384 {
385     bool hasForcedLayout = false;
386     for (auto& animation : m_acceleratedAnimationsPendingRunningStateChange) {
387         if (!hasForcedLayout) {
388             auto* effect = animation->effect();
389             if (is<KeyframeEffectReadOnly>(effect))
390                 hasForcedLayout |= downcast<KeyframeEffectReadOnly>(effect)->forceLayoutIfNeeded();
391         }
392         animation->applyPendingAcceleratedActions();
393     }
394
395     m_acceleratedAnimationsPendingRunningStateChange.clear();
396 }
397
398 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element)
399 {
400     // FIXME: This will let animations run using hardware compositing even if later in the active
401     // span of the current animations a new animation should require hardware compositing to be
402     // disabled (webkit.org/b/179974).
403     auto animations = animationsForElement(element);
404     for (const auto& animation : animations) {
405         if (is<KeyframeEffectReadOnly>(animation->effect()) && !downcast<KeyframeEffectReadOnly>(animation->effect())->isRunningAccelerated())
406             return false;
407     }
408     return !animations.isEmpty();
409 }
410
411 void DocumentTimeline::enqueueAnimationPlaybackEvent(AnimationPlaybackEvent& event)
412 {
413     m_pendingAnimationEvents.append(event);
414
415     if (!m_eventDispatchTaskQueue.hasPendingTasks())
416         m_eventDispatchTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performEventDispatchTask, this));
417 }
418
419 static inline bool compareAnimationPlaybackEvents(const Ref<WebCore::AnimationPlaybackEvent>& lhs, const Ref<WebCore::AnimationPlaybackEvent>& rhs)
420 {
421     // Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
422     // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
423     if (lhs->timelineTime() && !rhs->timelineTime())
424         return false;
425     if (!lhs->timelineTime() && rhs->timelineTime())
426         return true;
427     if (!lhs->timelineTime() && !rhs->timelineTime())
428         return true;
429     return lhs->timelineTime().value() < rhs->timelineTime().value();
430 }
431
432 void DocumentTimeline::performEventDispatchTask()
433 {
434     if (m_pendingAnimationEvents.isEmpty())
435         return;
436
437     auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
438
439     std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), compareAnimationPlaybackEvents);
440     for (auto& pendingEvent : pendingAnimationEvents)
441         pendingEvent->target()->dispatchEvent(pendingEvent);
442 }
443
444 } // namespace WebCore