Commit 4b4377e4 authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

[PM] Wire up V8 context tracking.

This hooks up the V8ContextTracker to actual v8::Context / ScriptState
lifetimes in renderers by introducing IPC. It also migrates the
V8ContextTracker data structures to mojom equivalents.

BUG=1080672

Change-Id: I7f88e40c64faf183d2fcc31fc05b8e59c92e21a0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2487702
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarJoe Mason <joenotcharles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823728}
parent f89a9f12
...@@ -6,5 +6,8 @@ mojom("mojom") { ...@@ -6,5 +6,8 @@ mojom("mojom") {
cpp_only = true cpp_only = true
disable_variants = true disable_variants = true
deps = [ "//mojo/public/mojom/base" ] deps = [
"//mojo/public/mojom/base",
"//third_party/blink/public/mojom/tokens",
]
} }
...@@ -144,8 +144,6 @@ static_library("performance_manager") { ...@@ -144,8 +144,6 @@ static_library("performance_manager") {
"v8_memory/v8_context_tracker_helpers.h", "v8_memory/v8_context_tracker_helpers.h",
"v8_memory/v8_context_tracker_internal.cc", "v8_memory/v8_context_tracker_internal.cc",
"v8_memory/v8_context_tracker_internal.h", "v8_memory/v8_context_tracker_internal.h",
"v8_memory/v8_context_tracker_types.cc",
"v8_memory/v8_context_tracker_types.h",
"v8_memory/v8_detailed_memory.cc", "v8_memory/v8_detailed_memory.cc",
"v8_memory/web_memory_aggregator.cc", "v8_memory/web_memory_aggregator.cc",
"v8_memory/web_memory_aggregator.h", "v8_memory/web_memory_aggregator.h",
...@@ -290,6 +288,7 @@ source_set("browser_tests") { ...@@ -290,6 +288,7 @@ source_set("browser_tests") {
"mechanisms/tab_loading_frame_navigation_scheduler_browsertest.cc", "mechanisms/tab_loading_frame_navigation_scheduler_browsertest.cc",
"performance_manager_browsertest.cc", "performance_manager_browsertest.cc",
"render_process_host_proxy_browsertest.cc", "render_process_host_proxy_browsertest.cc",
"v8_memory/v8_context_tracker_browsertest.cc",
] ]
deps = [ deps = [
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "components/performance_manager/graph/frame_node_impl.h" #include "components/performance_manager/graph/frame_node_impl.h"
#include "components/performance_manager/graph/graph_impl.h" #include "components/performance_manager/graph/graph_impl.h"
#include "components/performance_manager/graph/page_node_impl.h" #include "components/performance_manager/graph/page_node_impl.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h"
namespace performance_manager { namespace performance_manager {
...@@ -42,6 +43,27 @@ void ProcessNodeImpl::SetMainThreadTaskLoadIsLow( ...@@ -42,6 +43,27 @@ void ProcessNodeImpl::SetMainThreadTaskLoadIsLow(
main_thread_task_load_is_low); main_thread_task_load_is_low);
} }
void ProcessNodeImpl::OnV8ContextCreated(
mojom::V8ContextDescriptionPtr description,
mojom::IframeAttributionDataPtr iframe_attribution_data) {
if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph())) {
tracker->OnV8ContextCreated(PassKey(), this, *description,
std::move(iframe_attribution_data));
}
}
void ProcessNodeImpl::OnV8ContextDetached(
const blink::V8ContextToken& v8_context_token) {
if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph()))
tracker->OnV8ContextDetached(PassKey(), this, v8_context_token);
}
void ProcessNodeImpl::OnV8ContextDestroyed(
const blink::V8ContextToken& v8_context_token) {
if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph()))
tracker->OnV8ContextDestroyed(PassKey(), this, v8_context_token);
}
void ProcessNodeImpl::SetProcessExitStatus(int32_t exit_status) { void ProcessNodeImpl::SetProcessExitStatus(int32_t exit_status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// This may occur as the first event seen in the case where the process // This may occur as the first event seen in the case where the process
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "components/performance_manager/graph/properties.h" #include "components/performance_manager/graph/properties.h"
#include "components/performance_manager/public/graph/process_node.h" #include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/public/mojom/coordination_unit.mojom.h" #include "components/performance_manager/public/mojom/coordination_unit.mojom.h"
#include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
#include "components/performance_manager/public/render_process_host_proxy.h" #include "components/performance_manager/public/render_process_host_proxy.h"
#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver.h"
...@@ -58,6 +59,13 @@ class ProcessNodeImpl ...@@ -58,6 +59,13 @@ class ProcessNodeImpl
// mojom::ProcessCoordinationUnit implementation: // mojom::ProcessCoordinationUnit implementation:
void SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low) override; void SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low) override;
void OnV8ContextCreated(
mojom::V8ContextDescriptionPtr description,
mojom::IframeAttributionDataPtr iframe_attribution_data) override;
void OnV8ContextDetached(
const blink::V8ContextToken& v8_context_token) override;
void OnV8ContextDestroyed(
const blink::V8ContextToken& v8_context_token) override;
void SetProcessExitStatus(int32_t exit_status); void SetProcessExitStatus(int32_t exit_status);
void SetProcess(base::Process process, base::Time launch_time); void SetProcess(base::Process process, base::Time launch_time);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "components/performance_manager/public/decorators/page_live_state_decorator.h" #include "components/performance_manager/public/decorators/page_live_state_decorator.h"
#include "components/performance_manager/public/decorators/tab_properties_decorator.h" #include "components/performance_manager/public/decorators/tab_properties_decorator.h"
#include "components/performance_manager/public/graph/graph.h" #include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h"
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
#include "components/performance_manager/public/decorators/site_data_recorder.h" #include "components/performance_manager/public/decorators/site_data_recorder.h"
...@@ -51,6 +52,9 @@ void DefaultGraphCreatedCallback( ...@@ -51,6 +52,9 @@ void DefaultGraphCreatedCallback(
graph->PassToGraph(std::make_unique<SiteDataRecorder>()); graph->PassToGraph(std::make_unique<SiteDataRecorder>());
#endif #endif
// This depends on ExecutionContextRegistry, so must be added afterwards.
graph->PassToGraph(std::make_unique<v8_memory::V8ContextTracker>());
// Run graph created callbacks. // Run graph created callbacks.
std::move(external_graph_created_callback).Run(graph); std::move(external_graph_created_callback).Run(graph);
if (*GetAdditionalGraphCreatedCallback()) if (*GetAdditionalGraphCreatedCallback())
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import("//mojo/public/tools/bindings/mojom.gni") import("//mojo/public/tools/bindings/mojom.gni")
mojom_component("mojom") { mojom_component("mojom") {
output_prefix = "performace_manager_public_mojom" output_prefix = "performance_manager_public_mojom"
macro_prefix = "PERFORMANCE_MANAGER_PUBLIC_MOJOM" macro_prefix = "PERFORMANCE_MANAGER_PUBLIC_MOJOM"
cpp_only = true cpp_only = true
...@@ -13,11 +13,13 @@ mojom_component("mojom") { ...@@ -13,11 +13,13 @@ mojom_component("mojom") {
sources = [ sources = [
"coordination_unit.mojom", "coordination_unit.mojom",
"lifecycle.mojom", "lifecycle.mojom",
"v8_contexts.mojom",
"web_memory.mojom", "web_memory.mojom",
] ]
public_deps = [ public_deps = [
"//mojo/public/mojom/base", "//mojo/public/mojom/base",
"//third_party/blink/public/mojom/tokens",
"//ui/gfx/geometry/mojom", "//ui/gfx/geometry/mojom",
] ]
} }
...@@ -8,6 +8,8 @@ import "mojo/public/mojom/base/process_id.mojom"; ...@@ -8,6 +8,8 @@ import "mojo/public/mojom/base/process_id.mojom";
import "mojo/public/mojom/base/time.mojom"; import "mojo/public/mojom/base/time.mojom";
import "components/performance_manager/public/mojom/lifecycle.mojom"; import "components/performance_manager/public/mojom/lifecycle.mojom";
import "components/performance_manager/public/mojom/web_memory.mojom"; import "components/performance_manager/public/mojom/web_memory.mojom";
import "components/performance_manager/public/mojom/v8_contexts.mojom";
import "third_party/blink/public/mojom/tokens/tokens.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom";
// Any new type here needs to be mirrored between coordination_unit_types.h and // Any new type here needs to be mirrored between coordination_unit_types.h and
...@@ -79,4 +81,36 @@ interface DocumentCoordinationUnit { ...@@ -79,4 +81,36 @@ interface DocumentCoordinationUnit {
interface ProcessCoordinationUnit { interface ProcessCoordinationUnit {
// Property signals. // Property signals.
SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low); SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low);
//////////////////////////////////////////////////////////////////////////////
// V8 context tracking.
// Notifies the browser of a V8Context being created in a renderer
// process. If the context is associated with an ExecutionContext (EC) then
// |description.execution_context_token| will be provided. If the EC is a
// frame, and the parent of that frame is also in the same process, then
// |iframe_attribution_data| will be provided, otherwise these will be empty.
// See the V8ContextWorldType enum for a description of the relationship
// between world types, world names and execution contexts (in
// v8_contexts.mojom).
OnV8ContextCreated(V8ContextDescription description,
IframeAttributionData? iframe_attribution_data);
// Notifies the browser that a V8Context is now detached from its associated
// ExecutionContext (if one was provided during OnV8ContextCreated). If the
// context stays detached for a long time this is indicative of a Javascript
// leak, with the context being kept alive by a stray reference from another
// context. All ExecutionContext-associated V8Contexts will have this method
// called before they are destroyed, and it will not be called for other
// V8Contexts (they are never considered detached). It is possible that this
// is never called for contexts in a renderer processes that crashes or is
// otherwise terminated.
OnV8ContextDetached(blink.mojom.V8ContextToken v8_context_token);
// Notifies the browser that a V8Context has been garbage collected. This will
// only be called after OnV8ContextDetached if the OnV8ContextCreated had a
// non-empty |description.execution_context_token|. It is possible that this
// is never called for contexts in a renderer processes that crashes or is
// otherwise terminated.
OnV8ContextDestroyed(blink.mojom.V8ContextToken v8_context_token);
}; };
...@@ -2,49 +2,34 @@ ...@@ -2,49 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Defines various types that are used by the V8ContextTracker. Note that all of module performance_manager.mojom;
// these will be migrated to mojo types once the browser-side implementation is
// complete and tested.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_TYPES_H_ import "third_party/blink/public/mojom/tokens/tokens.mojom";
#define COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_TYPES_H_
#include <string>
#include "base/optional.h"
#include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager {
namespace v8_memory {
// Stores information about an iframe element from the point of view of the // Stores information about an iframe element from the point of view of the
// document that hosts the iframe. Explicitly allow copy and assign. This is // document that hosts the iframe. This is used for purely informational
// used in the performance.measureMemory API. // purposes in the performance.measureMemory API to identify an iframe in memory
// usage reports.
struct IframeAttributionData { struct IframeAttributionData {
IframeAttributionData(); // The value of the "id" attribute associated with the iframe element.
IframeAttributionData(const IframeAttributionData&); string? id;
IframeAttributionData& operator=(const IframeAttributionData&); // The value of the "src" attribute associated with the iframe element. We
~IframeAttributionData(); // don't use an URL because we don't need to parse this, or otherwise use
// it as an URL, and URL objects have a large memory footprint.
static IframeAttributionData Create(const base::Optional<std::string>& id, string? src;
const base::Optional<std::string>& src);
base::Optional<std::string> id;
// We don't use a GURL because we don't need to parse this, or otherwise use
// it as an URL, and GURL has a very large memory footprint.
base::Optional<std::string> src;
}; };
// Identifies a V8Context type. Note that this roughly corresponds to the // Identifies a V8Context type. Note that this roughly corresponds to the
// world types defined in blink, but with some simplifications. // world types defined in blink, but with some simplifications.
enum class V8ContextWorldType { enum V8ContextWorldType {
// The main world, corresponding to a frame / document. // The main world, corresponding to a frame / document.
kMain, kMain,
// Corresponds to the main world of a worker or worklet. // Corresponds to the main world of a worker or worklet.
kWorkerOrWorklet, kWorkerOrWorklet,
// Corresponds to an extension. // Corresponds to an extension. Will have a stable extension ID.
kExtension, kExtension,
// Corresponds to a non-extension isolated world. // Corresponds to a non-extension isolated world. Will have a human readable
// name.
kIsolated, kIsolated,
// Corresponds to the devtools inspector. Will not have a human readable // Corresponds to the devtools inspector. Will not have a human readable
// name or a stable id. // name or a stable id.
...@@ -55,35 +40,18 @@ enum class V8ContextWorldType { ...@@ -55,35 +40,18 @@ enum class V8ContextWorldType {
kRegExp, kRegExp,
}; };
// Information describing a V8 Context. Explicitly allow copy and assign. This // Information describing a V8 Context. A context is a unit of accounting for
// is used in IPC related to the performance.measureMemory API. // resource usage, and corresponds to a "world" in Blink parlance.
struct V8ContextDescription { struct V8ContextDescription {
V8ContextDescription(); // The unique token that names the world.
V8ContextDescription(const V8ContextDescription&); blink.mojom.V8ContextToken token;
V8ContextDescription& operator=(const V8ContextDescription&);
~V8ContextDescription();
static V8ContextDescription Create(
blink::V8ContextToken token,
V8ContextWorldType world_type,
const base::Optional<std::string>& world_name,
const base::Optional<blink::ExecutionContextToken>&
execution_context_token);
// The unique token that names this world.
blink::V8ContextToken token;
// The type of this world. // The type of this world.
V8ContextWorldType world_type = V8ContextWorldType::kMain; V8ContextWorldType world_type;
// Identifies this world. Only set for extension and isolated worlds. For // Identifies this world. Only set for extension and isolated worlds. For
// extension worlds this corresponds to the stable extension ID. For other // extension worlds this corresponds to the stable extension ID. For other
// isolated worlds this is a human-readable description. // isolated worlds this is a human-readable description.
base::Optional<std::string> world_name; string? world_name;
// The identity of the execution context that this V8Context is associated // The identity of the execution context that this V8Context is associated
// with. This is specified for all world types, except kRegExp worlds. // with. This is specified for all world types, except kRegExp worlds.
base::Optional<blink::ExecutionContextToken> execution_context_token; blink.mojom.ExecutionContextToken? execution_context_token;
}; };
\ No newline at end of file
} // namespace v8_memory
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_TYPES_H_
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/process/process.h" #include "base/process/process.h"
#include "base/run_loop.h"
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "components/performance_manager/graph/process_node_impl.h" #include "components/performance_manager/graph/process_node_impl.h"
......
...@@ -50,8 +50,9 @@ void FakeReportBadMessageForTesting(const std::string& error) { ...@@ -50,8 +50,9 @@ void FakeReportBadMessageForTesting(const std::string& error) {
V8ContextTracker::ExecutionContextState::ExecutionContextState( V8ContextTracker::ExecutionContextState::ExecutionContextState(
const blink::ExecutionContextToken& token, const blink::ExecutionContextToken& token,
const base::Optional<IframeAttributionData>& iframe_attribution_data) mojom::IframeAttributionDataPtr iframe_attribution_data)
: token(token), iframe_attribution_data(iframe_attribution_data) {} : token(token),
iframe_attribution_data(std::move(iframe_attribution_data)) {}
V8ContextTracker::ExecutionContextState::~ExecutionContextState() = default; V8ContextTracker::ExecutionContextState::~ExecutionContextState() = default;
...@@ -59,7 +60,7 @@ V8ContextTracker::ExecutionContextState::~ExecutionContextState() = default; ...@@ -59,7 +60,7 @@ V8ContextTracker::ExecutionContextState::~ExecutionContextState() = default;
// V8ContextTracker::V8ContextState implementation: // V8ContextTracker::V8ContextState implementation:
V8ContextTracker::V8ContextState::V8ContextState( V8ContextTracker::V8ContextState::V8ContextState(
const V8ContextDescription& description, const mojom::V8ContextDescription& description,
ExecutionContextState* execution_context_state) ExecutionContextState* execution_context_state)
: description(description), : description(description),
execution_context_state(execution_context_state) {} execution_context_state(execution_context_state) {}
...@@ -88,8 +89,8 @@ const V8ContextTracker::V8ContextState* V8ContextTracker::GetV8ContextState( ...@@ -88,8 +89,8 @@ const V8ContextTracker::V8ContextState* V8ContextTracker::GetV8ContextState(
void V8ContextTracker::OnV8ContextCreated( void V8ContextTracker::OnV8ContextCreated(
util::PassKey<ProcessNodeImpl> key, util::PassKey<ProcessNodeImpl> key,
ProcessNodeImpl* process_node, ProcessNodeImpl* process_node,
const V8ContextDescription& description, const mojom::V8ContextDescription& description,
const base::Optional<IframeAttributionData>& iframe_attribution_data) { mojom::IframeAttributionDataPtr iframe_attribution_data) {
DCHECK_ON_GRAPH_SEQUENCE(process_node->graph()); DCHECK_ON_GRAPH_SEQUENCE(process_node->graph());
// Validate the |description|. // Validate the |description|.
...@@ -136,7 +137,7 @@ void V8ContextTracker::OnV8ContextCreated( ...@@ -136,7 +137,7 @@ void V8ContextTracker::OnV8ContextCreated(
if (!raw_ec_data) { if (!raw_ec_data) {
ec_data = std::make_unique<ExecutionContextData>( ec_data = std::make_unique<ExecutionContextData>(
process_data, *description.execution_context_token, process_data, *description.execution_context_token,
iframe_attribution_data); std::move(iframe_attribution_data));
raw_ec_data = ec_data.get(); raw_ec_data = ec_data.get();
} }
} }
...@@ -177,13 +178,6 @@ void V8ContextTracker::OnV8ContextDetached( ...@@ -177,13 +178,6 @@ void V8ContextTracker::OnV8ContextDetached(
mojo::ReportBadMessage("repeated OnV8ContextDetached"); mojo::ReportBadMessage("repeated OnV8ContextDetached");
return; return;
} }
// Mark the context as detached. If this is the main context, then mark the
// parent ExecutionContext as destroyed as well.
if (v8_data->IsMainV8Context()) {
auto* ec_data = v8_data->GetExecutionContextData();
data_store_->MarkDestroyed(ec_data);
}
} }
void V8ContextTracker::OnV8ContextDestroyed( void V8ContextTracker::OnV8ContextDestroyed(
...@@ -205,7 +199,7 @@ void V8ContextTracker::OnRemoteIframeAttached( ...@@ -205,7 +199,7 @@ void V8ContextTracker::OnRemoteIframeAttached(
util::PassKey<FrameNodeImpl> key, util::PassKey<FrameNodeImpl> key,
FrameNodeImpl* parent_frame_node, FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
const IframeAttributionData& iframe_attribution_data) { mojom::IframeAttributionDataPtr iframe_attribution_data) {
DCHECK_ON_GRAPH_SEQUENCE(parent_frame_node->graph()); DCHECK_ON_GRAPH_SEQUENCE(parent_frame_node->graph());
// RemoteFrameTokens are issued by the browser to a renderer, so if we receive // RemoteFrameTokens are issued by the browser to a renderer, so if we receive
...@@ -222,12 +216,12 @@ void V8ContextTracker::OnRemoteIframeAttached( ...@@ -222,12 +216,12 @@ void V8ContextTracker::OnRemoteIframeAttached(
struct Data { struct Data {
mojo::ReportBadMessageCallback bad_message_callback; mojo::ReportBadMessageCallback bad_message_callback;
blink::RemoteFrameToken remote_frame_token; blink::RemoteFrameToken remote_frame_token;
IframeAttributionData iframe_attribution_data; mojom::IframeAttributionDataPtr iframe_attribution_data;
base::WeakPtr<FrameNode> frame_node; base::WeakPtr<FrameNode> frame_node;
}; };
std::unique_ptr<Data> data(new Data{mojo::GetBadMessageCallback(), std::unique_ptr<Data> data(
remote_frame_token, new Data{mojo::GetBadMessageCallback(), remote_frame_token,
iframe_attribution_data, nullptr}); std::move(iframe_attribution_data), nullptr});
auto on_pm_seq = base::BindOnce([](std::unique_ptr<Data> data, Graph* graph) { auto on_pm_seq = base::BindOnce([](std::unique_ptr<Data> data, Graph* graph) {
DCHECK(data); DCHECK(data);
...@@ -238,7 +232,7 @@ void V8ContextTracker::OnRemoteIframeAttached( ...@@ -238,7 +232,7 @@ void V8ContextTracker::OnRemoteIframeAttached(
if (auto* tracker = V8ContextTracker::GetFromGraph(graph)) { if (auto* tracker = V8ContextTracker::GetFromGraph(graph)) {
tracker->OnRemoteIframeAttachedImpl( tracker->OnRemoteIframeAttachedImpl(
std::move(data->bad_message_callback), frame_node, std::move(data->bad_message_callback), frame_node,
data->remote_frame_token, data->iframe_attribution_data); data->remote_frame_token, std::move(data->iframe_attribution_data));
} }
} }
}); });
...@@ -313,10 +307,10 @@ void V8ContextTracker::OnRemoteIframeDetached( ...@@ -313,10 +307,10 @@ void V8ContextTracker::OnRemoteIframeDetached(
void V8ContextTracker::OnRemoteIframeAttachedForTesting( void V8ContextTracker::OnRemoteIframeAttachedForTesting(
FrameNodeImpl* frame_node, FrameNodeImpl* frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
const IframeAttributionData& iframe_attribution_data) { mojom::IframeAttributionDataPtr iframe_attribution_data) {
OnRemoteIframeAttachedImpl(base::BindOnce(&FakeReportBadMessageForTesting), OnRemoteIframeAttachedImpl(base::BindOnce(&FakeReportBadMessageForTesting),
frame_node, remote_frame_token, frame_node, remote_frame_token,
iframe_attribution_data); std::move(iframe_attribution_data));
} }
void V8ContextTracker::OnRemoteIframeDetachedForTesting( void V8ContextTracker::OnRemoteIframeDetachedForTesting(
...@@ -456,7 +450,7 @@ void V8ContextTracker::OnRemoteIframeAttachedImpl( ...@@ -456,7 +450,7 @@ void V8ContextTracker::OnRemoteIframeAttachedImpl(
mojo::ReportBadMessageCallback bad_message_callback, mojo::ReportBadMessageCallback bad_message_callback,
FrameNodeImpl* frame_node, FrameNodeImpl* frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
const IframeAttributionData& iframe_attribution_data) { mojom::IframeAttributionDataPtr iframe_attribution_data) {
DCHECK(bad_message_callback); DCHECK(bad_message_callback);
DCHECK_ON_GRAPH_SEQUENCE(frame_node->graph()); DCHECK_ON_GRAPH_SEQUENCE(frame_node->graph());
...@@ -472,19 +466,19 @@ void V8ContextTracker::OnRemoteIframeAttachedImpl( ...@@ -472,19 +466,19 @@ void V8ContextTracker::OnRemoteIframeAttachedImpl(
blink::ExecutionContextToken ec_token(frame_node->frame_token()); blink::ExecutionContextToken ec_token(frame_node->frame_token());
auto* raw_ec_data = data_store_->Get(ec_token); auto* raw_ec_data = data_store_->Get(ec_token);
if (!raw_ec_data) { if (!raw_ec_data) {
ec_data = std::make_unique<ExecutionContextData>(process_data, ec_token, ec_data =
base::nullopt); std::make_unique<ExecutionContextData>(process_data, ec_token, nullptr);
raw_ec_data = ec_data.get(); raw_ec_data = ec_data.get();
} }
if (raw_ec_data->remote_frame_data() || if (raw_ec_data->remote_frame_data() ||
raw_ec_data->iframe_attribution_data != base::nullopt) { raw_ec_data->iframe_attribution_data) {
std::move(bad_message_callback).Run("unexpected OnRemoteIframeAttached"); std::move(bad_message_callback).Run("unexpected OnRemoteIframeAttached");
return; return;
} }
// Attach the iframe data to the ExecutionContextData. // Attach the iframe data to the ExecutionContextData.
raw_ec_data->iframe_attribution_data = iframe_attribution_data; raw_ec_data->iframe_attribution_data = std::move(iframe_attribution_data);
// Create the RemoteFrameData reference to this context. // Create the RemoteFrameData reference to this context.
auto* parent_process_data = auto* parent_process_data =
......
...@@ -7,14 +7,13 @@ ...@@ -7,14 +7,13 @@
#include <memory> #include <memory>
#include "base/optional.h"
#include "base/util/type_safety/pass_key.h" #include "base/util/type_safety/pass_key.h"
#include "components/performance_manager/public/execution_context/execution_context.h" #include "components/performance_manager/public/execution_context/execution_context.h"
#include "components/performance_manager/public/graph/graph.h" #include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/graph_registered.h" #include "components/performance_manager/public/graph/graph_registered.h"
#include "components/performance_manager/public/graph/node_data_describer.h" #include "components/performance_manager/public/graph/node_data_describer.h"
#include "components/performance_manager/public/graph/process_node.h" #include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_types.h" #include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
#include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager { namespace performance_manager {
...@@ -57,7 +56,7 @@ class V8ContextTracker final ...@@ -57,7 +56,7 @@ class V8ContextTracker final
ExecutionContextState() = delete; ExecutionContextState() = delete;
ExecutionContextState( ExecutionContextState(
const blink::ExecutionContextToken& token, const blink::ExecutionContextToken& token,
const base::Optional<IframeAttributionData>& iframe_attribution_data); mojom::IframeAttributionDataPtr iframe_attribution_data);
ExecutionContextState(const ExecutionContextState&) = delete; ExecutionContextState(const ExecutionContextState&) = delete;
ExecutionContextState& operator=(const ExecutionContextState&) = delete; ExecutionContextState& operator=(const ExecutionContextState&) = delete;
virtual ~ExecutionContextState(); virtual ~ExecutionContextState();
...@@ -74,7 +73,7 @@ class V8ContextTracker final ...@@ -74,7 +73,7 @@ class V8ContextTracker final
// This is sometimes only available asynchronously so is optional. Note that // This is sometimes only available asynchronously so is optional. Note that
// this value can change over time, but will generally reflect the most up // this value can change over time, but will generally reflect the most up
// to date data (with some lag). // to date data (with some lag).
base::Optional<IframeAttributionData> iframe_attribution_data; mojom::IframeAttributionDataPtr iframe_attribution_data;
// Whether or not the corresponding blink::ExecutionContext has been // Whether or not the corresponding blink::ExecutionContext has been
// destroyed. This occurs when the main V8Context associated with this // destroyed. This occurs when the main V8Context associated with this
...@@ -85,14 +84,14 @@ class V8ContextTracker final ...@@ -85,14 +84,14 @@ class V8ContextTracker final
struct V8ContextState { struct V8ContextState {
V8ContextState() = delete; V8ContextState() = delete;
V8ContextState(const V8ContextDescription& description, V8ContextState(const mojom::V8ContextDescription& description,
ExecutionContextState* execution_context_state); ExecutionContextState* execution_context_state);
V8ContextState(const V8ContextState&) = delete; V8ContextState(const V8ContextState&) = delete;
V8ContextState& operator=(const V8ContextState&) = delete; V8ContextState& operator=(const V8ContextState&) = delete;
virtual ~V8ContextState(); virtual ~V8ContextState();
// The full description of this context. // The full description of this context.
const V8ContextDescription description; const mojom::V8ContextDescription description;
// A pointer to the upstream ExecutionContextState that this V8Context is // A pointer to the upstream ExecutionContextState that this V8Context is
// associated with. Note that this can be nullptr for V8Contexts that are // associated with. Note that this can be nullptr for V8Contexts that are
...@@ -138,8 +137,8 @@ class V8ContextTracker final ...@@ -138,8 +137,8 @@ class V8ContextTracker final
void OnV8ContextCreated( void OnV8ContextCreated(
util::PassKey<ProcessNodeImpl> key, util::PassKey<ProcessNodeImpl> key,
ProcessNodeImpl* process_node, ProcessNodeImpl* process_node,
const V8ContextDescription& description, const mojom::V8ContextDescription& description,
const base::Optional<IframeAttributionData>& iframe_attribution_data); mojom::IframeAttributionDataPtr iframe_attribution_data);
// Notifies the tracker that a V8Context is now detached from its associated // Notifies the tracker that a V8Context is now detached from its associated
// ExecutionContext (if one was provided during OnV8ContextCreated). If the // ExecutionContext (if one was provided during OnV8ContextCreated). If the
...@@ -169,7 +168,7 @@ class V8ContextTracker final ...@@ -169,7 +168,7 @@ class V8ContextTracker final
util::PassKey<FrameNodeImpl> key, util::PassKey<FrameNodeImpl> key,
FrameNodeImpl* parent_frame_node, FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
const IframeAttributionData& iframe_attribution_data); mojom::IframeAttributionDataPtr iframe_attribution_data);
// TODO(chrisha): Add OnRemoteIframeAttributesChanged support. // TODO(chrisha): Add OnRemoteIframeAttributesChanged support.
...@@ -191,7 +190,7 @@ class V8ContextTracker final ...@@ -191,7 +190,7 @@ class V8ContextTracker final
void OnRemoteIframeAttachedForTesting( void OnRemoteIframeAttachedForTesting(
FrameNodeImpl* frame_node, FrameNodeImpl* frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
const IframeAttributionData& iframe_attribution_data); mojom::IframeAttributionDataPtr iframe_attribution_data);
void OnRemoteIframeDetachedForTesting( void OnRemoteIframeDetachedForTesting(
FrameNodeImpl* parent_frame_node, FrameNodeImpl* parent_frame_node,
...@@ -231,7 +230,7 @@ class V8ContextTracker final ...@@ -231,7 +230,7 @@ class V8ContextTracker final
mojo::ReportBadMessageCallback bad_message_callback, mojo::ReportBadMessageCallback bad_message_callback,
FrameNodeImpl* frame_node, FrameNodeImpl* frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
const IframeAttributionData& iframe_attribution_data); mojom::IframeAttributionDataPtr iframe_attribution_data);
// To maintain strict ordering with OnRemoteIframeAttached events, detached // To maintain strict ordering with OnRemoteIframeAttached events, detached
// events also detour through the UI thread to arrive here. // events also detour through the UI thread to arrive here.
......
// Copyright 2020 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 "components/performance_manager/v8_memory/v8_context_tracker.h"
#include <memory>
#include "base/strings/stringprintf.h"
#include "components/performance_manager/execution_context/execution_context_registry_impl.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/test_support/performance_manager_browsertest_harness.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace performance_manager {
namespace v8_memory {
class V8ContextTrackerTest : public PerformanceManagerBrowserTestHarness {
public:
using Super = PerformanceManagerBrowserTestHarness;
V8ContextTrackerTest() = default;
~V8ContextTrackerTest() override = default;
void OnGraphCreated(Graph* graph) override {
graph->PassToGraph(
std::make_unique<execution_context::ExecutionContextRegistryImpl>());
graph->PassToGraph(std::make_unique<V8ContextTracker>());
}
void ExpectCounts(size_t v8_context_count,
size_t execution_context_count,
size_t detached_v8_context_count,
size_t destroyed_execution_context_count) {
RunInGraph([&](Graph* graph) {
auto* v8ct = V8ContextTracker::GetFromGraph(graph);
ASSERT_TRUE(v8ct);
EXPECT_EQ(v8_context_count, v8ct->GetV8ContextCountForTesting());
EXPECT_EQ(execution_context_count,
v8ct->GetExecutionContextCountForTesting());
EXPECT_EQ(detached_v8_context_count,
v8ct->GetDetachedV8ContextCountForTesting());
EXPECT_EQ(destroyed_execution_context_count,
v8ct->GetDestroyedExecutionContextCountForTesting());
});
}
};
IN_PROC_BROWSER_TEST_F(V8ContextTrackerTest, AboutBlank) {
ExpectCounts(0, 0, 0, 0);
ASSERT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
ExpectCounts(1, 1, 0, 0);
}
IN_PROC_BROWSER_TEST_F(V8ContextTrackerTest, SameDocNavigation) {
ExpectCounts(0, 0, 0, 0);
GURL urla(embedded_test_server()->GetURL("a.com", "/a_embeds_b.html"));
ASSERT_TRUE(NavigateToURL(shell(), urla));
ExpectCounts(2, 2, 0, 0);
// Get pointers to the RFHs for each frame.
auto* contents = shell()->web_contents();
content::RenderFrameHost* rfha = contents->GetMainFrame();
content::RenderFrameHost* rfhb = nullptr;
auto frames = contents->GetAllFrames();
ASSERT_EQ(2u, frames.size());
for (auto* rfh : contents->GetAllFrames()) {
if (rfh != rfha)
rfhb = rfh;
}
// Execute a same document navigation in the child frame. This causes a
// v8 context to be detached, and new context attached to the execution
// context. So there will remain 2 execution contexts, there will be 3
// v8 contexts, 1 one of which is detached.
GURL urlb(embedded_test_server()->GetURL("b.com", "/b.html?foo=bar"));
ASSERT_TRUE(ExecJs(
rfhb, base::StringPrintf("location.href = \"%s\"", urlb.spec().c_str())));
WaitForLoad(contents);
ExpectCounts(3, 2, 1, 0);
}
IN_PROC_BROWSER_TEST_F(V8ContextTrackerTest, DetachedContext) {
ExpectCounts(0, 0, 0, 0);
GURL urla(embedded_test_server()->GetURL("a.com", "/a_embeds_a.html"));
ASSERT_TRUE(NavigateToURL(shell(), urla));
ExpectCounts(2, 2, 0, 0);
// Get pointers to the RFHs for each frame.
auto* contents = shell()->web_contents();
content::RenderFrameHost* rfha = contents->GetMainFrame();
// Keep a pointer to the window associated with the child iframe, but
// unload it.
ASSERT_TRUE(ExecJs(rfha,
"let iframe = document.getElementsByTagName('iframe')[0]; "
"document.body.leakyRef = iframe.contentWindow.window; "
"iframe.parentNode.removeChild(iframe); "
"console.log('detached and leaked iframe');"));
ExpectCounts(2, 2, 1, 1);
}
} // namespace v8_memory
} // namespace performance_manager
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include "components/performance_manager/graph/process_node_impl.h" #include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/public/execution_context/execution_context.h" #include "components/performance_manager/public/execution_context/execution_context.h"
#include "components/performance_manager/public/execution_context/execution_context_registry.h" #include "components/performance_manager/public/execution_context/execution_context_registry.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_types.h" #include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
namespace performance_manager { namespace performance_manager {
namespace v8_memory { namespace v8_memory {
...@@ -92,9 +92,9 @@ const execution_context::ExecutionContext* GetExecutionContext( ...@@ -92,9 +92,9 @@ const execution_context::ExecutionContext* GetExecutionContext(
} }
V8ContextDescriptionStatus ValidateV8ContextDescription( V8ContextDescriptionStatus ValidateV8ContextDescription(
const V8ContextDescription& description) { const mojom::V8ContextDescription& description) {
switch (description.world_type) { switch (description.world_type) {
case V8ContextWorldType::kMain: { case mojom::V8ContextWorldType::kMain: {
if (description.world_name) if (description.world_name)
return V8ContextDescriptionStatus::kUnexpectedWorldName; return V8ContextDescriptionStatus::kUnexpectedWorldName;
if (!description.execution_context_token) if (!description.execution_context_token)
...@@ -103,7 +103,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription( ...@@ -103,7 +103,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription(
return V8ContextDescriptionStatus::kMissingLocalFrameToken; return V8ContextDescriptionStatus::kMissingLocalFrameToken;
} break; } break;
case V8ContextWorldType::kWorkerOrWorklet: { case mojom::V8ContextWorldType::kWorkerOrWorklet: {
if (description.world_name) if (description.world_name)
return V8ContextDescriptionStatus::kUnexpectedWorldName; return V8ContextDescriptionStatus::kUnexpectedWorldName;
if (!description.execution_context_token) if (!description.execution_context_token)
...@@ -112,7 +112,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription( ...@@ -112,7 +112,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription(
return V8ContextDescriptionStatus::kUnexpectedLocalFrameToken; return V8ContextDescriptionStatus::kUnexpectedLocalFrameToken;
} break; } break;
case V8ContextWorldType::kExtension: { case mojom::V8ContextWorldType::kExtension: {
if (!description.world_name) if (!description.world_name)
return V8ContextDescriptionStatus::kMissingWorldName; return V8ContextDescriptionStatus::kMissingWorldName;
if (!IsValidExtensionId(description.world_name.value())) if (!IsValidExtensionId(description.world_name.value()))
...@@ -124,7 +124,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription( ...@@ -124,7 +124,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription(
return V8ContextDescriptionStatus::kUnexpectedWorkletToken; return V8ContextDescriptionStatus::kUnexpectedWorkletToken;
} break; } break;
case V8ContextWorldType::kIsolated: { case mojom::V8ContextWorldType::kIsolated: {
// World names are optional in isolated worlds. // World names are optional in isolated worlds.
// Only frame and workers can have isolated worlds, *not* worklets. // Only frame and workers can have isolated worlds, *not* worklets.
if (!description.execution_context_token) if (!description.execution_context_token)
...@@ -133,7 +133,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription( ...@@ -133,7 +133,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription(
return V8ContextDescriptionStatus::kUnexpectedWorkletToken; return V8ContextDescriptionStatus::kUnexpectedWorkletToken;
} break; } break;
case V8ContextWorldType::kInspector: { case mojom::V8ContextWorldType::kInspector: {
if (description.world_name) if (description.world_name)
return V8ContextDescriptionStatus::kUnexpectedWorldName; return V8ContextDescriptionStatus::kUnexpectedWorldName;
// Devtools can only inject into frames and workers, *not* worklets. // Devtools can only inject into frames and workers, *not* worklets.
...@@ -143,7 +143,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription( ...@@ -143,7 +143,7 @@ V8ContextDescriptionStatus ValidateV8ContextDescription(
return V8ContextDescriptionStatus::kUnexpectedWorkletToken; return V8ContextDescriptionStatus::kUnexpectedWorkletToken;
} break; } break;
case V8ContextWorldType::kRegExp: { case mojom::V8ContextWorldType::kRegExp: {
// Regexp worlds have no additional data. // Regexp worlds have no additional data.
if (description.world_name) if (description.world_name)
return V8ContextDescriptionStatus::kUnexpectedWorldName; return V8ContextDescriptionStatus::kUnexpectedWorldName;
...@@ -156,10 +156,10 @@ V8ContextDescriptionStatus ValidateV8ContextDescription( ...@@ -156,10 +156,10 @@ V8ContextDescriptionStatus ValidateV8ContextDescription(
} }
base::Optional<bool> ExpectIframeAttributionDataForV8ContextDescription( base::Optional<bool> ExpectIframeAttributionDataForV8ContextDescription(
const V8ContextDescription& description, const mojom::V8ContextDescription& description,
Graph* graph) { Graph* graph) {
switch (description.world_type) { switch (description.world_type) {
case V8ContextWorldType::kMain: { case mojom::V8ContextWorldType::kMain: {
// There's no guarantee that the actual ExecutionContext has yet been // There's no guarantee that the actual ExecutionContext has yet been
// created from our POV as there's a race between V8Context creation // created from our POV as there's a race between V8Context creation
// notifications and node creations. But if it does exist, we sanity check // notifications and node creations. But if it does exist, we sanity check
...@@ -173,11 +173,11 @@ base::Optional<bool> ExpectIframeAttributionDataForV8ContextDescription( ...@@ -173,11 +173,11 @@ base::Optional<bool> ExpectIframeAttributionDataForV8ContextDescription(
} }
} break; } break;
case V8ContextWorldType::kWorkerOrWorklet: case mojom::V8ContextWorldType::kWorkerOrWorklet:
case V8ContextWorldType::kExtension: case mojom::V8ContextWorldType::kExtension:
case V8ContextWorldType::kIsolated: case mojom::V8ContextWorldType::kIsolated:
case V8ContextWorldType::kInspector: case mojom::V8ContextWorldType::kInspector:
case V8ContextWorldType::kRegExp: { case mojom::V8ContextWorldType::kRegExp: {
} break; } break;
} }
......
...@@ -20,9 +20,11 @@ namespace execution_context { ...@@ -20,9 +20,11 @@ namespace execution_context {
class ExecutionContext; class ExecutionContext;
} // namespace execution_context } // namespace execution_context
namespace v8_memory { namespace mojom {
class V8ContextDescription;
} // namespace mojom
struct V8ContextDescription; namespace v8_memory {
// Helper function to convert a WorkerToken to an ExecutionContext token. // Helper function to convert a WorkerToken to an ExecutionContext token.
// TODO(crbug.com/1126285): There should be automatic type conversion for this // TODO(crbug.com/1126285): There should be automatic type conversion for this
...@@ -72,14 +74,14 @@ enum class V8ContextDescriptionStatus { ...@@ -72,14 +74,14 @@ enum class V8ContextDescriptionStatus {
// Validates the given V8ContextDescription. // Validates the given V8ContextDescription.
V8ContextDescriptionStatus ValidateV8ContextDescription( V8ContextDescriptionStatus ValidateV8ContextDescription(
const V8ContextDescription& description) WARN_UNUSED_RESULT; const mojom::V8ContextDescription& description) WARN_UNUSED_RESULT;
// Determines whether or not IframeAttributionData is expected to accompany the // Determines whether or not IframeAttributionData is expected to accompany the
// provided V8ContextDescription. This is not always able to be determined, in // provided V8ContextDescription. This is not always able to be determined, in
// which case base::nullopt will be returned. It is assumed that the // which case base::nullopt will be returned. It is assumed that the
// |description| has previously been validated. // |description| has previously been validated.
base::Optional<bool> ExpectIframeAttributionDataForV8ContextDescription( base::Optional<bool> ExpectIframeAttributionDataForV8ContextDescription(
const V8ContextDescription& description, const mojom::V8ContextDescription& description,
Graph* graph) WARN_UNUSED_RESULT; Graph* graph) WARN_UNUSED_RESULT;
// Small helper class for maintaining a count of objects that are optionally // Small helper class for maintaining a count of objects that are optionally
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "components/performance_manager/v8_memory/v8_context_tracker_internal.h" #include "components/performance_manager/v8_memory/v8_context_tracker_internal.h"
#include <utility>
#include "base/check.h" #include "base/check.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_helpers.h" #include "components/performance_manager/v8_memory/v8_context_tracker_helpers.h"
...@@ -17,13 +19,14 @@ namespace internal { ...@@ -17,13 +19,14 @@ namespace internal {
ExecutionContextData::ExecutionContextData( ExecutionContextData::ExecutionContextData(
ProcessData* process_data, ProcessData* process_data,
const blink::ExecutionContextToken& token, const blink::ExecutionContextToken& token,
const base::Optional<IframeAttributionData>& iframe_attribution_data) mojom::IframeAttributionDataPtr iframe_attribution_data)
: ExecutionContextState(token, iframe_attribution_data), : ExecutionContextState(token, std::move(iframe_attribution_data)),
process_data_(process_data) {} process_data_(process_data) {}
ExecutionContextData::~ExecutionContextData() { ExecutionContextData::~ExecutionContextData() {
DCHECK(!IsTracked()); DCHECK(!IsTracked());
DCHECK(ShouldDestroy()); DCHECK(ShouldDestroy());
DCHECK_EQ(0u, main_nondetached_v8_context_count_);
} }
bool ExecutionContextData::IsTracked() const { bool ExecutionContextData::IsTracked() const {
...@@ -57,6 +60,7 @@ void ExecutionContextData::IncrementV8ContextCount( ...@@ -57,6 +60,7 @@ void ExecutionContextData::IncrementV8ContextCount(
bool ExecutionContextData::DecrementV8ContextCount( bool ExecutionContextData::DecrementV8ContextCount(
util::PassKey<V8ContextData>) { util::PassKey<V8ContextData>) {
DCHECK_LT(0u, v8_context_count_); DCHECK_LT(0u, v8_context_count_);
DCHECK_LT(main_nondetached_v8_context_count_, v8_context_count_);
--v8_context_count_; --v8_context_count_;
return ShouldDestroy(); return ShouldDestroy();
} }
...@@ -68,14 +72,22 @@ bool ExecutionContextData::MarkDestroyed(util::PassKey<ProcessData>) { ...@@ -68,14 +72,22 @@ bool ExecutionContextData::MarkDestroyed(util::PassKey<ProcessData>) {
return true; return true;
} }
bool ExecutionContextData::MarkMainWorldSeen( bool ExecutionContextData::MarkMainV8ContextCreated(
util::PassKey<V8ContextTrackerDataStore>) { util::PassKey<V8ContextTrackerDataStore>) {
if (main_world_seen_) if (main_nondetached_v8_context_count_ >= v8_context_count_)
return false;
if (main_nondetached_v8_context_count_ >= 1)
return false; return false;
main_world_seen_ = true; ++main_nondetached_v8_context_count_;
return true; return true;
} }
void ExecutionContextData::MarkMainV8ContextDetached(
util::PassKey<V8ContextData>) {
DCHECK_LT(0u, main_nondetached_v8_context_count_);
--main_nondetached_v8_context_count_;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// RemoteFrameData implementation: // RemoteFrameData implementation:
...@@ -112,7 +124,7 @@ bool RemoteFrameData::IsTracked() const { ...@@ -112,7 +124,7 @@ bool RemoteFrameData::IsTracked() const {
// V8ContextData implementation: // V8ContextData implementation:
V8ContextData::V8ContextData(ProcessData* process_data, V8ContextData::V8ContextData(ProcessData* process_data,
const V8ContextDescription& description, const mojom::V8ContextDescription& description,
ExecutionContextData* execution_context_data) ExecutionContextData* execution_context_data)
: V8ContextState(description, execution_context_data), : V8ContextState(description, execution_context_data),
process_data_(process_data) { process_data_(process_data) {
...@@ -132,6 +144,10 @@ V8ContextData::V8ContextData(ProcessData* process_data, ...@@ -132,6 +144,10 @@ V8ContextData::V8ContextData(ProcessData* process_data,
V8ContextData::~V8ContextData() { V8ContextData::~V8ContextData() {
DCHECK(!IsTracked()); DCHECK(!IsTracked());
// Mark as detached if necessary so that main world counts are appropriately
// updated even during tear down code paths.
MarkDetachedImpl();
// If this is the last reference keeping alive a tracked ExecutionContextData, // If this is the last reference keeping alive a tracked ExecutionContextData,
// then clean it up as well. Untracked ExecutionContextDatas will go out of // then clean it up as well. Untracked ExecutionContextDatas will go out of
// scope on their own. // scope on their own.
...@@ -149,11 +165,13 @@ ExecutionContextData* V8ContextData::GetExecutionContextData() const { ...@@ -149,11 +165,13 @@ ExecutionContextData* V8ContextData::GetExecutionContextData() const {
return static_cast<ExecutionContextData*>(execution_context_state); return static_cast<ExecutionContextData*>(execution_context_state);
} }
void V8ContextData::SetWasTracked(util::PassKey<V8ContextTrackerDataStore>) {
DCHECK(!was_tracked_);
was_tracked_ = true;
}
bool V8ContextData::MarkDetached(util::PassKey<ProcessData>) { bool V8ContextData::MarkDetached(util::PassKey<ProcessData>) {
if (detached) return MarkDetachedImpl();
return false;
detached = true;
return true;
} }
bool V8ContextData::IsMainV8Context() const { bool V8ContextData::IsMainV8Context() const {
...@@ -171,8 +189,22 @@ bool V8ContextData::IsMainV8Context() const { ...@@ -171,8 +189,22 @@ bool V8ContextData::IsMainV8Context() const {
// Only main frames and workers can be "main" contexts. // Only main frames and workers can be "main" contexts.
auto world_type = description.world_type; auto world_type = description.world_type;
return world_type == V8ContextWorldType::kMain || return world_type == mojom::V8ContextWorldType::kMain ||
world_type == V8ContextWorldType::kWorkerOrWorklet; world_type == mojom::V8ContextWorldType::kWorkerOrWorklet;
}
bool V8ContextData::MarkDetachedImpl() {
if (detached)
return false;
detached = true;
// The EC is notified of the main V8 context only when it is passed to the
// data store (at which point |was_tracked_| is set to true). Only do the
// symmetric operation if the first occurred.
if (IsMainV8Context() && was_tracked_) {
if (auto* ec_data = GetExecutionContextData())
ec_data->MarkMainV8ContextDetached(PassKey());
}
return true;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
...@@ -321,10 +353,11 @@ bool V8ContextTrackerDataStore::Pass(std::unique_ptr<V8ContextData> v8_data) { ...@@ -321,10 +353,11 @@ bool V8ContextTrackerDataStore::Pass(std::unique_ptr<V8ContextData> v8_data) {
DCHECK(v8_data.get()); DCHECK(v8_data.get());
auto* ec_data = v8_data->GetExecutionContextData(); auto* ec_data = v8_data->GetExecutionContextData();
if (ec_data && v8_data->IsMainV8Context()) { if (ec_data && v8_data->IsMainV8Context()) {
if (!ec_data->MarkMainWorldSeen(PassKey())) if (!ec_data->MarkMainV8ContextCreated(PassKey()))
return false; return false;
} }
v8_data->process_data()->Add(PassKey(), v8_data.get()); v8_data->process_data()->Add(PassKey(), v8_data.get());
v8_data->SetWasTracked(PassKey());
auto result = global_v8_context_datas_.insert(std::move(v8_data)); auto result = global_v8_context_datas_.insert(std::move(v8_data));
DCHECK(result.second); DCHECK(result.second);
return true; return true;
......
...@@ -15,13 +15,12 @@ ...@@ -15,13 +15,12 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/containers/linked_list.h" #include "base/containers/linked_list.h"
#include "base/optional.h"
#include "base/util/type_safety/pass_key.h" #include "base/util/type_safety/pass_key.h"
#include "components/performance_manager/graph/node_attached_data_impl.h" #include "components/performance_manager/graph/node_attached_data_impl.h"
#include "components/performance_manager/graph/process_node_impl.h" #include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h" #include "components/performance_manager/v8_memory/v8_context_tracker.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_helpers.h" #include "components/performance_manager/v8_memory/v8_context_tracker_helpers.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_types.h"
#include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager { namespace performance_manager {
...@@ -66,10 +65,9 @@ class ExecutionContextData : public base::LinkNode<ExecutionContextData>, ...@@ -66,10 +65,9 @@ class ExecutionContextData : public base::LinkNode<ExecutionContextData>,
ExecutionContextData() = delete; ExecutionContextData() = delete;
ExecutionContextData(const ExecutionContextData&) = delete; ExecutionContextData(const ExecutionContextData&) = delete;
ExecutionContextData( ExecutionContextData(ProcessData* process_data,
ProcessData* process_data, const blink::ExecutionContextToken& token,
const blink::ExecutionContextToken& token, mojom::IframeAttributionDataPtr iframe_attribution_data);
const base::Optional<IframeAttributionData>& iframe_attribution_data);
ExecutionContextData& operator=(const ExecutionContextData&) = delete; ExecutionContextData& operator=(const ExecutionContextData&) = delete;
~ExecutionContextData() override; ~ExecutionContextData() override;
...@@ -77,7 +75,9 @@ class ExecutionContextData : public base::LinkNode<ExecutionContextData>, ...@@ -77,7 +75,9 @@ class ExecutionContextData : public base::LinkNode<ExecutionContextData>,
ProcessData* process_data() const { return process_data_; } ProcessData* process_data() const { return process_data_; }
RemoteFrameData* remote_frame_data() { return remote_frame_data_; } RemoteFrameData* remote_frame_data() { return remote_frame_data_; }
size_t v8_context_count() const { return v8_context_count_; } size_t v8_context_count() const { return v8_context_count_; }
bool main_world_seen() const { return main_world_seen_; } size_t main_nondetached_v8_context_count() const {
return main_nondetached_v8_context_count_;
}
// For consistency, all Data objects have a GetToken() function. // For consistency, all Data objects have a GetToken() function.
const blink::ExecutionContextToken& GetToken() const { return token; } const blink::ExecutionContextToken& GetToken() const { return token; }
...@@ -107,12 +107,13 @@ class ExecutionContextData : public base::LinkNode<ExecutionContextData>, ...@@ -107,12 +107,13 @@ class ExecutionContextData : public base::LinkNode<ExecutionContextData>,
// if it was already destroyed. // if it was already destroyed.
WARN_UNUSED_RESULT bool MarkDestroyed(util::PassKey<ProcessData>); WARN_UNUSED_RESULT bool MarkDestroyed(util::PassKey<ProcessData>);
// Marks the main world as having been seen. Returns true if the state changed // Used for tracking the total number of non-detached "main" V8Contexts
// and false if this had already occurred. This is called when the // associated with this ExecutionContext. This should always be no more than
// V8ContextData is passed to the data store and can prevent it from // 1. A new context may become the main context during a same-document
// succeeding. // navigation of a frame.
WARN_UNUSED_RESULT bool MarkMainWorldSeen( WARN_UNUSED_RESULT bool MarkMainV8ContextCreated(
util::PassKey<V8ContextTrackerDataStore>); util::PassKey<V8ContextTrackerDataStore>);
void MarkMainV8ContextDetached(util::PassKey<V8ContextData>);
private: private:
ProcessData* const process_data_; ProcessData* const process_data_;
...@@ -122,9 +123,12 @@ class ExecutionContextData : public base::LinkNode<ExecutionContextData>, ...@@ -122,9 +123,12 @@ class ExecutionContextData : public base::LinkNode<ExecutionContextData>,
// The count of V8ContextDatas keeping this object alive. // The count of V8ContextDatas keeping this object alive.
size_t v8_context_count_ = 0; size_t v8_context_count_ = 0;
// True if a main world V8Context has been seen for this EC. Can only ever // The number of non-detached main worlds that are currently associated with
// toggle from false to true. // this ExecutionContext. There can be no more than 1 of these. Once
bool main_world_seen_ = false; // document and frame lifetime semantics have been cleaned up, there will only
// be a single main context per ExecutionContext over its lifetime; right now
// there can be multiple due to same-document navigations.
size_t main_nondetached_v8_context_count_ = 0;
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
...@@ -178,7 +182,7 @@ class V8ContextData : public base::LinkNode<V8ContextData>, ...@@ -178,7 +182,7 @@ class V8ContextData : public base::LinkNode<V8ContextData>,
V8ContextData() = delete; V8ContextData() = delete;
V8ContextData(ProcessData* process_data, V8ContextData(ProcessData* process_data,
const V8ContextDescription& description, const mojom::V8ContextDescription& description,
ExecutionContextData* execution_context_data); ExecutionContextData* execution_context_data);
V8ContextData(const V8ContextData&) = delete; V8ContextData(const V8ContextData&) = delete;
V8ContextData& operator=(const V8ContextData&) = delete; V8ContextData& operator=(const V8ContextData&) = delete;
...@@ -198,6 +202,9 @@ class V8ContextData : public base::LinkNode<V8ContextData>, ...@@ -198,6 +202,9 @@ class V8ContextData : public base::LinkNode<V8ContextData>,
// Returns the ExecutionContextData associated with this V8ContextData. // Returns the ExecutionContextData associated with this V8ContextData.
ExecutionContextData* GetExecutionContextData() const; ExecutionContextData* GetExecutionContextData() const;
// Marks this context as having been successfully passed into the data store.
void SetWasTracked(util::PassKey<V8ContextTrackerDataStore>);
// Marks this context as detached. Returns true if the state changed, false // Marks this context as detached. Returns true if the state changed, false
// if it was already detached. // if it was already detached.
WARN_UNUSED_RESULT bool MarkDetached(util::PassKey<ProcessData>); WARN_UNUSED_RESULT bool MarkDetached(util::PassKey<ProcessData>);
...@@ -209,7 +216,10 @@ class V8ContextData : public base::LinkNode<V8ContextData>, ...@@ -209,7 +216,10 @@ class V8ContextData : public base::LinkNode<V8ContextData>,
bool IsMainV8Context() const; bool IsMainV8Context() const;
private: private:
bool MarkDetachedImpl();
ProcessData* const process_data_; ProcessData* const process_data_;
bool was_tracked_ = false;
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
......
...@@ -6,13 +6,12 @@ ...@@ -6,13 +6,12 @@
#include <memory> #include <memory>
#include "base/optional.h"
#include "base/test/gtest_util.h" #include "base/test/gtest_util.h"
#include "components/performance_manager/execution_context/execution_context_registry_impl.h" #include "components/performance_manager/execution_context/execution_context_registry_impl.h"
#include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
#include "components/performance_manager/test_support/graph_test_harness.h" #include "components/performance_manager/test_support/graph_test_harness.h"
#include "components/performance_manager/test_support/mock_graphs.h" #include "components/performance_manager/test_support/mock_graphs.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h" #include "components/performance_manager/v8_memory/v8_context_tracker.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_types.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/common/tokens/tokens.h"
...@@ -45,13 +44,15 @@ class V8ContextTrackerInternalTest : public GraphTestHarness { ...@@ -45,13 +44,15 @@ class V8ContextTrackerInternalTest : public GraphTestHarness {
MockSinglePageWithMultipleProcessesGraph mock_graph_; MockSinglePageWithMultipleProcessesGraph mock_graph_;
}; };
V8ContextDescription MakeMatchingV8ContextDescription( mojom::V8ContextDescription MakeMatchingV8ContextDescription(
ExecutionContextData* ec_data, ExecutionContextData* ec_data,
bool main_world = true) { bool main_world = true) {
DCHECK(ec_data); DCHECK(ec_data);
V8ContextDescription v8_desc; mojom::V8ContextDescription v8_desc;
if (!main_world) { if (main_world) {
v8_desc.world_type = V8ContextWorldType::kIsolated; v8_desc.world_type = mojom::V8ContextWorldType::kMain;
} else {
v8_desc.world_type = mojom::V8ContextWorldType::kExtension;
v8_desc.world_name = kExtensionId; v8_desc.world_name = kExtensionId;
} }
v8_desc.execution_context_token = ec_data->GetToken(); v8_desc.execution_context_token = ec_data->GetToken();
...@@ -68,7 +69,7 @@ TEST_F(V8ContextTrackerInternalDeathTest, ...@@ -68,7 +69,7 @@ TEST_F(V8ContextTrackerInternalDeathTest,
static_cast<ProcessNodeImpl*>(mock_graph_.process.get())); static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt); process_data, mock_graph_.frame->frame_token(), nullptr);
EXPECT_TRUE(ec_data->ShouldDestroy()); EXPECT_TRUE(ec_data->ShouldDestroy());
EXPECT_DCHECK_DEATH(data_store()->Pass(std::move(ec_data))); EXPECT_DCHECK_DEATH(data_store()->Pass(std::move(ec_data)));
} }
...@@ -79,19 +80,19 @@ TEST_F(V8ContextTrackerInternalDeathTest, ...@@ -79,19 +80,19 @@ TEST_F(V8ContextTrackerInternalDeathTest,
static_cast<ProcessNodeImpl*>(mock_graph_.process.get())); static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt); process_data, mock_graph_.frame->frame_token(), nullptr);
EXPECT_TRUE(ec_data->ShouldDestroy()); EXPECT_TRUE(ec_data->ShouldDestroy());
EXPECT_FALSE(ec_data->main_world_seen()); EXPECT_EQ(0u, ec_data->main_nondetached_v8_context_count());
V8ContextDescription v8_desc; mojom::V8ContextDescription v8_desc;
v8_desc.world_type = V8ContextWorldType::kMain; v8_desc.world_type = mojom::V8ContextWorldType::kMain;
v8_desc.execution_context_token = ec_data->GetToken(); v8_desc.execution_context_token = ec_data->GetToken();
std::unique_ptr<V8ContextData> v8_data = std::unique_ptr<V8ContextData> v8_data =
std::make_unique<V8ContextData>(process_data, v8_desc, ec_data.get()); std::make_unique<V8ContextData>(process_data, v8_desc, ec_data.get());
EXPECT_TRUE(v8_data->IsMainV8Context()); EXPECT_TRUE(v8_data->IsMainV8Context());
EXPECT_TRUE(data_store()->Pass(std::move(v8_data))); EXPECT_TRUE(data_store()->Pass(std::move(v8_data)));
EXPECT_TRUE(ec_data->main_world_seen()); EXPECT_EQ(1u, ec_data->main_nondetached_v8_context_count());
v8_desc.token = blink::V8ContextToken(); v8_desc.token = blink::V8ContextToken();
v8_data = v8_data =
...@@ -107,7 +108,7 @@ TEST_F(V8ContextTrackerInternalDeathTest, SameProcessRemoteFrameDataExplodes) { ...@@ -107,7 +108,7 @@ TEST_F(V8ContextTrackerInternalDeathTest, SameProcessRemoteFrameDataExplodes) {
static_cast<ProcessNodeImpl*>(mock_graph_.process.get())); static_cast<ProcessNodeImpl*>(mock_graph_.process.get()));
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt); process_data, mock_graph_.frame->frame_token(), nullptr);
std::unique_ptr<RemoteFrameData> rf_data; std::unique_ptr<RemoteFrameData> rf_data;
EXPECT_DCHECK_DEATH( EXPECT_DCHECK_DEATH(
rf_data = std::make_unique<RemoteFrameData>( rf_data = std::make_unique<RemoteFrameData>(
...@@ -121,7 +122,7 @@ TEST_F(V8ContextTrackerInternalDeathTest, CrossProcessV8ContextDataExplodes) { ...@@ -121,7 +122,7 @@ TEST_F(V8ContextTrackerInternalDeathTest, CrossProcessV8ContextDataExplodes) {
static_cast<ProcessNodeImpl*>(mock_graph_.other_process.get())); static_cast<ProcessNodeImpl*>(mock_graph_.other_process.get()));
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt); process_data, mock_graph_.frame->frame_token(), nullptr);
std::unique_ptr<V8ContextData> v8_data; std::unique_ptr<V8ContextData> v8_data;
EXPECT_DCHECK_DEATH(v8_data = std::make_unique<V8ContextData>( EXPECT_DCHECK_DEATH(v8_data = std::make_unique<V8ContextData>(
other_process_data, other_process_data,
...@@ -136,7 +137,7 @@ TEST_F(V8ContextTrackerInternalTest, ExecutionContextDataShouldDestroy) { ...@@ -136,7 +137,7 @@ TEST_F(V8ContextTrackerInternalTest, ExecutionContextDataShouldDestroy) {
// With no references "ShouldDestroy" should return true. // With no references "ShouldDestroy" should return true.
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt); process_data, mock_graph_.frame->frame_token(), nullptr);
EXPECT_FALSE(ec_data->remote_frame_data()); EXPECT_FALSE(ec_data->remote_frame_data());
EXPECT_EQ(0u, ec_data->v8_context_count()); EXPECT_EQ(0u, ec_data->v8_context_count());
EXPECT_TRUE(ec_data->ShouldDestroy()); EXPECT_TRUE(ec_data->ShouldDestroy());
...@@ -193,7 +194,7 @@ TEST_F(V8ContextTrackerInternalTest, ...@@ -193,7 +194,7 @@ TEST_F(V8ContextTrackerInternalTest,
// Create an ExecutionContextData. // Create an ExecutionContextData.
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt); process_data, mock_graph_.frame->frame_token(), nullptr);
auto* raw_ec_data = ec_data.get(); auto* raw_ec_data = ec_data.get();
EXPECT_FALSE(ec_data->IsTracked()); EXPECT_FALSE(ec_data->IsTracked());
...@@ -236,7 +237,7 @@ TEST_F(V8ContextTrackerInternalTest, ...@@ -236,7 +237,7 @@ TEST_F(V8ContextTrackerInternalTest,
// Create an ExecutionContextData. // Create an ExecutionContextData.
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt); process_data, mock_graph_.frame->frame_token(), nullptr);
auto* raw_ec_data = ec_data.get(); auto* raw_ec_data = ec_data.get();
EXPECT_FALSE(ec_data->IsTracked()); EXPECT_FALSE(ec_data->IsTracked());
...@@ -276,7 +277,7 @@ TEST_F(V8ContextTrackerInternalTest, ContextCounts) { ...@@ -276,7 +277,7 @@ TEST_F(V8ContextTrackerInternalTest, ContextCounts) {
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data, mock_graph_.frame->frame_token(), base::nullopt); process_data, mock_graph_.frame->frame_token(), nullptr);
auto* raw_ec_data = ec_data.get(); auto* raw_ec_data = ec_data.get();
std::unique_ptr<V8ContextData> v8_data1 = std::make_unique<V8ContextData>( std::unique_ptr<V8ContextData> v8_data1 = std::make_unique<V8ContextData>(
...@@ -365,7 +366,7 @@ class V8ContextTrackerInternalTearDownOrderTest ...@@ -365,7 +366,7 @@ class V8ContextTrackerInternalTearDownOrderTest
// Create an ExecutionContextData. // Create an ExecutionContextData.
std::unique_ptr<ExecutionContextData> ec_data = std::unique_ptr<ExecutionContextData> ec_data =
std::make_unique<ExecutionContextData>( std::make_unique<ExecutionContextData>(
process_data_, mock_graph_.frame->frame_token(), base::nullopt); process_data_, mock_graph_.frame->frame_token(), nullptr);
ec_data_ = ec_data.get(); ec_data_ = ec_data.get();
// Create a RemoteFrameData. // Create a RemoteFrameData.
......
// Copyright 2020 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 "components/performance_manager/v8_memory/v8_context_tracker_types.h"
namespace performance_manager {
namespace v8_memory {
////////////////////////////////////////////////////////////////////////////////
// IframeAttributionData implementation:
IframeAttributionData::IframeAttributionData() = default;
IframeAttributionData::IframeAttributionData(const IframeAttributionData&) =
default;
IframeAttributionData& IframeAttributionData::operator=(
const IframeAttributionData&) = default;
IframeAttributionData::~IframeAttributionData() = default;
// static
IframeAttributionData IframeAttributionData::Create(
const base::Optional<std::string>& id,
const base::Optional<std::string>& src) {
IframeAttributionData data;
data.id = id;
data.src = src;
return data;
}
////////////////////////////////////////////////////////////////////////////////
// V8ContextDescription implementation:
V8ContextDescription::V8ContextDescription() = default;
V8ContextDescription::V8ContextDescription(const V8ContextDescription&) =
default;
V8ContextDescription& V8ContextDescription::operator=(
const V8ContextDescription&) = default;
V8ContextDescription::~V8ContextDescription() = default;
// static
V8ContextDescription V8ContextDescription::Create(
blink::V8ContextToken token,
V8ContextWorldType world_type,
const base::Optional<std::string>& world_name,
const base::Optional<blink::ExecutionContextToken>&
execution_context_token) {
V8ContextDescription desc;
desc.token = token;
desc.world_type = world_type;
desc.world_name = world_name;
desc.execution_context_token = execution_context_token;
return desc;
}
} // namespace v8_memory
} // namespace performance_manager
<!DOCTYPE html>
<html language="en">
<head>
<title>a.com</title>
<script language="javascript">
function onLoad() {
// The embedded iframe is dynamically loaded because we don't know
// the port where it is hosted until runtime.
console.log("a_embeds_a.html loaded");
let l = window.location;
let url = `${l.protocol}//a.com:${l.port}/a.html`;
console.log(`loading iframe [${url}]`);
let iframe = document.createElement('iframe');
iframe.setAttribute('src', url);
document.body.appendChild(iframe);
}
</script>
</head>
<body onload="onLoad();">
Welcome to a.com, embedding a.com!
</body>
</html>
...@@ -4,13 +4,71 @@ ...@@ -4,13 +4,71 @@
#include "third_party/blink/renderer/controller/performance_manager/renderer_resource_coordinator_impl.h" #include "third_party/blink/renderer/controller/performance_manager/renderer_resource_coordinator_impl.h"
#include "base/bind.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/core/dom/document.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/local_frame.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h" #include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
namespace WTF {
// Copies the data by move.
template <>
struct CrossThreadCopier<
performance_manager::mojom::blink::V8ContextDescriptionPtr>
: public WTF::CrossThreadCopierByValuePassThrough<
performance_manager::mojom::blink::V8ContextDescriptionPtr> {};
// Copies the data by move.
template <>
struct CrossThreadCopier<
performance_manager::mojom::blink::IframeAttributionDataPtr>
: public WTF::CrossThreadCopierByValuePassThrough<
performance_manager::mojom::blink::IframeAttributionDataPtr> {};
// Copies the data using the copy constructor.
template <>
struct CrossThreadCopier<blink::V8ContextToken>
: public WTF::CrossThreadCopierPassThrough<blink::V8ContextToken> {};
} // namespace WTF
namespace blink { namespace blink {
namespace {
using performance_manager::mojom::blink::V8ContextWorldType;
// Determines if the given stable world ID is an extension world ID.
// Extensions IDs are 32-character strings containing characters in the range of
// 'a' to 'p', inclusive.
// TODO(chrisha): Lift this somewhere public and common in components/extensions
// and reuse it from there.
bool IsExtensionStableWorldId(const String& stable_world_id) {
if (stable_world_id.IsNull() || stable_world_id.IsEmpty())
return false;
if (stable_world_id.length() != 32)
return false;
for (size_t i = 0; i < stable_world_id.length(); ++i) {
if (stable_world_id[i] < 'a' || stable_world_id[i] > 'p')
return false;
}
return true;
}
} // namespace
RendererResourceCoordinatorImpl::~RendererResourceCoordinatorImpl() = default; RendererResourceCoordinatorImpl::~RendererResourceCoordinatorImpl() = default;
// static // static
...@@ -41,17 +99,94 @@ void RendererResourceCoordinatorImpl::SetMainThreadTaskLoadIsLow( ...@@ -41,17 +99,94 @@ void RendererResourceCoordinatorImpl::SetMainThreadTaskLoadIsLow(
void RendererResourceCoordinatorImpl::OnScriptStateCreated( void RendererResourceCoordinatorImpl::OnScriptStateCreated(
ScriptState* script_state, ScriptState* script_state,
ExecutionContext* execution_context) { ExecutionContext* execution_context) {
// TODO(chrisha): Extract tokens and forward this to the browser! DCHECK(script_state);
if (!service_)
return;
auto v8_desc = performance_manager::mojom::blink::V8ContextDescription::New();
v8_desc->token = script_state->GetToken();
performance_manager::mojom::blink::IframeAttributionDataPtr
iframe_attribution_data;
// Default the world name to being empty.
auto& dom_wrapper = script_state->World();
switch (dom_wrapper.GetWorldType()) {
case DOMWrapperWorld::WorldType::kMain: {
v8_desc->world_type = V8ContextWorldType::kMain;
} break;
case DOMWrapperWorld::WorldType::kIsolated: {
auto stable_world_id = dom_wrapper.NonMainWorldStableId();
if (IsExtensionStableWorldId(stable_world_id)) {
v8_desc->world_type = V8ContextWorldType::kExtension;
v8_desc->world_name = stable_world_id;
} else {
v8_desc->world_type = V8ContextWorldType::kIsolated;
v8_desc->world_name = dom_wrapper.NonMainWorldHumanReadableName();
}
} break;
case DOMWrapperWorld::WorldType::kInspectorIsolated: {
v8_desc->world_type = V8ContextWorldType::kInspector;
} break;
case DOMWrapperWorld::WorldType::kRegExp: {
v8_desc->world_type = V8ContextWorldType::kRegExp;
} break;
case DOMWrapperWorld::WorldType::kForV8ContextSnapshotNonMain: {
// This should not happen in the production browser.
NOTREACHED();
} break;
case DOMWrapperWorld::WorldType::kWorker: {
v8_desc->world_type = V8ContextWorldType::kWorkerOrWorklet;
} break;
}
if (execution_context) {
// This should never happen for a regexp world.
DCHECK_NE(DOMWrapperWorld::WorldType::kRegExp, dom_wrapper.GetWorldType());
v8_desc->execution_context_token =
execution_context->GetExecutionContextToken();
// Only report the iframe data alongside the main world.
// If this is the main world (so also a LocalDOMWindow) ...
if (v8_desc->world_type == V8ContextWorldType::kMain) {
auto* local_dom_window = To<LocalDOMWindow>(execution_context);
// ... with a parent ...
auto* local_frame = local_dom_window->GetFrame();
DCHECK(local_frame);
if (auto* parent_frame = local_frame->Parent()) {
// ... that is also local ...
if (IsA<LocalFrame>(parent_frame)) {
// ... then we want to grab the iframe data associated with this
// frame.
auto* owner = To<HTMLFrameOwnerElement>(local_frame->Owner());
DCHECK(owner);
iframe_attribution_data =
performance_manager::mojom::blink::IframeAttributionData::New();
iframe_attribution_data->id =
owner->FastGetAttribute(html_names::kIdAttr);
iframe_attribution_data->src =
owner->FastGetAttribute(html_names::kSrcAttr);
}
}
}
}
DispatchOnV8ContextCreated(std::move(v8_desc),
std::move(iframe_attribution_data));
} }
void RendererResourceCoordinatorImpl::OnScriptStateDetached( void RendererResourceCoordinatorImpl::OnScriptStateDetached(
ScriptState* script_state) { ScriptState* script_state) {
// TODO(chrisha): Extract tokens and forward this to the browser! DCHECK(script_state);
DispatchOnV8ContextDetached(script_state->GetToken());
} }
void RendererResourceCoordinatorImpl::OnScriptStateDestroyed( void RendererResourceCoordinatorImpl::OnScriptStateDestroyed(
ScriptState* script_state) { ScriptState* script_state) {
// TODO(chrisha): Extract tokens and forward this to the browser! DCHECK(script_state);
DispatchOnV8ContextDestroyed(script_state->GetToken());
} }
RendererResourceCoordinatorImpl::RendererResourceCoordinatorImpl( RendererResourceCoordinatorImpl::RendererResourceCoordinatorImpl(
...@@ -60,4 +195,59 @@ RendererResourceCoordinatorImpl::RendererResourceCoordinatorImpl( ...@@ -60,4 +195,59 @@ RendererResourceCoordinatorImpl::RendererResourceCoordinatorImpl(
service_.Bind(std::move(remote)); service_.Bind(std::move(remote));
} }
void RendererResourceCoordinatorImpl::DispatchOnV8ContextCreated(
performance_manager::mojom::blink::V8ContextDescriptionPtr v8_desc,
performance_manager::mojom::blink::IframeAttributionDataPtr
iframe_attribution_data) {
if (!service_)
return;
// Calls to this can arrive on any thread (due to workers, etc), but the
// interface itself is bound to the main thread. In this case, once we've
// collated the necessary data we bounce over to the main thread. Note that
// posting "this" unretained is safe because the renderer resource coordinator
// is a singleton that leaks at process shutdown.
if (!IsMainThread()) {
blink::PostCrossThreadTask(
*Thread::MainThread()->GetTaskRunner(), FROM_HERE,
WTF::CrossThreadBindOnce(
&RendererResourceCoordinatorImpl::DispatchOnV8ContextCreated,
WTF::CrossThreadUnretained(this), std::move(v8_desc),
std::move(iframe_attribution_data)));
} else {
service_->OnV8ContextCreated(std::move(v8_desc),
std::move(iframe_attribution_data));
}
}
void RendererResourceCoordinatorImpl::DispatchOnV8ContextDetached(
const blink::V8ContextToken& token) {
if (!service_)
return;
// See DispatchOnV8ContextCreated for why this is both needed and safe.
if (!IsMainThread()) {
blink::PostCrossThreadTask(
*Thread::MainThread()->GetTaskRunner(), FROM_HERE,
WTF::CrossThreadBindOnce(
&RendererResourceCoordinatorImpl::DispatchOnV8ContextDetached,
WTF::CrossThreadUnretained(this), token));
} else {
service_->OnV8ContextDetached(token);
}
}
void RendererResourceCoordinatorImpl::DispatchOnV8ContextDestroyed(
const blink::V8ContextToken& token) {
if (!service_)
return;
// See DispatchOnV8ContextCreated for why this is both needed and safe.
if (!IsMainThread()) {
blink::PostCrossThreadTask(
*Thread::MainThread()->GetTaskRunner(), FROM_HERE,
WTF::CrossThreadBindOnce(
&RendererResourceCoordinatorImpl::DispatchOnV8ContextDestroyed,
WTF::CrossThreadUnretained(this), token));
} else {
service_->OnV8ContextDestroyed(token);
}
}
} // namespace blink } // namespace blink
...@@ -5,8 +5,12 @@ ...@@ -5,8 +5,12 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PERFORMANCE_MANAGER_RENDERER_RESOURCE_COORDINATOR_IMPL_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PERFORMANCE_MANAGER_RENDERER_RESOURCE_COORDINATOR_IMPL_H_
#define THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PERFORMANCE_MANAGER_RENDERER_RESOURCE_COORDINATOR_IMPL_H_ #define THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PERFORMANCE_MANAGER_RENDERER_RESOURCE_COORDINATOR_IMPL_H_
#include "base/memory/scoped_refptr.h"
#include "base/sequenced_task_runner.h"
#include "components/performance_manager/public/mojom/coordination_unit.mojom-blink.h" #include "components/performance_manager/public/mojom/coordination_unit.mojom-blink.h"
#include "components/performance_manager/public/mojom/v8_contexts.mojom-blink.h"
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/renderer/controller/controller_export.h" #include "third_party/blink/renderer/controller/controller_export.h"
#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h" #include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
...@@ -39,6 +43,15 @@ class CONTROLLER_EXPORT RendererResourceCoordinatorImpl final ...@@ -39,6 +43,15 @@ class CONTROLLER_EXPORT RendererResourceCoordinatorImpl final
mojo::PendingRemote< mojo::PendingRemote<
performance_manager::mojom::blink::ProcessCoordinationUnit> remote); performance_manager::mojom::blink::ProcessCoordinationUnit> remote);
// Used for dispatching script state events which can arrive on any thread
// but need to be sent outbound from the main thread.
void DispatchOnV8ContextCreated(
performance_manager::mojom::blink::V8ContextDescriptionPtr v8_desc,
performance_manager::mojom::blink::IframeAttributionDataPtr
iframe_attribution_data);
void DispatchOnV8ContextDetached(const blink::V8ContextToken& token);
void DispatchOnV8ContextDestroyed(const blink::V8ContextToken& token);
mojo::Remote<performance_manager::mojom::blink::ProcessCoordinationUnit> mojo::Remote<performance_manager::mojom::blink::ProcessCoordinationUnit>
service_; service_;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment