flutter_flutter/engine/core/dom/ScriptedAnimationController.cpp
Elliott Sprehn 53e76b3c58 Make all callers of scheduleAnimation() use scheduleVisualUpdate().
The only scheduleAnimation() methods left are going through the layers of
client interfaces. I'll rename and remove those next. This is working
towards making requestAnimationFrame() not cause a full repaint and also
making it more clear to callers that scheduling a frame causes a raster.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/875283003
2015-01-26 15:42:33 -08:00

234 lines
9.4 KiB
C++

/*
* Copyright (C) 2011 Google Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sky/engine/config.h"
#include "sky/engine/core/dom/ScriptedAnimationController.h"
#include "sky/engine/core/css/MediaQueryListListener.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/dom/RequestAnimationFrameCallback.h"
#include "sky/engine/core/events/Event.h"
#include "sky/engine/core/frame/FrameView.h"
#include "sky/engine/core/frame/LocalDOMWindow.h"
#include "sky/engine/core/inspector/InspectorTraceEvents.h"
#include "sky/engine/platform/Logging.h"
namespace blink {
std::pair<EventTarget*, StringImpl*> eventTargetKey(const Event* event)
{
return std::make_pair(event->target(), event->type().impl());
}
ScriptedAnimationController::ScriptedAnimationController(Document* document)
: m_document(document)
, m_nextCallbackId(0)
, m_suspendCount(0)
{
}
ScriptedAnimationController::~ScriptedAnimationController()
{
}
void ScriptedAnimationController::suspend()
{
++m_suspendCount;
WTF_LOG(ScriptedAnimationController, "suspend: count = %d", m_suspendCount);
}
void ScriptedAnimationController::resume()
{
// It would be nice to put an ASSERT(m_suspendCount > 0) here, but in WK1 resume() can be called
// even when suspend hasn't (if a tab was created in the background).
if (m_suspendCount > 0)
--m_suspendCount;
WTF_LOG(ScriptedAnimationController, "resume: count = %d", m_suspendCount);
scheduleAnimationIfNeeded();
}
ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassOwnPtr<RequestAnimationFrameCallback> callback)
{
ScriptedAnimationController::CallbackId id = ++m_nextCallbackId;
WTF_LOG(ScriptedAnimationController, "registerCallback: id = %d", id);
callback->m_cancelled = false;
callback->m_id = id;
m_callbacks.append(callback);
scheduleAnimationIfNeeded();
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "RequestAnimationFrame", TRACE_EVENT_SCOPE_PROCESS, "data", InspectorAnimationFrameEvent::data(m_document, id));
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", TRACE_EVENT_SCOPE_PROCESS, "stack", InspectorCallStackEvent::currentCallStack());
return id;
}
void ScriptedAnimationController::cancelCallback(CallbackId id)
{
WTF_LOG(ScriptedAnimationController, "cancelCallback: id = %d", id);
for (size_t i = 0; i < m_callbacks.size(); ++i) {
if (m_callbacks[i]->m_id == id) {
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "CancelAnimationFrame", TRACE_EVENT_SCOPE_PROCESS, "data", InspectorAnimationFrameEvent::data(m_document, id));
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", TRACE_EVENT_SCOPE_PROCESS, "stack", InspectorCallStackEvent::currentCallStack());
m_callbacks.remove(i);
return;
}
}
for (size_t i = 0; i < m_callbacksToInvoke.size(); ++i) {
if (m_callbacksToInvoke[i]->m_id == id) {
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "CancelAnimationFrame", TRACE_EVENT_SCOPE_PROCESS, "data", InspectorAnimationFrameEvent::data(m_document, id));
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", TRACE_EVENT_SCOPE_PROCESS, "stack", InspectorCallStackEvent::currentCallStack());
m_callbacksToInvoke[i]->m_cancelled = true;
// will be removed at the end of executeCallbacks()
return;
}
}
}
void ScriptedAnimationController::dispatchEvents()
{
Vector<RefPtr<Event> > events;
events.swap(m_eventQueue);
m_perFrameEvents.clear();
for (size_t i = 0; i < events.size(); ++i) {
EventTarget* eventTarget = events[i]->target();
// FIXME: we should figure out how to make dispatchEvent properly virtual to avoid
// special casting window.
// FIXME: We should not fire events for nodes that are no longer in the tree.
if (LocalDOMWindow* window = eventTarget->toDOMWindow())
window->dispatchEvent(events[i], nullptr);
else
eventTarget->dispatchEvent(events[i]);
}
}
void ScriptedAnimationController::executeCallbacks(double monotonicTimeNow)
{
// dispatchEvents() runs script which can cause the document to be destroyed.
if (!m_document)
return;
double highResNowMs = 1000.0 * m_document->timing()->monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow);
double legacyHighResNowMs = 1000.0 * m_document->timing()->monotonicTimeToPseudoWallTime(monotonicTimeNow);
// First, generate a list of callbacks to consider. Callbacks registered from this point
// on are considered only for the "next" frame, not this one.
ASSERT(m_callbacksToInvoke.isEmpty());
m_callbacksToInvoke.swap(m_callbacks);
for (size_t i = 0; i < m_callbacksToInvoke.size(); ++i) {
RequestAnimationFrameCallback* callback = m_callbacksToInvoke[i].get();
if (!callback->m_cancelled) {
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "FireAnimationFrame", "data", InspectorAnimationFrameEvent::data(m_document, callback->m_id));
if (callback->m_useLegacyTimeBase)
callback->handleEvent(legacyHighResNowMs);
else
callback->handleEvent(highResNowMs);
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", TRACE_EVENT_SCOPE_PROCESS, "data", InspectorUpdateCountersEvent::data());
}
}
m_callbacksToInvoke.clear();
}
void ScriptedAnimationController::callMediaQueryListListeners()
{
MediaQueryListListeners listeners;
listeners.swap(m_mediaQueryListListeners);
for (MediaQueryListListeners::const_iterator it = listeners.begin(), end = listeners.end();
it != end; ++it) {
(*it)->notifyMediaQueryChanged();
}
}
void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTimeNow)
{
WTF_LOG(ScriptedAnimationController, "serviceScriptedAnimations: #callbacks = %d, #events = %d, #mediaQueryListListeners = %d, count = %d",
static_cast<int>(m_callbacks.size()),
static_cast<int>(m_eventQueue.size()),
static_cast<int>(m_mediaQueryListListeners.size()),
m_suspendCount);
if (!m_callbacks.size() && !m_eventQueue.size() && !m_mediaQueryListListeners.size())
return;
if (m_suspendCount)
return;
RefPtr<ScriptedAnimationController> protect(this);
callMediaQueryListListeners();
dispatchEvents();
executeCallbacks(monotonicTimeNow);
scheduleAnimationIfNeeded();
}
void ScriptedAnimationController::enqueueEvent(PassRefPtr<Event> event)
{
WTF_LOG(ScriptedAnimationController, "enqueueEvent");
m_eventQueue.append(event);
scheduleAnimationIfNeeded();
}
void ScriptedAnimationController::enqueuePerFrameEvent(PassRefPtr<Event> event)
{
if (!m_perFrameEvents.add(eventTargetKey(event.get())).isNewEntry)
return;
enqueueEvent(event);
}
void ScriptedAnimationController::enqueueMediaQueryChangeListeners(Vector<RefPtr<MediaQueryListListener> >& listeners)
{
WTF_LOG(ScriptedAnimationController, "enqueueMediaQueryChangeListeners");
for (size_t i = 0; i < listeners.size(); ++i) {
m_mediaQueryListListeners.add(listeners[i]);
}
scheduleAnimationIfNeeded();
}
void ScriptedAnimationController::scheduleAnimationIfNeeded()
{
WTF_LOG(ScriptedAnimationController, "scheduleAnimationIfNeeded: document = %d, count = %d, #callbacks = %d, #events = %d, #mediaQueryListListeners =%d, frameView = %d",
m_document ? 1 : 0, m_suspendCount,
static_cast<int>(m_callbacks.size()),
static_cast<int>(m_eventQueue.size()),
static_cast<int>(m_mediaQueryListListeners.size()),
m_document && m_document->view() ? 1 : 0);
if (!m_document)
return;
if (m_suspendCount)
return;
if (!m_callbacks.size() && !m_eventQueue.size() && !m_mediaQueryListListeners.size())
return;
// TODO(esprehn): This causes a full raster on every raf even if nothing changed.
m_document->scheduleVisualUpdate();
}
}