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