Home | History | Annotate | Download | only in dom
      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