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