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