[Web Animations] Make imported/mozilla/css-animations/test_event-dispatch.html pass...
[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     Seconds scheduleDelay = Seconds::infinity();
131
132     for (const auto& animation : animations()) {
133         auto animationTimeToNextRequiredTick = animation->timeToNextRequiredTick();
134         if (animationTimeToNextRequiredTick < animationInterval) {
135             scheduleAnimationResolution();
136             return;
137         }
138         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
139     }
140
141     if (scheduleDelay < Seconds::infinity())
142         m_animationScheduleTimer.startOneShot(scheduleDelay);
143 }
144
145 void DocumentTimeline::animationScheduleTimerFired()
146 {
147     scheduleAnimationResolution();
148 }
149
150 void DocumentTimeline::scheduleAnimationResolution()
151 {
152 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
153     DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
154 #else
155     // FIXME: We need to use the same logic as ScriptedAnimationController here,
156     // which will be addressed by the refactor tracked by webkit.org/b/179293.
157     m_animationResolutionTimer.startOneShot(animationInterval);
158 #endif
159 }
160
161 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
162 void DocumentTimeline::displayRefreshFired()
163 #else
164 void DocumentTimeline::animationResolutionTimerFired()
165 #endif
166 {
167     updateAnimations();
168 }
169
170 void DocumentTimeline::updateAnimations()
171 {
172     if (m_document && hasElementAnimations()) {
173         for (const auto& elementToAnimationsMapItem : elementToAnimationsMap())
174             elementToAnimationsMapItem.key->invalidateStyleAndLayerComposition();
175         for (const auto& elementToCSSAnimationsMapItem : elementToCSSAnimationsMap())
176             elementToCSSAnimationsMapItem.key->invalidateStyleAndLayerComposition();
177         for (const auto& elementToCSSTransitionsMapItem : elementToCSSTransitionsMap())
178             elementToCSSTransitionsMapItem.key->invalidateStyleAndLayerComposition();
179         m_document->updateStyleIfNeeded();
180     }
181
182     for (auto& animation : m_acceleratedAnimationsPendingRunningStateChange)
183         animation->startOrStopAccelerated();
184     m_acceleratedAnimationsPendingRunningStateChange.clear();
185
186     // Time has advanced, the timing model requires invalidation now.
187     timingModelDidChange();
188 }
189
190 std::unique_ptr<RenderStyle> DocumentTimeline::animatedStyleForRenderer(RenderElement& renderer)
191 {
192     std::unique_ptr<RenderStyle> result;
193
194     if (auto* element = renderer.element()) {
195         for (auto animation : animationsForElement(*element)) {
196             if (is<KeyframeEffectReadOnly>(animation->effect()))
197                 downcast<KeyframeEffectReadOnly>(animation->effect())->getAnimatedStyle(result);
198         }
199     }
200
201     if (!result)
202         result = RenderStyle::clonePtr(renderer.style());
203
204     return result;
205 }
206
207 void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)
208 {
209     m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
210 }
211
212 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element)
213 {
214     // FIXME: This will let animations run using hardware compositing even if later in the active
215     // span of the current animations a new animation should require hardware compositing to be
216     // disabled (webkit.org/b/179974).
217     auto animations = animationsForElement(element);
218     for (const auto& animation : animations) {
219         if (is<KeyframeEffectReadOnly>(animation->effect()) && !downcast<KeyframeEffectReadOnly>(animation->effect())->isRunningAccelerated())
220             return false;
221     }
222     return !animations.isEmpty();
223 }
224
225 void DocumentTimeline::enqueueAnimationPlaybackEvent(AnimationPlaybackEvent& event)
226 {
227     m_pendingAnimationEvents.append(event);
228
229     if (!m_eventDispatchTaskQueue.hasPendingTasks())
230         m_eventDispatchTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performEventDispatchTask, this));
231 }
232
233 static inline bool compareAnimationPlaybackEvents(const Ref<WebCore::AnimationPlaybackEvent>& lhs, const Ref<WebCore::AnimationPlaybackEvent>& rhs)
234 {
235     // Sort the events by their scheduled event time such that events that were scheduled to occur earlier, sort before events scheduled to occur later
236     // and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
237     if (lhs->timelineTime() && !rhs->timelineTime())
238         return false;
239     if (!lhs->timelineTime() && rhs->timelineTime())
240         return true;
241     if (!lhs->timelineTime() && !rhs->timelineTime())
242         return true;
243     return lhs->timelineTime().value() < rhs->timelineTime().value();
244 }
245
246 void DocumentTimeline::performEventDispatchTask()
247 {
248     if (m_pendingAnimationEvents.isEmpty())
249         return;
250
251     auto pendingAnimationEvents = WTFMove(m_pendingAnimationEvents);
252
253     std::stable_sort(pendingAnimationEvents.begin(), pendingAnimationEvents.end(), compareAnimationPlaybackEvents);
254     for (auto& pendingEvent : pendingAnimationEvents)
255         pendingEvent->target()->dispatchEvent(pendingEvent);
256 }
257
258 void DocumentTimeline::windowScreenDidChange(PlatformDisplayID displayID)
259 {
260 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
261     DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
262 #else
263     UNUSED_PARAM(displayID);
264 #endif
265 }
266
267 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
268 RefPtr<DisplayRefreshMonitor> DocumentTimeline::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
269 {
270     if (!m_document || !m_document->page())
271         return nullptr;
272
273     if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
274         return monitor;
275
276     return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
277 }
278 #endif
279
280 } // namespace WebCore