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