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 50namespace WebCore { 51 52ScriptedAnimationController::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 68ScriptedAnimationController::~ScriptedAnimationController() 69{ 70} 71 72void ScriptedAnimationController::suspend() 73{ 74 ++m_suspendCount; 75} 76 77void 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 88void 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 104ScriptedAnimationController::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 118void 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 130void 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 171void 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 182void 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) 214void ScriptedAnimationController::animationTimerFired(Timer<ScriptedAnimationController>&) 215{ 216 m_lastAnimationFrameTimeMonotonic = monotonicallyIncreasingTime(); 217 serviceScriptedAnimations(m_lastAnimationFrameTimeMonotonic); 218} 219#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) 220void ScriptedAnimationController::displayRefreshFired(double monotonicTimeNow) 221{ 222 serviceScriptedAnimations(monotonicTimeNow); 223} 224#endif 225#endif 226 227 228#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) 229PassRefPtr<DisplayRefreshMonitor> ScriptedAnimationController::createDisplayRefreshMonitor(PlatformDisplayID displayID) const 230{ 231 return m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID); 232} 233#endif 234 235 236} 237 238#endif 239