1b39bf16e03503ac3c237f44b3f5fefa5686ee62
[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> protect(*this);
151
152     for (size_t i = 0; i < callbacks.size(); ++i) {
153         RequestAnimationFrameCallback* callback = callbacks[i].get();
154         if (!callback->m_firedOrCancelled) {
155             callback->m_firedOrCancelled = true;
156             InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_document, callback->m_id);
157             if (callback->m_useLegacyTimeBase)
158                 callback->handleEvent(legacyHighResNowMs);
159             else
160                 callback->handleEvent(highResNowMs);
161             InspectorInstrumentation::didFireAnimationFrame(cookie);
162         }
163     }
164
165     // Remove any callbacks we fired from the list of pending callbacks.
166     for (size_t i = 0; i < m_callbacks.size();) {
167         if (m_callbacks[i]->m_firedOrCancelled)
168             m_callbacks.remove(i);
169         else
170             ++i;
171     }
172
173     if (m_callbacks.size())
174         scheduleAnimation();
175 }
176
177 void ScriptedAnimationController::windowScreenDidChange(PlatformDisplayID displayID)
178 {
179     if (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled())
180         return;
181 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
182     DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
183 #else
184     UNUSED_PARAM(displayID);
185 #endif
186 }
187
188 void ScriptedAnimationController::scheduleAnimation()
189 {
190     if (!m_document || (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled()))
191         return;
192
193 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
194 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
195     if (!m_isUsingTimer && !m_isThrottled) {
196         if (DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this))
197             return;
198
199         m_isUsingTimer = true;
200     }
201 #endif
202     if (m_animationTimer.isActive())
203         return;
204
205     double animationInterval = MinimumAnimationInterval;
206 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
207     if (m_isThrottled)
208         animationInterval = MinimumThrottledAnimationInterval;
209 #endif
210
211     double scheduleDelay = std::max<double>(animationInterval - (monotonicallyIncreasingTime() - m_lastAnimationFrameTimeMonotonic), 0);
212     m_animationTimer.startOneShot(scheduleDelay);
213 #else
214     if (FrameView* frameView = m_document->view())
215         frameView->scheduleAnimation();
216 #endif
217 }
218
219 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
220 void ScriptedAnimationController::animationTimerFired()
221 {
222     m_lastAnimationFrameTimeMonotonic = monotonicallyIncreasingTime();
223     serviceScriptedAnimations(m_lastAnimationFrameTimeMonotonic);
224 }
225 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
226 void ScriptedAnimationController::displayRefreshFired(double monotonicTimeNow)
227 {
228     serviceScriptedAnimations(monotonicTimeNow);
229 }
230 #endif
231 #endif
232
233
234 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
235 RefPtr<DisplayRefreshMonitor> ScriptedAnimationController::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
236 {
237     if (!m_document->page())
238         return nullptr;
239
240     if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
241         return monitor;
242
243     return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);
244 }
245 #endif
246
247
248 }
249
250 #endif