[Web Animations] Enable seeking for hardware animations
[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 animationInterval { 15_ms };
42
43 namespace WebCore {
44
45 Ref<DocumentTimeline> DocumentTimeline::create(Document& document, PlatformDisplayID displayID)
46 {
47     return adoptRef(*new DocumentTimeline(document, displayID));
48 }
49
50 DocumentTimeline::DocumentTimeline(Document& document, PlatformDisplayID displayID)
51     : AnimationTimeline(DocumentTimelineClass)
52     , m_document(&document)
53     , m_animationScheduleTimer(*this, &DocumentTimeline::animationScheduleTimerFired)
54 #if !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
55     , m_animationResolutionTimer(*this, &DocumentTimeline::animationResolutionTimerFired)
56 #endif
57 {
58     windowScreenDidChange(displayID);
59 }
60
61 DocumentTimeline::~DocumentTimeline()
62 {
63     m_invalidationTaskQueue.close();
64     m_eventDispatchTaskQueue.close();
65 }
66
67 void DocumentTimeline::detachFromDocument()
68 {
69     m_document = nullptr;
70 }
71
72 std::optional<Seconds> DocumentTimeline::currentTime()
73 {
74     if (m_paused || !m_document || !m_document->domWindow())
75         return AnimationTimeline::currentTime();
76
77     if (!m_cachedCurrentTime) {
78         m_cachedCurrentTime = Seconds(m_document->domWindow()->nowTimestamp());
79         scheduleInvalidationTaskIfNeeded();
80     }
81     return m_cachedCurrentTime;
82 }
83
84 void DocumentTimeline::pause()
85 {
86     m_paused = true;
87 }
88
89 void DocumentTimeline::timingModelDidChange()
90 {
91     if (m_needsUpdateAnimationSchedule)
92         return;
93
94     m_needsUpdateAnimationSchedule = true;
95
96     // We know that we will resolve animations again, so we can cancel the timer right away.
97     if (m_animationScheduleTimer.isActive())
98         m_animationScheduleTimer.stop();
99
100     scheduleInvalidationTaskIfNeeded();
101 }
102
103 void DocumentTimeline::scheduleInvalidationTaskIfNeeded()
104 {
105     if (m_invalidationTaskQueue.hasPendingTasks())
106         return;
107
108     m_invalidationTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performInvalidationTask, this));
109 }
110
111 void DocumentTimeline::performInvalidationTask()
112 {
113     // Now that the timing model has changed we can see if there are DOM events to dispatch for declarative animations.
114     for (auto& animation : animations()) {
115         if (is<DeclarativeAnimation>(animation))
116             downcast<DeclarativeAnimation>(*animation).invalidateDOMEvents();
117     }
118
119     updateAnimationSchedule();
120     m_cachedCurrentTime = std::nullopt;
121 }
122
123 void DocumentTimeline::updateAnimationSchedule()
124 {
125     if (!m_needsUpdateAnimationSchedule)
126         return;
127
128     m_needsUpdateAnimationSchedule = false;
129
130     if (!m_acceleratedAnimationsPendingRunningStateChange.isEmpty()) {
131         scheduleAnimationResolution();
132         return;
133     }
134
135     Seconds scheduleDelay = Seconds::infinity();
136
137     for (const auto& animation : animations()) {
138         auto animationTimeToNextRequiredTick = animation->timeToNextRequiredTick();
139         if (animationTimeToNextRequiredTick < animationInterval) {
140             scheduleAnimationResolution();
141             return;
142         }
143         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
144     }
145
146     if (scheduleDelay < Seconds::infinity())
147         m_animationScheduleTimer.startOneShot(scheduleDelay);
148 }
149
150 void DocumentTimeline::animationScheduleTimerFired()
151 {
152     scheduleAnimationResolution();
153 }
154
155 void DocumentTimeline::scheduleAnimationResolution()
156 {
157 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
158     DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
159 #else
160     // FIXME: We need to use the same logic as ScriptedAnimationController here,
161     // which will be addressed by the refactor tracked by webkit.org/b/179293.
162     m_animationResolutionTimer.startOneShot(animationInterval);
163 #endif
164 }
165
166 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
167 void DocumentTimeline::displayRefreshFired()
168 #else
169 void DocumentTimeline::animationResolutionTimerFired()
170 #endif
171 {
172     updateAnimations();
173 }
174
175 void DocumentTimeline::updateAnimations()
176 {
177     if (m_document && hasElementAnimations()) {
178         for (const auto& elementToAnimationsMapItem : elementToAnimationsMap())
179             elementToAnimationsMapItem.key->invalidateStyleAndLayerComposition();
180         for (const auto& elementToCSSAnimationsMapItem : elementToCSSAnimationsMap())
181             elementToCSSAnimationsMapItem.key->invalidateStyleAndLayerComposition();
182         for (const auto& elementToCSSTransitionsMapItem : elementToCSSTransitionsMap())
183             elementToCSSTransitionsMapItem.key->invalidateStyleAndLayerComposition();
184         m_document->updateStyleIfNeeded();
185     }
186
187     applyPendingAcceleratedAnimations();
188
189     // Time has advanced, the timing model requires invalidation now.
190     timingModelDidChange();
191 }
192
193 std::unique_ptr<RenderStyle> DocumentTimeline::animatedStyleForRenderer(RenderElement& renderer)
194 {
195     std::unique_ptr<RenderStyle> result;
196
197     if (auto* element = renderer.element()) {
198         for (const auto& animation : animationsForElement(*element)) {
199             if (is<KeyframeEffectReadOnly>(animation->effect()))
200                 downcast<KeyframeEffectReadOnly>(animation->effect())->getAnimatedStyle(result);
201         }
202     }
203
204     if (!result)
205         result = RenderStyle::clonePtr(renderer.style());
206
207     return result;
208 }
209
210 void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)
211 {
212     m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
213 }
214
215 void DocumentTimeline::applyPendingAcceleratedAnimations()
216 {
217     for (auto& animation : m_acceleratedAnimationsPendingRunningStateChange)
218         animation->applyPendingAcceleratedActions();
219     m_acceleratedAnimationsPendingRunningStateChange.clear();
220 }
221
222 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element)
223 {
224     // FIXME: This will let animations run using hardware compositing even if later in the active
225     // span of the current animations a new animation should require hardware compositing to be
226     // disabled (webkit.org/b/179974).
227     auto animations = animationsForElement(element);
228     for (const auto& animation : animations) {
229         if (is<KeyframeEffectReadOnly>(animation->effect()) && !downcast<KeyframeEffectReadOnly>(animation->effect())->isRunningAccelerated())
230             return false;
231     }
232     return !animations.isEmpty();
233 }
234
235 void DocumentTimeline::enqueueAnimationPlaybackEvent(AnimationPlaybackEvent& event)
236 {
237     m_pendingAnimationEvents.append(event);
238
239     if (!m_eventDispatchTaskQueue.hasPendingTasks())
240         m_eventDispatchTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performEventDispatchTask, this));
241 }
242
243 static inline bool compareAnimationPlaybackEvents(const Ref<WebCore::AnimationPlaybackEvent>& lhs, const Ref<WebCore::AnimationPlaybackEvent>& rhs)
244 {
245     // Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
246     // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
247     if (lhs->timelineTime() && !rhs->timelineTime())
248         return false;
249     if (!lhs->timelineTime() && rhs->timelineTime())
250         return true;
251     if (!lhs->timelineTime() && !rhs->timelineTime())
252         return true;
253     return lhs->timelineTime().value() < rhs->timelineTime().value();
254 }
255
256 void DocumentTimeline::performEventDispatchTask()
257 {
258     if (m_pendingAnimationEvents.isEmpty())
259         return;
260
261     auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
262
263     std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), compareAnimationPlaybackEvents);
264     for (auto& pendingEvent : pendingAnimationEvents)
265         pendingEvent->target()->dispatchEvent(pendingEvent);
266 }
267
268 void DocumentTimeline::windowScreenDidChange(PlatformDisplayID displayID)
269 {
270 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
271     DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
272 #else
273     UNUSED_PARAM(displayID);
274 #endif
275 }
276
277 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
278 RefPtr<DisplayRefreshMonitor> DocumentTimeline::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
279 {
280     if (!m_document || !m_document->page())
281         return nullptr;
282
283     if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
284         return monitor;
285
286     return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
287 }
288 #endif
289
290 } // namespace WebCore