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 "core/dom/ScriptedAnimationController.h"
28
29 #include "core/css/MediaQueryListListener.h"
30 #include "core/dom/Document.h"
31 #include "core/dom/RequestAnimationFrameCallback.h"
32 #include "core/events/Event.h"
33 #include "core/frame/LocalDOMWindow.h"
34 #include "core/frame/FrameView.h"
35 #include "core/inspector/InspectorInstrumentation.h"
36 #include "core/inspector/InspectorTraceEvents.h"
37 #include "core/loader/DocumentLoader.h"
38 #include "platform/Logging.h"
39
40 namespace blink {
41
eventTargetKey(const Event * event)42 std::pair<EventTarget*, StringImpl*> eventTargetKey(const Event* event)
43 {
44 return std::make_pair(event->target(), event->type().impl());
45 }
46
ScriptedAnimationController(Document * document)47 ScriptedAnimationController::ScriptedAnimationController(Document* document)
48 : m_document(document)
49 , m_nextCallbackId(0)
50 , m_suspendCount(0)
51 {
52 }
53
~ScriptedAnimationController()54 ScriptedAnimationController::~ScriptedAnimationController()
55 {
56 }
57
trace(Visitor * visitor)58 void ScriptedAnimationController::trace(Visitor* visitor)
59 {
60 #if ENABLE(OILPAN)
61 visitor->trace(m_callbacks);
62 visitor->trace(m_callbacksToInvoke);
63 visitor->trace(m_document);
64 visitor->trace(m_eventQueue);
65 visitor->trace(m_mediaQueryListListeners);
66 visitor->trace(m_perFrameEvents);
67 #endif
68 }
69
suspend()70 void ScriptedAnimationController::suspend()
71 {
72 ++m_suspendCount;
73 }
74
resume()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 scheduleAnimationIfNeeded();
82 }
83
registerCallback(RequestAnimationFrameCallback * callback)84 ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(RequestAnimationFrameCallback* callback)
85 {
86 ScriptedAnimationController::CallbackId id = ++m_nextCallbackId;
87 callback->m_cancelled = false;
88 callback->m_id = id;
89 m_callbacks.append(callback);
90 scheduleAnimationIfNeeded();
91
92 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "RequestAnimationFrame", "data", InspectorAnimationFrameEvent::data(m_document, id));
93 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
94 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
95 InspectorInstrumentation::didRequestAnimationFrame(m_document, id);
96
97 return id;
98 }
99
cancelCallback(CallbackId id)100 void ScriptedAnimationController::cancelCallback(CallbackId id)
101 {
102 for (size_t i = 0; i < m_callbacks.size(); ++i) {
103 if (m_callbacks[i]->m_id == id) {
104 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "CancelAnimationFrame", "data", InspectorAnimationFrameEvent::data(m_document, id));
105 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
106 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
107 InspectorInstrumentation::didCancelAnimationFrame(m_document, id);
108 m_callbacks.remove(i);
109 return;
110 }
111 }
112 for (size_t i = 0; i < m_callbacksToInvoke.size(); ++i) {
113 if (m_callbacksToInvoke[i]->m_id == id) {
114 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "CancelAnimationFrame", "data", InspectorAnimationFrameEvent::data(m_document, id));
115 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
116 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
117 InspectorInstrumentation::didCancelAnimationFrame(m_document, id);
118 m_callbacksToInvoke[i]->m_cancelled = true;
119 // will be removed at the end of executeCallbacks()
120 return;
121 }
122 }
123 }
124
dispatchEvents()125 void ScriptedAnimationController::dispatchEvents()
126 {
127 WillBeHeapVector<RefPtrWillBeMember<Event> > events;
128 events.swap(m_eventQueue);
129 m_perFrameEvents.clear();
130
131 for (size_t i = 0; i < events.size(); ++i) {
132 EventTarget* eventTarget = events[i]->target();
133 // FIXME: we should figure out how to make dispatchEvent properly virtual to avoid
134 // special casting window.
135 // FIXME: We should not fire events for nodes that are no longer in the tree.
136 if (LocalDOMWindow* window = eventTarget->toDOMWindow())
137 window->dispatchEvent(events[i], nullptr);
138 else
139 eventTarget->dispatchEvent(events[i]);
140
141 InspectorInstrumentation::didRemoveEvent(eventTarget, events[i].get());
142 }
143 }
144
executeCallbacks(double monotonicTimeNow)145 void ScriptedAnimationController::executeCallbacks(double monotonicTimeNow)
146 {
147 // dispatchEvents() runs script which can cause the document to be destroyed.
148 if (!m_document)
149 return;
150
151 double highResNowMs = 1000.0 * m_document->loader()->timing()->monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow);
152 double legacyHighResNowMs = 1000.0 * m_document->loader()->timing()->monotonicTimeToPseudoWallTime(monotonicTimeNow);
153
154 // First, generate a list of callbacks to consider. Callbacks registered from this point
155 // on are considered only for the "next" frame, not this one.
156 ASSERT(m_callbacksToInvoke.isEmpty());
157 m_callbacksToInvoke.swap(m_callbacks);
158
159 for (size_t i = 0; i < m_callbacksToInvoke.size(); ++i) {
160 RequestAnimationFrameCallback* callback = m_callbacksToInvoke[i].get();
161 if (!callback->m_cancelled) {
162 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "FireAnimationFrame", "data", InspectorAnimationFrameEvent::data(m_document, callback->m_id));
163 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
164 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_document, callback->m_id);
165 if (callback->m_useLegacyTimeBase)
166 callback->handleEvent(legacyHighResNowMs);
167 else
168 callback->handleEvent(highResNowMs);
169 InspectorInstrumentation::didFireAnimationFrame(cookie);
170 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
171 }
172 }
173
174 m_callbacksToInvoke.clear();
175 }
176
callMediaQueryListListeners()177 void ScriptedAnimationController::callMediaQueryListListeners()
178 {
179 MediaQueryListListeners listeners;
180 listeners.swap(m_mediaQueryListListeners);
181
182 for (MediaQueryListListeners::const_iterator it = listeners.begin(), end = listeners.end();
183 it != end; ++it) {
184 (*it)->notifyMediaQueryChanged();
185 }
186 }
187
serviceScriptedAnimations(double monotonicTimeNow)188 void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTimeNow)
189 {
190 if (!m_callbacks.size() && !m_eventQueue.size() && !m_mediaQueryListListeners.size())
191 return;
192
193 if (m_suspendCount)
194 return;
195
196 RefPtrWillBeRawPtr<ScriptedAnimationController> protect(this);
197
198 callMediaQueryListListeners();
199 dispatchEvents();
200 executeCallbacks(monotonicTimeNow);
201
202 scheduleAnimationIfNeeded();
203 }
204
enqueueEvent(PassRefPtrWillBeRawPtr<Event> event)205 void ScriptedAnimationController::enqueueEvent(PassRefPtrWillBeRawPtr<Event> event)
206 {
207 InspectorInstrumentation::didEnqueueEvent(event->target(), event.get());
208 m_eventQueue.append(event);
209 scheduleAnimationIfNeeded();
210 }
211
enqueuePerFrameEvent(PassRefPtrWillBeRawPtr<Event> event)212 void ScriptedAnimationController::enqueuePerFrameEvent(PassRefPtrWillBeRawPtr<Event> event)
213 {
214 if (!m_perFrameEvents.add(eventTargetKey(event.get())).isNewEntry)
215 return;
216 enqueueEvent(event);
217 }
218
enqueueMediaQueryChangeListeners(WillBeHeapVector<RefPtrWillBeMember<MediaQueryListListener>> & listeners)219 void ScriptedAnimationController::enqueueMediaQueryChangeListeners(WillBeHeapVector<RefPtrWillBeMember<MediaQueryListListener> >& listeners)
220 {
221 for (size_t i = 0; i < listeners.size(); ++i) {
222 m_mediaQueryListListeners.add(listeners[i]);
223 }
224 scheduleAnimationIfNeeded();
225 }
226
scheduleAnimationIfNeeded()227 void ScriptedAnimationController::scheduleAnimationIfNeeded()
228 {
229 if (!m_document)
230 return;
231
232 if (m_suspendCount)
233 return;
234
235 if (!m_callbacks.size() && !m_eventQueue.size() && !m_mediaQueryListListeners.size())
236 return;
237
238 if (FrameView* frameView = m_document->view())
239 frameView->scheduleAnimation();
240 }
241
242 }
243