blob: 3d85454ae1f9d789b0aca458de407c6436825ba3 [file]
// Copyright 2023 The Chromium Authors
// 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/lcp_critical_path_predictor/lcp_script_observer.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/loader/lcp_critical_path_predictor_util.h"
#include "third_party/blink/renderer/bindings/core/v8/capture_source_location.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
namespace blink {
LCPScriptObserver::~LCPScriptObserver() = default;
HashSet<String> LCPScriptObserver::GetExecutingScriptUrls() {
HashSet<String> script_urls;
// Gather sync and async scripts in execution
for (const probe::ExecuteScript* probe : stack_script_probes_) {
if (probe->script_url.empty()) {
continue;
}
script_urls.insert(probe->script_url);
}
// Gather async functions in execution
for (const probe::CallFunction* probe : stack_function_probes_) {
String url = GetScriptUrlFromCallFunctionProbe(probe);
if (url.empty()) {
continue;
}
script_urls.insert(url);
}
// Gather (promise) microtasks in execution. This is required as Probes
// do not yet have an implementation that covers microtasks.
v8::Isolate* isolate = v8::Isolate::TryGetCurrent();
auto v8_stack_urls = CaptureScriptUrlsFromCurrentStack(isolate, 0);
for (auto& url : v8_stack_urls) {
if (url.empty()) {
continue;
}
script_urls.insert(url);
}
const String document_url = local_root_->GetDocument()->Url();
if (!document_url.empty()) {
script_urls.erase(document_url);
}
return script_urls;
}
String LCPScriptObserver::GetScriptUrlFromCallFunctionProbe(
const probe::CallFunction* probe) {
v8::Local<v8::Value> resource_name =
probe->function->GetScriptOrigin().ResourceName();
String script_url;
if (!resource_name.IsEmpty()) {
v8::Isolate* isolate = ToIsolate(local_root_);
v8::MaybeLocal<v8::String> resource_name_string =
resource_name->ToString(isolate->GetCurrentContext());
if (!resource_name_string.IsEmpty()) {
script_url = ToCoreString(isolate, resource_name_string.ToLocalChecked());
}
}
return script_url;
}
LCPScriptObserver::LCPScriptObserver(LocalFrame* local_root)
: local_root_(local_root) {
CHECK(blink::LcppScriptObserverEnabled());
local_root_->GetProbeSink()->AddLCPScriptObserver(this);
}
void LCPScriptObserver::Will(const probe::ExecuteScript& probe) {
stack_script_probes_.push_back(&probe);
}
void LCPScriptObserver::Did(const probe::ExecuteScript& probe) {
DCHECK(!stack_script_probes_.empty());
stack_script_probes_.pop_back();
}
void LCPScriptObserver::Will(const probe::CallFunction& probe) {
// Do not process nested microtasks as that might potentially lead to a
// slowdown of custom element callbacks.
if (probe.depth) {
return;
}
stack_function_probes_.push_back(&probe);
}
void LCPScriptObserver::Did(const probe::CallFunction& probe) {
if (probe.depth) {
return;
}
DCHECK(!stack_function_probes_.empty());
stack_function_probes_.pop_back();
}
void LCPScriptObserver::Trace(Visitor* visitor) const {
visitor->Trace(local_root_);
}
void LCPScriptObserver::Shutdown() {
if (!local_root_) {
return;
}
local_root_->GetProbeSink()->RemoveLCPScriptObserver(this);
local_root_ = nullptr;
}
} // namespace blink