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