2011-09-18 Ilya Tikhonovsky <loislo@chromium.org>
[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 "Element.h"
33 #include "FrameView.h"
34 #include "InspectorInstrumentation.h"
35 #include "RequestAnimationFrameCallback.h"
36
37 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
38 #include <algorithm>
39 #include <wtf/CurrentTime.h>
40
41 using namespace std;
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 #endif
46
47 namespace WebCore {
48
49 ScriptedAnimationController::ScriptedAnimationController(Document* document)
50     : m_document(document)
51     , m_nextCallbackId(0)
52     , m_suspendCount(0)
53 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
54     , m_animationTimer(this, &ScriptedAnimationController::animationTimerFired)
55     , m_lastAnimationFrameTime(0)
56 #endif
57 {
58 }
59
60 void ScriptedAnimationController::suspend()
61 {
62     ++m_suspendCount;
63 }
64
65 void ScriptedAnimationController::resume()
66 {
67     --m_suspendCount;
68     if (!m_suspendCount && m_callbacks.size())
69         scheduleAnimation();
70 }
71
72 ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassRefPtr<RequestAnimationFrameCallback> callback, Element* animationElement)
73 {
74     ScriptedAnimationController::CallbackId id = m_nextCallbackId++;
75     callback->m_firedOrCancelled = false;
76     callback->m_id = id;
77     callback->m_element = animationElement;
78     m_callbacks.append(callback);
79
80     InspectorInstrumentation::didRegisterAnimationFrameCallback(m_document, id);
81
82     if (!m_suspendCount)
83         scheduleAnimation();
84     return id;
85 }
86
87 void ScriptedAnimationController::cancelCallback(CallbackId id)
88 {
89     for (size_t i = 0; i < m_callbacks.size(); ++i) {
90         if (m_callbacks[i]->m_id == id) {
91             m_callbacks[i]->m_firedOrCancelled = true;
92             InspectorInstrumentation::didCancelAnimationFrameCallback(m_document, id);
93             m_callbacks.remove(i);
94             return;
95         }
96     }
97 }
98
99 void ScriptedAnimationController::serviceScriptedAnimations(DOMTimeStamp time)
100 {
101     if (!m_callbacks.size() || m_suspendCount)
102         return;
103     // We want to run the callback for all elements in the document that have registered
104     // for a callback and that are visible.  Running the callbacks can cause new callbacks
105     // to be registered, existing callbacks to be cancelled, and elements to gain or lose
106     // visibility so this code has to iterate carefully.
107
108     // FIXME: Currently, this code doesn't do any visibility tests beyond checking display:
109
110     // First, generate a list of callbacks to consider.  Callbacks registered from this point
111     // on are considered only for the "next" frame, not this one.
112     CallbackList callbacks(m_callbacks);
113
114     // Firing the callback may cause the visibility of other elements to change.  To avoid
115     // missing any callbacks, we keep iterating through the list of candiate callbacks and firing
116     // them until nothing new becomes visible.
117     bool firedCallback;
118     do {
119         firedCallback = false;
120         // A previous iteration may have invalidated style (or layout).  Update styles for each iteration
121         // for now since all we check is the existence of a renderer.
122         m_document->updateStyleIfNeeded();
123         for (size_t i = 0; i < callbacks.size(); ++i) {
124             RequestAnimationFrameCallback* callback = callbacks[i].get();
125             if (!callback->m_firedOrCancelled && (!callback->m_element || callback->m_element->renderer())) {
126                 callback->m_firedOrCancelled = true;
127                 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrameEvent(m_document, callback->m_id);
128                 callback->handleEvent(time);
129                 InspectorInstrumentation::didFireAnimationFrameEvent(cookie);
130                 firedCallback = true;
131                 callbacks.remove(i);
132                 break;
133             }
134         }
135     } while (firedCallback);
136
137     // Remove any callbacks we fired from the list of pending callbacks.
138     for (size_t i = 0; i < m_callbacks.size();) {
139         if (m_callbacks[i]->m_firedOrCancelled)
140             m_callbacks.remove(i);
141         else
142             ++i;
143     }
144
145     if (m_callbacks.size())
146         scheduleAnimation();
147 }
148     
149 void ScriptedAnimationController::scheduleAnimation()
150 {
151 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
152     double scheduleDelay = max<double>(MinimumAnimationInterval - (currentTime() - m_lastAnimationFrameTime), 0);
153     m_animationTimer.startOneShot(scheduleDelay);
154 #else
155     if (FrameView* frameView = m_document->view())
156         frameView->scheduleAnimation();
157 #endif
158 }
159
160 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
161 void ScriptedAnimationController::animationTimerFired(Timer<ScriptedAnimationController>*)
162 {
163     m_lastAnimationFrameTime = currentTime();
164     serviceScriptedAnimations(m_lastAnimationFrameTime);
165 }
166 #endif
167
168 }
169
170 #endif
171