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 42 std::pair<EventTarget*, StringImpl*> eventTargetKey(const Event* event) 43 { 44 return std::make_pair(event->target(), event->type().impl()); 45 } 46 47 ScriptedAnimationController::ScriptedAnimationController(Document* document) 48 : m_document(document) 49 , m_nextCallbackId(0) 50 , m_suspendCount(0) 51 { 52 } 53 54 ScriptedAnimationController::~ScriptedAnimationController() 55 { 56 } 57 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 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 scheduleAnimationIfNeeded(); 82 } 83 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 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 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 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 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 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 205 void ScriptedAnimationController::enqueueEvent(PassRefPtrWillBeRawPtr<Event> event) 206 { 207 InspectorInstrumentation::didEnqueueEvent(event->target(), event.get()); 208 m_eventQueue.append(event); 209 scheduleAnimationIfNeeded(); 210 } 211 212 void ScriptedAnimationController::enqueuePerFrameEvent(PassRefPtrWillBeRawPtr<Event> event) 213 { 214 if (!m_perFrameEvents.add(eventTargetKey(event.get())).isNewEntry) 215 return; 216 enqueueEvent(event); 217 } 218 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 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