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