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