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