Layout Test animations/needs-layout.html is a flaky Image Failure.
[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     applyPendingAcceleratedAnimations();
180
181     updateAnimationSchedule();
182     m_cachedCurrentTime = std::nullopt;
183 }
184
185 void DocumentTimeline::updateAnimationSchedule()
186 {
187     if (!m_needsUpdateAnimationSchedule)
188         return;
189
190     m_needsUpdateAnimationSchedule = false;
191
192     if (!m_acceleratedAnimationsPendingRunningStateChange.isEmpty()) {
193         scheduleAnimationResolution();
194         return;
195     }
196
197     Seconds scheduleDelay = Seconds::infinity();
198
199     for (const auto& animation : animations()) {
200         auto animationTimeToNextRequiredTick = animation->timeToNextRequiredTick();
201         if (animationTimeToNextRequiredTick < animationInterval()) {
202             scheduleAnimationResolution();
203             return;
204         }
205         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
206     }
207
208     if (scheduleDelay < Seconds::infinity())
209         m_animationScheduleTimer.startOneShot(scheduleDelay);
210 }
211
212 void DocumentTimeline::animationScheduleTimerFired()
213 {
214     scheduleAnimationResolution();
215 }
216
217 void DocumentTimeline::scheduleAnimationResolution()
218 {
219 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
220     DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
221 #else
222     // FIXME: We need to use the same logic as ScriptedAnimationController here,
223     // which will be addressed by the refactor tracked by webkit.org/b/179293.
224     m_animationResolutionTimer.startOneShot(animationInterval());
225 #endif
226 }
227
228 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
229 void DocumentTimeline::displayRefreshFired()
230 #else
231 void DocumentTimeline::animationResolutionTimerFired()
232 #endif
233 {
234     updateAnimations();
235 }
236
237 void DocumentTimeline::updateAnimations()
238 {
239     if (m_document && hasElementAnimations()) {
240         for (const auto& elementToAnimationsMapItem : elementToAnimationsMap())
241             elementToAnimationsMapItem.key->invalidateStyleAndLayerComposition();
242         for (const auto& elementToCSSAnimationsMapItem : elementToCSSAnimationsMap())
243             elementToCSSAnimationsMapItem.key->invalidateStyleAndLayerComposition();
244         for (const auto& elementToCSSTransitionsMapItem : elementToCSSTransitionsMap())
245             elementToCSSTransitionsMapItem.key->invalidateStyleAndLayerComposition();
246         m_document->updateStyleIfNeeded();
247     }
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     bool hasForcedLayout = false;
336     for (auto& animation : m_acceleratedAnimationsPendingRunningStateChange) {
337         if (!hasForcedLayout) {
338             auto* effect = animation->effect();
339             if (is<KeyframeEffectReadOnly>(effect))
340                 hasForcedLayout |= downcast<KeyframeEffectReadOnly>(effect)->forceLayoutIfNeeded();
341         }
342         animation->applyPendingAcceleratedActions();
343     }
344
345     m_acceleratedAnimationsPendingRunningStateChange.clear();
346 }
347
348 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element)
349 {
350     // FIXME: This will let animations run using hardware compositing even if later in the active
351     // span of the current animations a new animation should require hardware compositing to be
352     // disabled (webkit.org/b/179974).
353     auto animations = animationsForElement(element);
354     for (const auto& animation : animations) {
355         if (is<KeyframeEffectReadOnly>(animation->effect()) && !downcast<KeyframeEffectReadOnly>(animation->effect())->isRunningAccelerated())
356             return false;
357     }
358     return !animations.isEmpty();
359 }
360
361 void DocumentTimeline::enqueueAnimationPlaybackEvent(AnimationPlaybackEvent& event)
362 {
363     m_pendingAnimationEvents.append(event);
364
365     if (!m_eventDispatchTaskQueue.hasPendingTasks())
366         m_eventDispatchTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performEventDispatchTask, this));
367 }
368
369 static inline bool compareAnimationPlaybackEvents(const Ref<WebCore::AnimationPlaybackEvent>& lhs, const Ref<WebCore::AnimationPlaybackEvent>& rhs)
370 {
371     // Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
372     // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
373     if (lhs->timelineTime() && !rhs->timelineTime())
374         return false;
375     if (!lhs->timelineTime() && rhs->timelineTime())
376         return true;
377     if (!lhs->timelineTime() && !rhs->timelineTime())
378         return true;
379     return lhs->timelineTime().value() < rhs->timelineTime().value();
380 }
381
382 void DocumentTimeline::performEventDispatchTask()
383 {
384     if (m_pendingAnimationEvents.isEmpty())
385         return;
386
387     auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
388
389     std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), compareAnimationPlaybackEvents);
390     for (auto& pendingEvent : pendingAnimationEvents)
391         pendingEvent->target()->dispatchEvent(pendingEvent);
392 }
393
394 void DocumentTimeline::windowScreenDidChange(PlatformDisplayID displayID)
395 {
396 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
397     DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
398 #else
399     UNUSED_PARAM(displayID);
400 #endif
401 }
402
403 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
404 RefPtr<DisplayRefreshMonitor> DocumentTimeline::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
405 {
406     if (!m_document || !m_document->page())
407         return nullptr;
408
409     if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
410         return monitor;
411
412     return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
413 }
414 #endif
415
416 } // namespace WebCore