ecd1942e2bd35036384b9ce662a9990d2ce55812
[WebKit-https.git] / Source / WebCore / dom / ScriptedAnimationController.cpp
1 /*
2  * Copyright (C) 2011 Google 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  *  DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  */
25
26 #include "config.h"
27 #include "ScriptedAnimationController.h"
28
29 #if ENABLE(REQUEST_ANIMATION_FRAME)
30
31 #include "DisplayRefreshMonitor.h"
32 #include "DisplayRefreshMonitorManager.h"
33 #include "Document.h"
34 #include "DocumentLoader.h"
35 #include "FrameView.h"
36 #include "InspectorInstrumentation.h"
37 #include "Logging.h"
38 #include "MainFrame.h"
39 #include "Page.h"
40 #include "RequestAnimationFrameCallback.h"
41 #include "Settings.h"
42 #include <wtf/Ref.h>
43 #include <wtf/SystemTracing.h>
44
45 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
46 #include <algorithm>
47 #include <wtf/CurrentTime.h>
48
49 // Allow a little more than 60fps to make sure we can at least hit that frame rate.
50 #define MinimumAnimationInterval 0.015
51 #define MinimumThrottledAnimationInterval 10
52 #endif
53
54 namespace WebCore {
55
56 ScriptedAnimationController::ScriptedAnimationController(Document* document, PlatformDisplayID displayID)
57     : m_document(document)
58 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
59     , m_animationTimer(*this, &ScriptedAnimationController::animationTimerFired)
60 #endif
61 {
62     windowScreenDidChange(displayID);
63 }
64
65 ScriptedAnimationController::~ScriptedAnimationController()
66 {
67 }
68
69 bool ScriptedAnimationController::requestAnimationFrameEnabled() const
70 {
71     return m_document && (!m_document->settings() || m_document->settings()->requestAnimationFrameEnabled());
72 }
73
74 void ScriptedAnimationController::suspend()
75 {
76     ++m_suspendCount;
77 }
78
79 void ScriptedAnimationController::resume()
80 {
81     // It would be nice to put an ASSERT(m_suspendCount > 0) here, but in WK1 resume() can be called
82     // even when suspend hasn't (if a tab was created in the background).
83     if (m_suspendCount > 0)
84         --m_suspendCount;
85
86     if (!m_suspendCount && m_callbacks.size())
87         scheduleAnimation();
88 }
89
90 void ScriptedAnimationController::setThrottled(bool isThrottled)
91 {
92 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
93     if (m_isThrottled == isThrottled)
94         return;
95
96     LOG(Animations, "%p - Setting RequestAnimationFrame throttling state to %d in frame %p (isMainFrame: %d)", this, isThrottled, m_document->frame(), m_document->frame() ? m_document->frame()->isMainFrame() : 0);
97
98     m_isThrottled = isThrottled;
99     if (m_animationTimer.isActive()) {
100         m_animationTimer.stop();
101         scheduleAnimation();
102     }
103 #else
104     UNUSED_PARAM(isThrottled);
105 #endif
106 }
107
108 bool ScriptedAnimationController::isThrottled() const
109 {
110 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
111     return m_isThrottled;
112 #else
113     return false;
114 #endif
115 }
116
117 ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassRefPtr<RequestAnimationFrameCallback> callback)
118 {
119     ScriptedAnimationController::CallbackId id = ++m_nextCallbackId;
120     callback->m_firedOrCancelled = false;
121     callback->m_id = id;
122     m_callbacks.append(callback);
123
124     InspectorInstrumentation::didRequestAnimationFrame(m_document, id);
125
126     if (!m_suspendCount)
127         scheduleAnimation();
128     return id;
129 }
130
131 void ScriptedAnimationController::cancelCallback(CallbackId id)
132 {
133     for (size_t i = 0; i < m_callbacks.size(); ++i) {
134         if (m_callbacks[i]->m_id == id) {
135             m_callbacks[i]->m_firedOrCancelled = true;
136             InspectorInstrumentation::didCancelAnimationFrame(m_document, id);
137             m_callbacks.remove(i);
138             return;
139         }
140     }
141 }
142
143 void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTimeNow)
144 {
145     if (!m_callbacks.size() || m_suspendCount || !requestAnimationFrameEnabled())
146         return;
147
148     TraceScope tracingScope(RAFCallbackStart, RAFCallbackEnd);
149     
150     double highResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow);
151     double legacyHighResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToPseudoWallTime(monotonicTimeNow);
152
153     // First, generate a list of callbacks to consider.  Callbacks registered from this point
154     // on are considered only for the "next" frame, not this one.
155     CallbackList callbacks(m_callbacks);
156
157     // Invoking callbacks may detach elements from our document, which clears the document's
158     // reference to us, so take a defensive reference.
159     Ref<ScriptedAnimationController> protectedThis(*this);
160
161     for (auto& callback : callbacks) {
162         if (!callback->m_firedOrCancelled) {
163             callback->m_firedOrCancelled = true;
164             InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_document, callback->m_id);
165             if (callback->m_useLegacyTimeBase)
166                 callback->handleEvent(legacyHighResNowMs);
167             else
168                 callback->handleEvent(highResNowMs);
169             InspectorInstrumentation::didFireAnimationFrame(cookie);
170         }
171     }
172
173     // Remove any callbacks we fired from the list of pending callbacks.
174     for (size_t i = 0; i < m_callbacks.size();) {
175         if (m_callbacks[i]->m_firedOrCancelled)
176             m_callbacks.remove(i);
177         else
178             ++i;
179     }
180
181     if (m_callbacks.size())
182         scheduleAnimation();
183 }
184
185 void ScriptedAnimationController::windowScreenDidChange(PlatformDisplayID displayID)
186 {
187     if (!requestAnimationFrameEnabled())
188         return;
189 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
190     DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
191 #else
192     UNUSED_PARAM(displayID);
193 #endif
194 }
195
196 void ScriptedAnimationController::scheduleAnimation()
197 {
198     if (!requestAnimationFrameEnabled())
199         return;
200
201 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
202 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
203     if (!m_isUsingTimer && !m_isThrottled) {
204         if (DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this))
205             return;
206
207         m_isUsingTimer = true;
208     }
209 #endif
210     if (m_animationTimer.isActive())
211         return;
212
213     double animationInterval = MinimumAnimationInterval;
214 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
215     if (m_isThrottled)
216         animationInterval = MinimumThrottledAnimationInterval;
217 #endif
218
219     double scheduleDelay = std::max<double>(animationInterval - (monotonicallyIncreasingTime() - m_lastAnimationFrameTimeMonotonic), 0);
220     m_animationTimer.startOneShot(scheduleDelay);
221 #else
222     if (FrameView* frameView = m_document->view())
223         frameView->scheduleAnimation();
224 #endif
225 }
226
227 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
228 void ScriptedAnimationController::animationTimerFired()
229 {
230     m_lastAnimationFrameTimeMonotonic = monotonicallyIncreasingTime();
231     serviceScriptedAnimations(m_lastAnimationFrameTimeMonotonic);
232 }
233 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
234 void ScriptedAnimationController::displayRefreshFired(double monotonicTimeNow)
235 {
236     serviceScriptedAnimations(monotonicTimeNow);
237 }
238 #endif
239 #endif
240
241
242 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
243 RefPtr<DisplayRefreshMonitor> ScriptedAnimationController::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
244 {
245     if (!m_document->page())
246         return nullptr;
247
248     if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
249         return monitor;
250
251     return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
252 }
253 #endif
254
255
256 }
257
258 #endif