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/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