c4dba7bb28fed04e3bb36bdebd71e8b48f838763
[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 }
63
64 void DocumentTimeline::detachFromDocument()
65 {
66     m_document = nullptr;
67 }
68
69 std::optional<Seconds> DocumentTimeline::currentTime()
70 {
71     if (m_paused || !m_document)
72         return AnimationTimeline::currentTime();
73
74     if (!m_cachedCurrentTime) {
75         m_cachedCurrentTime = Seconds(m_document->domWindow()->nowTimestamp());
76         scheduleInvalidationTaskIfNeeded();
77     }
78     return m_cachedCurrentTime;
79 }
80
81 void DocumentTimeline::pause()
82 {
83     m_paused = true;
84 }
85
86 void DocumentTimeline::animationTimingModelDidChange()
87 {
88     if (m_needsUpdateAnimationSchedule)
89         return;
90
91     m_needsUpdateAnimationSchedule = true;
92
93     // We know that we will resolve animations again, so we can cancel the timer right away.
94     if (m_animationScheduleTimer.isActive())
95         m_animationScheduleTimer.stop();
96
97     scheduleInvalidationTaskIfNeeded();
98 }
99
100 void DocumentTimeline::scheduleInvalidationTaskIfNeeded()
101 {
102     if (m_invalidationTaskQueue.hasPendingTasks())
103         return;
104
105     m_invalidationTaskQueue.enqueueTask(std::bind(&DocumentTimeline::performInvalidationTask, this));
106 }
107
108 void DocumentTimeline::performInvalidationTask()
109 {
110     updateAnimationSchedule();
111     m_cachedCurrentTime = std::nullopt;
112 }
113
114 void DocumentTimeline::updateAnimationSchedule()
115 {
116     if (!m_needsUpdateAnimationSchedule)
117         return;
118
119     m_needsUpdateAnimationSchedule = false;
120
121     Seconds now = currentTime().value();
122     Seconds scheduleDelay = Seconds::infinity();
123
124     for (const auto& animation : animations()) {
125         auto animationTimeToNextRequiredTick = animation->timeToNextRequiredTick(now);
126         if (animationTimeToNextRequiredTick < animationInterval) {
127             scheduleAnimationResolution();
128             return;
129         }
130         scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
131     }
132
133     if (scheduleDelay < Seconds::infinity())
134         m_animationScheduleTimer.startOneShot(scheduleDelay);
135 }
136
137 void DocumentTimeline::animationScheduleTimerFired()
138 {
139     scheduleAnimationResolution();
140 }
141
142 void DocumentTimeline::scheduleAnimationResolution()
143 {
144 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
145     DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
146 #else
147     // FIXME: We need to use the same logic as ScriptedAnimationController here,
148     // which will be addressed by the refactor tracked by webkit.org/b/179293.
149     m_animationResolutionTimer.startOneShot(animationInterval);
150 #endif
151 }
152
153 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
154 void DocumentTimeline::displayRefreshFired()
155 #else
156 void DocumentTimeline::animationResolutionTimerFired()
157 #endif
158 {
159     updateAnimations();
160 }
161
162 void DocumentTimeline::updateAnimations()
163 {
164     if (m_document && !elementToAnimationsMap().isEmpty()) {
165         for (const auto& elementToAnimationsMapItem : elementToAnimationsMap())
166             elementToAnimationsMapItem.key->invalidateStyleAndLayerComposition();
167         m_document->updateStyleIfNeeded();
168     }
169
170     for (auto animation : m_acceleratedAnimationsPendingRunningStateChange)
171         animation->startOrStopAccelerated();
172     m_acceleratedAnimationsPendingRunningStateChange.clear();
173
174     // Time has advanced, the timing model requires invalidation now.
175     animationTimingModelDidChange();
176 }
177
178 std::unique_ptr<RenderStyle> DocumentTimeline::animatedStyleForRenderer(RenderElement& renderer)
179 {
180     std::unique_ptr<RenderStyle> result;
181
182     if (auto* element = renderer.element()) {
183         for (auto animation : animationsForElement(*element)) {
184             if (animation->effect() && animation->effect()->isKeyframeEffect())
185                 downcast<KeyframeEffect>(animation->effect())->getAnimatedStyle(result);
186         }
187     }
188
189     if (!result)
190         result = RenderStyle::clonePtr(renderer.style());
191
192     return result;
193 }
194
195 void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)
196 {
197     m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
198 }
199
200 bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element)
201 {
202     // FIXME: This will let animations run using hardware compositing even if later in the active
203     // span of the current animations a new animation should require hardware compositing to be
204     // disabled (webkit.org/b/179974).
205     auto animations = animationsForElement(element);
206     for (const auto& animation : animations) {
207         if (animation->effect() && animation->effect()->isKeyframeEffect() && !downcast<KeyframeEffect>(animation->effect())->isRunningAccelerated())
208             return false;
209     }
210     return !animations.isEmpty();
211 }
212
213 void DocumentTimeline::windowScreenDidChange(PlatformDisplayID displayID)
214 {
215 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
216     DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
217 #else
218     UNUSED_PARAM(displayID);
219 #endif
220 }
221
222 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
223 RefPtr<DisplayRefreshMonitor> DocumentTimeline::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
224 {
225     if (!m_document || !m_document->page())
226         return nullptr;
227
228     if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
229         return monitor;
230
231     return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
232 }
233 #endif
234
235 } // namespace WebCore