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