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