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