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