blob: 1cb5429dedd0b259f6e385d4c7c15eda705511fa [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/timing/performance_observer.h"
#include <algorithm>
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_performance_observer_callback.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/use_counter.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
#include "third_party/blink/renderer/core/performance_entry_names.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
#include "third_party/blink/renderer/core/timing/performance_entry.h"
#include "third_party/blink/renderer/core/timing/performance_observer_entry_list.h"
#include "third_party/blink/renderer/core/timing/performance_observer_init.h"
#include "third_party/blink/renderer/core/timing/window_performance.h"
#include "third_party/blink/renderer/core/timing/worker_global_scope_performance.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/timer.h"
namespace blink {
PerformanceObserver* PerformanceObserver::Create(
ScriptState* script_state,
V8PerformanceObserverCallback* callback) {
LocalDOMWindow* window = ToLocalDOMWindow(script_state->GetContext());
ExecutionContext* context = ExecutionContext::From(script_state);
if (window) {
UseCounter::Count(context, WebFeature::kPerformanceObserverForWindow);
return MakeGarbageCollected<PerformanceObserver>(
context, DOMWindowPerformance::performance(*window), callback);
}
if (auto* scope = DynamicTo<WorkerGlobalScope>(context)) {
UseCounter::Count(context, WebFeature::kPerformanceObserverForWorker);
return MakeGarbageCollected<PerformanceObserver>(
context, WorkerGlobalScopePerformance::performance(*scope), callback);
}
V8ThrowException::ThrowTypeError(
script_state->GetIsolate(),
ExceptionMessages::FailedToConstruct(
"PerformanceObserver",
"No 'worker' or 'window' in current context."));
return nullptr;
}
// static
Vector<AtomicString> PerformanceObserver::supportedEntryTypes(
ScriptState* script_state) {
// The list of supported types, in alphabetical order.
Vector<AtomicString> supportedEntryTypes;
auto* execution_context = ExecutionContext::From(script_state);
if (execution_context->IsDocument()) {
if (origin_trials::ElementTimingEnabled(execution_context))
supportedEntryTypes.push_back(performance_entry_names::kElement);
if (origin_trials::EventTimingEnabled(execution_context)) {
supportedEntryTypes.push_back(performance_entry_names::kEvent);
supportedEntryTypes.push_back(performance_entry_names::kFirstInput);
}
if (origin_trials::LayoutJankAPIEnabled(execution_context))
supportedEntryTypes.push_back(performance_entry_names::kLayoutJank);
supportedEntryTypes.push_back(performance_entry_names::kLongtask);
}
supportedEntryTypes.push_back(performance_entry_names::kMark);
supportedEntryTypes.push_back(performance_entry_names::kMeasure);
if (execution_context->IsDocument()) {
supportedEntryTypes.push_back(performance_entry_names::kNavigation);
supportedEntryTypes.push_back(performance_entry_names::kPaint);
}
supportedEntryTypes.push_back(performance_entry_names::kResource);
return supportedEntryTypes;
}
PerformanceObserver::PerformanceObserver(
ExecutionContext* execution_context,
Performance* performance,
V8PerformanceObserverCallback* callback)
: ContextClient(execution_context),
execution_context_(execution_context),
callback_(callback),
performance_(performance),
filter_options_(PerformanceEntry::kInvalid),
type_(PerformanceObserverType::kUnknown),
is_registered_(false) {
DCHECK(performance_);
}
void PerformanceObserver::observe(const PerformanceObserverInit* observer_init,
ExceptionState& exception_state) {
if (!performance_) {
exception_state.ThrowTypeError(
"Window/worker may be destroyed? Performance target is invalid.");
return;
}
if (observer_init->hasEntryTypes()) {
if (observer_init->hasType()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"An observe() call MUST NOT include both entryTypes and type.");
return;
}
if (type_ == PerformanceObserverType::kTypeObserver) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidModificationError,
"This observer has performed observe({type:...}, therefore it cannot "
"perform observe({entryTypes:...})");
return;
}
type_ = PerformanceObserverType::kEntryTypesObserver;
PerformanceEntryTypeMask entry_types = PerformanceEntry::kInvalid;
const Vector<String>& sequence = observer_init->entryTypes();
for (const auto& entry_type_string : sequence) {
entry_types |=
PerformanceEntry::ToEntryTypeEnum(AtomicString(entry_type_string));
}
if (entry_types == PerformanceEntry::kInvalid) {
String message =
"The Performance Observer MUST have at least one valid entryType in "
"its "
"entryTypes attribute.";
GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
kJSMessageSource, kWarningMessageLevel, message));
return;
}
filter_options_ = entry_types;
} else {
if (!observer_init->hasType()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"An observe() call MUST include either entryTypes or type.");
return;
}
if (type_ == PerformanceObserverType::kEntryTypesObserver) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidModificationError,
"This observer has performed observe({entryTypes:...}, therefore it "
"cannot perform observe({type:...})");
return;
}
type_ = PerformanceObserverType::kTypeObserver;
PerformanceEntryType entry_type =
PerformanceEntry::ToEntryTypeEnum(AtomicString(observer_init->type()));
if (entry_type == PerformanceEntry::kInvalid) {
String message =
"The Performance Observer MUST have a valid entryType in its "
"type attribute.";
GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
kJSMessageSource, kWarningMessageLevel, message));
return;
}
filter_options_ |= entry_type;
}
if (filter_options_ & PerformanceEntry::kLayoutJank) {
UseCounter::Count(GetExecutionContext(),
WebFeature::kLayoutJankExplicitlyRequested);
}
if (is_registered_)
performance_->UpdatePerformanceObserverFilterOptions();
else
performance_->RegisterPerformanceObserver(*this);
is_registered_ = true;
}
void PerformanceObserver::disconnect() {
performance_entries_.clear();
if (performance_)
performance_->UnregisterPerformanceObserver(*this);
is_registered_ = false;
}
PerformanceEntryVector PerformanceObserver::takeRecords() {
PerformanceEntryVector performance_entries;
performance_entries.swap(performance_entries_);
return performance_entries;
}
void PerformanceObserver::EnqueuePerformanceEntry(PerformanceEntry& entry) {
performance_entries_.push_back(&entry);
if (performance_)
performance_->ActivateObserver(*this);
}
bool PerformanceObserver::HasPendingActivity() const {
return is_registered_;
}
bool PerformanceObserver::ShouldBeSuspended() const {
return execution_context_->IsContextPaused();
}
void PerformanceObserver::Deliver() {
DCHECK(!ShouldBeSuspended());
if (!GetExecutionContext())
return;
if (performance_entries_.IsEmpty())
return;
PerformanceEntryVector performance_entries;
performance_entries.swap(performance_entries_);
PerformanceObserverEntryList* entry_list =
MakeGarbageCollected<PerformanceObserverEntryList>(performance_entries);
callback_->InvokeAndReportException(this, entry_list, this);
}
void PerformanceObserver::Trace(blink::Visitor* visitor) {
visitor->Trace(execution_context_);
visitor->Trace(callback_);
visitor->Trace(performance_);
visitor->Trace(performance_entries_);
ScriptWrappable::Trace(visitor);
ContextClient::Trace(visitor);
}
} // namespace blink