fd7dcce4e5f30ba65a2911cb7a9e76d2607860a9
[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 "RequestAnimationFrameCallback.h"
40 #include "Settings.h"
41 #include <wtf/Ref.h>
42
43 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
44 #include <algorithm>
45 #include <wtf/CurrentTime.h>
46
47 // Allow a little more than 60fps to make sure we can at least hit that frame rate.
48 #define MinimumAnimationInterval 0.015
49 #define MinimumThrottledAnimationInterval 10
50 #endif
51
52 namespace WebCore {
53
54 ScriptedAnimationController::ScriptedAnimationController(Document* document, PlatformDisplayID displayID)
55     : m_document(document)
56 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
57     , m_animationTimer(*this, &ScriptedAnimationController::animationTimerFired)
58 #endif
59 {
60     windowScreenDidChange(displayID);
61 }
62
63 ScriptedAnimationController::~ScriptedAnimationController()
64 {
65 }
66
67 void ScriptedAnimationController::suspend()
68 {
69     ++m_suspendCount;
70 }
71
72 void ScriptedAnimationController::resume()
73 {
74     // It would be nice to put an ASSERT(m_suspendCount > 0) here, but in WK1 resume() can be called
75     // even when suspend hasn't (if a tab was created in the background).
76     if (m_suspendCount > 0)
77         --m_suspendCount;
78
79     if (!m_suspendCount && m_callbacks.size())
80         scheduleAnimation();
81 }
82
83 void ScriptedAnimationController::setThrottled(bool isThrottled)
84 {
85 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
86     if (m_isThrottled == isThrottled)
87         return;
88
89     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);
90
91     m_isThrottled = isThrottled;
92     if (m_animationTimer.isActive()) {
93         m_animationTimer.stop();
94         scheduleAnimation();
95     }
96 #else
97     UNUSED_PARAM(isThrottled);
98 #endif
99 }
100
101 bool ScriptedAnimationController::isThrottled() const
102 {
103 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
104     return m_isThrottled;
105 #else
106     return false;
107 #endif
108 }
109
110 ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassRefPtr<RequestAnimationFrameCallback> callback)
111 {
112     ScriptedAnimationController::CallbackId id = ++m_nextCallbackId;
113     callback->m_firedOrCancelled = false;
114     callback->m_id = id;
115     m_callbacks.append(callback);
116
117     InspectorInstrumentation::didRequestAnimationFrame(m_document, id);
118
119     if (!m_suspendCount)
120         scheduleAnimation();
121     return id;
122 }
123
124 void ScriptedAnimationController::cancelCallback(CallbackId id)
125 {
126     for (size_t i = 0; i < m_callbacks.size(); ++i) {
127         if (m_callbacks[i]->m_id == id) {
128             m_callbacks[i]->m_firedOrCancelled = true;
129             InspectorInstrumentation::didCancelAnimationFrame(m_document, id);
130             m_callbacks.remove(i);
131             return;
132         }
133     }
134 }
135
136 void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTimeNow)
137 {
138     if (!m_callbacks.size() || m_suspendCount || (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled()))
139         return;
140
141     double highResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow);
142     double legacyHighResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToPseudoWallTime(monotonicTimeNow);
143
144     // First, generate a list of callbacks to consider.  Callbacks registered from this point
145     // on are considered only for the "next" frame, not this one.
146     CallbackList callbacks(m_callbacks);
147
148     // Invoking callbacks may detach elements from our document, which clears the document's
149     // reference to us, so take a defensive reference.
150     Ref<ScriptedAnimationController> protectedThis(*this);
151
152     for (auto& callback : callbacks) {
153         if (!callback->m_firedOrCancelled) {
154             callback->m_firedOrCancelled = true;
155             InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_document, callback->m_id);
156             if (callback->m_useLegacyTimeBase)
157                 callback->handleEvent(legacyHighResNowMs);
158             else
159                 callback->handleEvent(highResNowMs);
160             InspectorInstrumentation::didFireAnimationFrame(cookie);
161         }
162     }
163
164     // Remove any callbacks we fired from the list of pending callbacks.
165     for (size_t i = 0; i < m_callbacks.size();) {
166         if (m_callbacks[i]->m_firedOrCancelled)
167             m_callbacks.remove(i);
168         else
169             ++i;
170     }
171
172     if (m_callbacks.size())
173         scheduleAnimation();
174 }
175
176 void ScriptedAnimationController::windowScreenDidChange(PlatformDisplayID displayID)
177 {
178     if (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled())
179         return;
180 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
181     DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
182 #else
183     UNUSED_PARAM(displayID);
184 #endif
185 }
186
187 void ScriptedAnimationController::scheduleAnimation()
188 {
189     if (!m_document || (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled()))
190         return;
191
192 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
193 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
194     if (!m_isUsingTimer && !m_isThrottled) {
195         if (DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this))
196             return;
197
198         m_isUsingTimer = true;
199     }
200 #endif
201     if (m_animationTimer.isActive())
202         return;
203
204     double animationInterval = MinimumAnimationInterval;
205 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
206     if (m_isThrottled)
207         animationInterval = MinimumThrottledAnimationInterval;
208 #endif
209
210     double scheduleDelay = std::max<double>(animationInterval - (monotonicallyIncreasingTime() - m_lastAnimationFrameTimeMonotonic), 0);
211     m_animationTimer.startOneShot(scheduleDelay);
212 #else
213     if (FrameView* frameView = m_document->view())
214         frameView->scheduleAnimation();
215 #endif
216 }
217
218 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
219 void ScriptedAnimationController::animationTimerFired()
220 {
221     m_lastAnimationFrameTimeMonotonic = monotonicallyIncreasingTime();
222     serviceScriptedAnimations(m_lastAnimationFrameTimeMonotonic);
223 }
224 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
225 void ScriptedAnimationController::displayRefreshFired(double monotonicTimeNow)
226 {
227     serviceScriptedAnimations(monotonicTimeNow);
228 }
229 #endif
230 #endif
231
232
233 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
234 RefPtr<DisplayRefreshMonitor> ScriptedAnimationController::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
235 {
236     if (!m_document->page())
237         return nullptr;
238
239     if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
240         return monitor;
241
242     return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
243 }
244 #endif
245
246
247 }
248
249 #endif