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