2 * Copyright (C) 2011 Google Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ScriptedAnimationController.h"
29 #if ENABLE(REQUEST_ANIMATION_FRAME)
31 #include "DisplayRefreshMonitor.h"
32 #include "DisplayRefreshMonitorManager.h"
34 #include "DocumentLoader.h"
35 #include "FrameView.h"
36 #include "InspectorInstrumentation.h"
37 #include "RequestAnimationFrameCallback.h"
41 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
43 #include <wtf/CurrentTime.h>
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
52 ScriptedAnimationController::ScriptedAnimationController(Document* document, PlatformDisplayID displayID)
53 : m_document(document)
54 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
55 , m_animationTimer(*this, &ScriptedAnimationController::animationTimerFired)
58 windowScreenDidChange(displayID);
61 ScriptedAnimationController::~ScriptedAnimationController()
65 void ScriptedAnimationController::suspend()
70 void ScriptedAnimationController::resume()
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)
77 if (!m_suspendCount && m_callbacks.size())
81 void ScriptedAnimationController::setThrottled(bool isThrottled)
83 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
84 if (m_isThrottled == isThrottled)
87 m_isThrottled = isThrottled;
88 if (m_animationTimer.isActive()) {
89 m_animationTimer.stop();
93 UNUSED_PARAM(isThrottled);
97 ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassRefPtr<RequestAnimationFrameCallback> callback)
99 ScriptedAnimationController::CallbackId id = ++m_nextCallbackId;
100 callback->m_firedOrCancelled = false;
102 m_callbacks.append(callback);
104 InspectorInstrumentation::didRequestAnimationFrame(m_document, id);
111 void ScriptedAnimationController::cancelCallback(CallbackId id)
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);
123 void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTimeNow)
125 if (!m_callbacks.size() || m_suspendCount || (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled()))
128 double highResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow);
129 double legacyHighResNowMs = 1000.0 * m_document->loader()->timing().monotonicTimeToPseudoWallTime(monotonicTimeNow);
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);
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);
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);
147 callback->handleEvent(highResNowMs);
148 InspectorInstrumentation::didFireAnimationFrame(cookie);
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);
160 if (m_callbacks.size())
164 void ScriptedAnimationController::windowScreenDidChange(PlatformDisplayID displayID)
166 if (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled())
168 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
169 DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
171 UNUSED_PARAM(displayID);
175 void ScriptedAnimationController::scheduleAnimation()
177 if (!m_document || (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled()))
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))
186 m_isUsingTimer = true;
189 if (m_animationTimer.isActive())
192 double animationInterval = MinimumAnimationInterval;
193 #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
195 animationInterval = MinimumThrottledAnimationInterval;
198 double scheduleDelay = std::max<double>(animationInterval - (monotonicallyIncreasingTime() - m_lastAnimationFrameTimeMonotonic), 0);
199 m_animationTimer.startOneShot(scheduleDelay);
201 if (FrameView* frameView = m_document->view())
202 frameView->scheduleAnimation();
206 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
207 void ScriptedAnimationController::animationTimerFired()
209 m_lastAnimationFrameTimeMonotonic = monotonicallyIncreasingTime();
210 serviceScriptedAnimations(m_lastAnimationFrameTimeMonotonic);
212 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
213 void ScriptedAnimationController::displayRefreshFired(double monotonicTimeNow)
215 serviceScriptedAnimations(monotonicTimeNow);
221 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
222 RefPtr<DisplayRefreshMonitor> ScriptedAnimationController::createDisplayRefreshMonitor(PlatformDisplayID displayID) const
224 if (!m_document->page())
227 if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID))
230 return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID);