blob: 094fef963e36c8b436653b3594075ad3c615f3b3 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/dom/frame_request_callback_collection.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
namespace blink {
FrameRequestCallbackCollection::FrameRequestCallbackCollection(
ExecutionContext* context)
: context_(context) {}
FrameRequestCallbackCollection::CallbackId
FrameRequestCallbackCollection::RegisterFrameCallback(FrameCallback* callback) {
FrameRequestCallbackCollection::CallbackId id = ++next_callback_id_;
callback->SetIsCancelled(false);
callback->SetId(id);
frame_callbacks_.push_back(callback);
TRACE_EVENT_INSTANT1("devtools.timeline", "RequestAnimationFrame",
TRACE_EVENT_SCOPE_THREAD, "data",
inspector_animation_frame_event::Data(context_, id));
probe::AsyncTaskScheduledBreakable(context_, "requestAnimationFrame",
callback->async_task_id());
return id;
}
void FrameRequestCallbackCollection::CancelFrameCallback(CallbackId id) {
CancelCallbackInternal(id, "CancelAnimationFrame", "cancelAnimationFrame");
}
void FrameRequestCallbackCollection::CancelPostFrameCallback(CallbackId id) {
CancelCallbackInternal(id, "CancelPostAnimationFrame",
"cancelPostAnimationFrame");
}
void FrameRequestCallbackCollection::CancelCallbackInternal(
CallbackId id,
const char* trace_event_name,
const char* probe_name) {
for (wtf_size_t i = 0; i < frame_callbacks_.size(); ++i) {
if (frame_callbacks_[i]->Id() == id) {
probe::AsyncTaskCanceledBreakable(context_, probe_name,
frame_callbacks_[i]->async_task_id());
frame_callbacks_.EraseAt(i);
TRACE_EVENT_INSTANT1("devtools.timeline", trace_event_name,
TRACE_EVENT_SCOPE_THREAD, "data",
inspector_animation_frame_event::Data(context_, id));
return;
}
}
for (wtf_size_t i = 0; i < post_frame_callbacks_.size(); ++i) {
if (post_frame_callbacks_[i]->Id() == id) {
probe::AsyncTaskCanceledBreakable(
context_, probe_name, post_frame_callbacks_[i]->async_task_id());
post_frame_callbacks_.EraseAt(i);
TRACE_EVENT_INSTANT1("devtools.timeline", trace_event_name,
TRACE_EVENT_SCOPE_THREAD, "data",
inspector_animation_frame_event::Data(context_, id));
return;
}
}
for (const auto& callback : callbacks_to_invoke_) {
if (callback->Id() == id) {
probe::AsyncTaskCanceledBreakable(context_, probe_name,
callback->async_task_id());
TRACE_EVENT_INSTANT1("devtools.timeline", trace_event_name,
TRACE_EVENT_SCOPE_THREAD, "data",
inspector_animation_frame_event::Data(context_, id));
callback->SetIsCancelled(true);
// will be removed at the end of ExecuteCallbacks() or
// ExecutePostFrameCallbacks()
return;
}
}
}
void FrameRequestCallbackCollection::ExecuteFrameCallbacks(
double high_res_now_ms,
double high_res_now_ms_legacy) {
TRACE_EVENT0("blink",
"FrameRequestCallbackCollection::ExecuteFrameCallbacks");
ExecuteCallbacksInternal(frame_callbacks_, "FireAnimationFrame",
"requestAnimationFrame", high_res_now_ms,
high_res_now_ms_legacy);
}
void FrameRequestCallbackCollection::ExecutePostFrameCallbacks(
double high_res_now_ms,
double high_res_now_ms_legacy) {
ExecuteCallbacksInternal(post_frame_callbacks_, "FirePostAnimationFrame",
"requestPostAnimationFrame", high_res_now_ms,
high_res_now_ms_legacy);
}
void FrameRequestCallbackCollection::ExecuteCallbacksInternal(
CallbackList& callbacks,
const char* trace_event_name,
const char* probe_name,
double high_res_now_ms,
double high_res_now_ms_legacy) {
// First, generate a list of callbacks to consider. Callbacks registered from
// this point on are considered only for the "next" frame, not this one.
DCHECK(callbacks_to_invoke_.IsEmpty());
swap(callbacks_to_invoke_, callbacks);
for (const auto& callback : callbacks_to_invoke_) {
// When the ExecutionContext is destroyed (e.g. an iframe is detached),
// there is no path to perform wrapper tracing for the callbacks. In such a
// case, the callback functions may already have been collected by V8 GC.
// Since it's possible that a callback function being invoked detaches an
// iframe, we need to check the condition for each callback.
if (context_->IsContextDestroyed())
break;
if (callback->IsCancelled()) {
// Another requestAnimationFrame callback already cancelled this one
UseCounter::Count(context_,
WebFeature::kAnimationFrameCancelledWithinFrame);
continue;
}
TRACE_EVENT1(
"devtools.timeline", trace_event_name, "data",
inspector_animation_frame_event::Data(context_, callback->Id()));
probe::AsyncTask async_task(context_, callback->async_task_id());
probe::UserCallback probe(context_, probe_name, AtomicString(), true);
if (callback->GetUseLegacyTimeBase())
callback->Invoke(high_res_now_ms_legacy);
else
callback->Invoke(high_res_now_ms);
}
callbacks_to_invoke_.clear();
}
FrameRequestCallbackCollection::CallbackId
FrameRequestCallbackCollection::RegisterPostFrameCallback(
FrameCallback* callback) {
CallbackId id = ++next_callback_id_;
callback->SetIsCancelled(false);
callback->SetId(id);
post_frame_callbacks_.push_back(callback);
TRACE_EVENT_INSTANT1("devtools.timeline", "RequestPostAnimationFrame",
TRACE_EVENT_SCOPE_THREAD, "data",
inspector_animation_frame_event::Data(context_, id));
probe::AsyncTaskScheduledBreakable(context_, "requestPostAnimationFrame",
callback->async_task_id());
return id;
}
void FrameRequestCallbackCollection::Trace(Visitor* visitor) {
visitor->Trace(frame_callbacks_);
visitor->Trace(post_frame_callbacks_);
visitor->Trace(callbacks_to_invoke_);
visitor->Trace(context_);
}
FrameRequestCallbackCollection::V8FrameCallback::V8FrameCallback(
V8FrameRequestCallback* callback)
: callback_(callback) {}
void FrameRequestCallbackCollection::V8FrameCallback::Trace(
blink::Visitor* visitor) {
visitor->Trace(callback_);
FrameRequestCallbackCollection::FrameCallback::Trace(visitor);
}
void FrameRequestCallbackCollection::V8FrameCallback::Invoke(
double highResTime) {
callback_->InvokeAndReportException(nullptr, highResTime);
}
} // namespace blink