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