Commit 6e354da4 authored by Joe Mason's avatar Joe Mason Committed by Commit Bot

[PM] Monitor remote iframe attach/detach for V8ContextTracker

Instruments HTMLFrameOwnerElement to notify RendererResourceCoordinator
whenever the content frame changes. If the new content frame is a
RemoteFrame, RendererResourceCoordinator will notify PerformanceManager
of the frame's RemoteFrameToken and some attribution information for the
iframe. PerformanceManager stores this information in V8ContextTracker.

Also updates frame_test_helpers::CreateProvisional to accept local
frames since the underlying WebLocalFrame::CreateProvisional now allows
this.

Bug: 1085129
Change-Id: I43d7d7f1309db9c4b1760ca32983c0e8a2f51a6d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2363710Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Commit-Queue: Joe Mason <joenotcharles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828284}
parent 4e41c445
...@@ -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/public/execution_context/execution_context_registry.h"
#include "components/performance_manager/v8_memory/v8_context_tracker.h" #include "components/performance_manager/v8_memory/v8_context_tracker.h"
namespace performance_manager { namespace performance_manager {
...@@ -46,6 +47,7 @@ void ProcessNodeImpl::SetMainThreadTaskLoadIsLow( ...@@ -46,6 +47,7 @@ void ProcessNodeImpl::SetMainThreadTaskLoadIsLow(
void ProcessNodeImpl::OnV8ContextCreated( void ProcessNodeImpl::OnV8ContextCreated(
mojom::V8ContextDescriptionPtr description, mojom::V8ContextDescriptionPtr description,
mojom::IframeAttributionDataPtr iframe_attribution_data) { mojom::IframeAttributionDataPtr iframe_attribution_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph())) { if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph())) {
tracker->OnV8ContextCreated(PassKey(), this, *description, tracker->OnV8ContextCreated(PassKey(), this, *description,
std::move(iframe_attribution_data)); std::move(iframe_attribution_data));
...@@ -54,16 +56,55 @@ void ProcessNodeImpl::OnV8ContextCreated( ...@@ -54,16 +56,55 @@ void ProcessNodeImpl::OnV8ContextCreated(
void ProcessNodeImpl::OnV8ContextDetached( void ProcessNodeImpl::OnV8ContextDetached(
const blink::V8ContextToken& v8_context_token) { const blink::V8ContextToken& v8_context_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph())) if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph()))
tracker->OnV8ContextDetached(PassKey(), this, v8_context_token); tracker->OnV8ContextDetached(PassKey(), this, v8_context_token);
} }
void ProcessNodeImpl::OnV8ContextDestroyed( void ProcessNodeImpl::OnV8ContextDestroyed(
const blink::V8ContextToken& v8_context_token) { const blink::V8ContextToken& v8_context_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph())) if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph()))
tracker->OnV8ContextDestroyed(PassKey(), this, v8_context_token); tracker->OnV8ContextDestroyed(PassKey(), this, v8_context_token);
} }
void ProcessNodeImpl::OnRemoteIframeAttached(
const blink::LocalFrameToken& parent_frame_token,
const blink::RemoteFrameToken& remote_frame_token,
mojom::IframeAttributionDataPtr iframe_attribution_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph())) {
auto* ec_registry =
execution_context::ExecutionContextRegistry::GetFromGraph(graph());
DCHECK(ec_registry);
auto* parent_frame_node =
ec_registry->GetFrameNodeByFrameToken(parent_frame_token);
if (parent_frame_node) {
tracker->OnRemoteIframeAttached(
PassKey(), FrameNodeImpl::FromNode(parent_frame_node),
remote_frame_token, std::move(iframe_attribution_data));
}
}
}
void ProcessNodeImpl::OnRemoteIframeDetached(
const blink::LocalFrameToken& parent_frame_token,
const blink::RemoteFrameToken& remote_frame_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (auto* tracker = v8_memory::V8ContextTracker::GetFromGraph(graph())) {
auto* ec_registry =
execution_context::ExecutionContextRegistry::GetFromGraph(graph());
DCHECK(ec_registry);
auto* parent_frame_node =
ec_registry->GetFrameNodeByFrameToken(parent_frame_token);
if (parent_frame_node) {
tracker->OnRemoteIframeDetached(
PassKey(), FrameNodeImpl::FromNode(parent_frame_node),
remote_frame_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
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#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"
#include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager { namespace performance_manager {
...@@ -66,6 +67,13 @@ class ProcessNodeImpl ...@@ -66,6 +67,13 @@ class ProcessNodeImpl
const blink::V8ContextToken& v8_context_token) override; const blink::V8ContextToken& v8_context_token) override;
void OnV8ContextDestroyed( void OnV8ContextDestroyed(
const blink::V8ContextToken& v8_context_token) override; const blink::V8ContextToken& v8_context_token) override;
void OnRemoteIframeAttached(
const blink::LocalFrameToken& parent_frame_token,
const blink::RemoteFrameToken& remote_frame_token,
mojom::IframeAttributionDataPtr iframe_attribution_data) override;
void OnRemoteIframeDetached(
const blink::LocalFrameToken& parent_frame_token,
const blink::RemoteFrameToken& remote_frame_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);
......
...@@ -7,8 +7,8 @@ module performance_manager.mojom; ...@@ -7,8 +7,8 @@ module performance_manager.mojom;
import "mojo/public/mojom/base/process_id.mojom"; 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/v8_contexts.mojom"; import "components/performance_manager/public/mojom/v8_contexts.mojom";
import "components/performance_manager/public/mojom/web_memory.mojom";
import "third_party/blink/public/mojom/tokens/tokens.mojom"; import "third_party/blink/public/mojom/tokens/tokens.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom";
...@@ -78,6 +78,8 @@ interface DocumentCoordinationUnit { ...@@ -78,6 +78,8 @@ interface DocumentCoordinationUnit {
(WebMemoryMeasurement measurement); (WebMemoryMeasurement measurement);
}; };
// Interface used by the RendererResourceCoordinator singleton to send state
// associated with resource management of a renderer process to the embedder.
interface ProcessCoordinationUnit { interface ProcessCoordinationUnit {
// Property signals. // Property signals.
SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low); SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low);
...@@ -113,4 +115,19 @@ interface ProcessCoordinationUnit { ...@@ -113,4 +115,19 @@ interface ProcessCoordinationUnit {
// is never called for contexts in a renderer processes that crashes or is // is never called for contexts in a renderer processes that crashes or is
// otherwise terminated. // otherwise terminated.
OnV8ContextDestroyed(blink.mojom.V8ContextToken v8_context_token); OnV8ContextDestroyed(blink.mojom.V8ContextToken v8_context_token);
// Called when a RemoteFrame is attached as the content of a local iframe.
// The browser process will use this to maintain a map from the RemoteFrame's
// token to the iframe's attribution information as seen by the renderer
// process.
OnRemoteIframeAttached(blink.mojom.LocalFrameToken parent_frame_token,
blink.mojom.RemoteFrameToken remote_frame_token,
IframeAttributionData iframe_attribution_data);
// Called when a RemoteFrame that was the content of a local iframe is
// removed from the frame. Only the frame token is needed because the
// iframe's attribution information was previously associated with the token
// by OnRemoteIframeAttached.
OnRemoteIframeDetached(blink.mojom.LocalFrameToken parent_frame_token,
blink.mojom.RemoteFrameToken remote_frame_token);
}; };
...@@ -196,7 +196,7 @@ void V8ContextTracker::OnV8ContextDestroyed( ...@@ -196,7 +196,7 @@ void V8ContextTracker::OnV8ContextDestroyed(
} }
void V8ContextTracker::OnRemoteIframeAttached( void V8ContextTracker::OnRemoteIframeAttached(
util::PassKey<FrameNodeImpl> key, util::PassKey<ProcessNodeImpl> key,
FrameNodeImpl* parent_frame_node, FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
mojom::IframeAttributionDataPtr iframe_attribution_data) { mojom::IframeAttributionDataPtr iframe_attribution_data) {
...@@ -218,6 +218,7 @@ void V8ContextTracker::OnRemoteIframeAttached( ...@@ -218,6 +218,7 @@ void V8ContextTracker::OnRemoteIframeAttached(
blink::RemoteFrameToken remote_frame_token; blink::RemoteFrameToken remote_frame_token;
mojom::IframeAttributionDataPtr iframe_attribution_data; mojom::IframeAttributionDataPtr iframe_attribution_data;
base::WeakPtr<FrameNode> frame_node; base::WeakPtr<FrameNode> frame_node;
base::WeakPtr<FrameNode> parent_frame_node;
}; };
std::unique_ptr<Data> data( std::unique_ptr<Data> data(
new Data{mojo::GetBadMessageCallback(), remote_frame_token, new Data{mojo::GetBadMessageCallback(), remote_frame_token,
...@@ -227,12 +228,32 @@ void V8ContextTracker::OnRemoteIframeAttached( ...@@ -227,12 +228,32 @@ void V8ContextTracker::OnRemoteIframeAttached(
DCHECK(data); DCHECK(data);
DCHECK(graph); DCHECK(graph);
DCHECK_ON_GRAPH_SEQUENCE(graph); DCHECK_ON_GRAPH_SEQUENCE(graph);
if (data->frame_node) { // Only dispatch if the frame and its parent still exist after the
// round-trip through the UI thread. If the frame still exists but now has
// no parent, we don't need to record IframeAttribution data for it since
// it's now unreachable.
//
// An example of this is the custom <webview> element used in Chrome UI
// (extensions/renderer/resources/guest_view/web_view/web_view.js). This
// element has an inner web contents with an opener relationship to the
// webview, but no parent-child relationship. However since it is a custom
// element implemented on top of <iframe>, the renderer has no way to
// distinguish it from a regular iframe. At the moment the contents is
// attached it has a transient parent frame, which is reported through
// OnRemoteIframeAttached, but the parent frame disappears shortly
// afterward.
//
// TODO(crbug.com/1085129): Write an end-to-end browsertest that covers
// this case once all parts of the measure memory API are hooked up.
if (data->frame_node && data->parent_frame_node) {
auto* frame_node = FrameNodeImpl::FromNode(data->frame_node.get()); auto* frame_node = FrameNodeImpl::FromNode(data->frame_node.get());
auto* parent_frame_node =
FrameNodeImpl::FromNode(data->parent_frame_node.get());
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, std::move(data->iframe_attribution_data)); parent_frame_node, data->remote_frame_token,
std::move(data->iframe_attribution_data));
} }
} }
}); });
...@@ -263,7 +284,7 @@ void V8ContextTracker::OnRemoteIframeAttached( ...@@ -263,7 +284,7 @@ void V8ContextTracker::OnRemoteIframeAttached(
} }
void V8ContextTracker::OnRemoteIframeDetached( void V8ContextTracker::OnRemoteIframeDetached(
util::PassKey<FrameNodeImpl> key, util::PassKey<ProcessNodeImpl> key,
FrameNodeImpl* parent_frame_node, FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token) { const blink::RemoteFrameToken& remote_frame_token) {
DCHECK_ON_GRAPH_SEQUENCE(parent_frame_node->graph()); DCHECK_ON_GRAPH_SEQUENCE(parent_frame_node->graph());
...@@ -306,10 +327,11 @@ void V8ContextTracker::OnRemoteIframeDetached( ...@@ -306,10 +327,11 @@ void V8ContextTracker::OnRemoteIframeDetached(
void V8ContextTracker::OnRemoteIframeAttachedForTesting( void V8ContextTracker::OnRemoteIframeAttachedForTesting(
FrameNodeImpl* frame_node, FrameNodeImpl* frame_node,
FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
mojom::IframeAttributionDataPtr iframe_attribution_data) { mojom::IframeAttributionDataPtr iframe_attribution_data) {
OnRemoteIframeAttachedImpl(base::BindOnce(&FakeReportBadMessageForTesting), OnRemoteIframeAttachedImpl(base::BindOnce(&FakeReportBadMessageForTesting),
frame_node, remote_frame_token, frame_node, parent_frame_node, remote_frame_token,
std::move(iframe_attribution_data)); std::move(iframe_attribution_data));
} }
...@@ -449,11 +471,18 @@ void V8ContextTracker::OnBeforeProcessNodeRemoved(const ProcessNode* node) { ...@@ -449,11 +471,18 @@ void V8ContextTracker::OnBeforeProcessNodeRemoved(const ProcessNode* node) {
void V8ContextTracker::OnRemoteIframeAttachedImpl( void V8ContextTracker::OnRemoteIframeAttachedImpl(
mojo::ReportBadMessageCallback bad_message_callback, mojo::ReportBadMessageCallback bad_message_callback,
FrameNodeImpl* frame_node, FrameNodeImpl* frame_node,
FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
mojom::IframeAttributionDataPtr 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());
if (frame_node->parent_frame_node() != parent_frame_node) {
std::move(bad_message_callback)
.Run("OnRemoteIframeAttached has wrong parent frame");
return;
}
if (data_store_->Get(remote_frame_token)) { if (data_store_->Get(remote_frame_token)) {
std::move(bad_message_callback).Run("repeated OnRemoteIframeAttached"); std::move(bad_message_callback).Run("repeated OnRemoteIframeAttached");
return; return;
......
...@@ -165,7 +165,7 @@ class V8ContextTracker final ...@@ -165,7 +165,7 @@ class V8ContextTracker final
// be called for bookkeeping. This should only be called once for a given // be called for bookkeeping. This should only be called once for a given
// |remote_frame_token|. // |remote_frame_token|.
void OnRemoteIframeAttached( void OnRemoteIframeAttached(
util::PassKey<FrameNodeImpl> key, util::PassKey<ProcessNodeImpl> key,
FrameNodeImpl* parent_frame_node, FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
mojom::IframeAttributionDataPtr iframe_attribution_data); mojom::IframeAttributionDataPtr iframe_attribution_data);
...@@ -180,7 +180,7 @@ class V8ContextTracker final ...@@ -180,7 +180,7 @@ class V8ContextTracker final
// |remote_frame_token|, and only after a matching "OnRemoteIframeAttached" // |remote_frame_token|, and only after a matching "OnRemoteIframeAttached"
// call. // call.
void OnRemoteIframeDetached( void OnRemoteIframeDetached(
util::PassKey<FrameNodeImpl> key, util::PassKey<ProcessNodeImpl> key,
FrameNodeImpl* parent_frame_node, FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token); const blink::RemoteFrameToken& remote_frame_token);
...@@ -189,6 +189,7 @@ class V8ContextTracker final ...@@ -189,6 +189,7 @@ class V8ContextTracker final
void OnRemoteIframeAttachedForTesting( void OnRemoteIframeAttachedForTesting(
FrameNodeImpl* frame_node, FrameNodeImpl* frame_node,
FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
mojom::IframeAttributionDataPtr iframe_attribution_data); mojom::IframeAttributionDataPtr iframe_attribution_data);
...@@ -229,6 +230,7 @@ class V8ContextTracker final ...@@ -229,6 +230,7 @@ class V8ContextTracker final
void OnRemoteIframeAttachedImpl( void OnRemoteIframeAttachedImpl(
mojo::ReportBadMessageCallback bad_message_callback, mojo::ReportBadMessageCallback bad_message_callback,
FrameNodeImpl* frame_node, FrameNodeImpl* frame_node,
FrameNodeImpl* parent_frame_node,
const blink::RemoteFrameToken& remote_frame_token, const blink::RemoteFrameToken& remote_frame_token,
mojom::IframeAttributionDataPtr iframe_attribution_data); mojom::IframeAttributionDataPtr iframe_attribution_data);
......
...@@ -140,13 +140,20 @@ TEST_F(V8ContextTrackerDeathTest, MissingContextExplodes) { ...@@ -140,13 +140,20 @@ TEST_F(V8ContextTrackerDeathTest, MissingContextExplodes) {
} }
TEST_F(V8ContextTrackerDeathTest, DoubleRemoteFrameCreatedExplodes) { TEST_F(V8ContextTrackerDeathTest, DoubleRemoteFrameCreatedExplodes) {
tracker->OnRemoteIframeAttachedForTesting(mock_graph.child_frame.get(), tracker->OnRemoteIframeAttachedForTesting(
kChildFrameRemoteToken, mock_graph.child_frame.get(), mock_graph.frame.get(),
GetFakeIframeAttributionDataPtr()); kChildFrameRemoteToken, GetFakeIframeAttributionDataPtr());
EXPECT_DCHECK_DEATH(tracker->OnRemoteIframeAttachedForTesting( EXPECT_DCHECK_DEATH(tracker->OnRemoteIframeAttachedForTesting(
mock_graph.child_frame.get(), kChildFrameRemoteToken, mock_graph.child_frame.get(), mock_graph.frame.get(),
GetFakeIframeAttributionDataPtr())); kChildFrameRemoteToken, GetFakeIframeAttributionDataPtr()));
}
TEST_F(V8ContextTrackerDeathTest, RemoteFrameWithBadParentExplodes) {
// child_frame's real parent is frame.
EXPECT_DCHECK_DEATH(tracker->OnRemoteIframeAttachedForTesting(
mock_graph.child_frame.get(), mock_graph.child_frame.get(),
kChildFrameRemoteToken, GetFakeIframeAttributionDataPtr()));
} }
TEST_F(V8ContextTrackerDeathTest, IframeAttributionDataForMainFrameExplodes) { TEST_F(V8ContextTrackerDeathTest, IframeAttributionDataForMainFrameExplodes) {
...@@ -170,7 +177,7 @@ TEST_F(V8ContextTrackerDeathTest, IframeAttributionDataForInProcessChildFrame) { ...@@ -170,7 +177,7 @@ TEST_F(V8ContextTrackerDeathTest, IframeAttributionDataForInProcessChildFrame) {
// notification should explode because |child2_frame| is in the same process // notification should explode because |child2_frame| is in the same process
// as its parent. // as its parent.
EXPECT_DCHECK_DEATH(tracker->OnRemoteIframeAttachedForTesting( EXPECT_DCHECK_DEATH(tracker->OnRemoteIframeAttachedForTesting(
child2_frame.get(), blink::RemoteFrameToken(), child2_frame.get(), mock_graph.frame.get(), blink::RemoteFrameToken(),
GetFakeIframeAttributionDataPtr())); GetFakeIframeAttributionDataPtr()));
// This should succeed because iframe data is provided. // This should succeed because iframe data is provided.
...@@ -344,8 +351,8 @@ TEST_F(V8ContextTrackerTest, MultipleV8ContextsForExecutionContext) { ...@@ -344,8 +351,8 @@ TEST_F(V8ContextTrackerTest, MultipleV8ContextsForExecutionContext) {
{ {
SCOPED_TRACE(""); SCOPED_TRACE("");
tracker->OnRemoteIframeAttachedForTesting( tracker->OnRemoteIframeAttachedForTesting(
mock_graph.child_frame.get(), kChildFrameRemoteToken, mock_graph.child_frame.get(), mock_graph.frame.get(),
GetFakeIframeAttributionDataPtr()); kChildFrameRemoteToken, GetFakeIframeAttributionDataPtr());
EXPECT_THAT(tracker, CountsMatch(4, 2)); EXPECT_THAT(tracker, CountsMatch(4, 2));
EXPECT_THAT(tracker, DetachedCountsMatch(0, 0)); EXPECT_THAT(tracker, DetachedCountsMatch(0, 0));
} }
...@@ -483,8 +490,8 @@ TEST_F(V8ContextTrackerTest, AllEventOrders) { ...@@ -483,8 +490,8 @@ TEST_F(V8ContextTrackerTest, AllEventOrders) {
auto iframeattach = [self = this]() { auto iframeattach = [self = this]() {
SCOPED_TRACE(""); SCOPED_TRACE("");
self->tracker->OnRemoteIframeAttachedForTesting( self->tracker->OnRemoteIframeAttachedForTesting(
self->mock_graph.child_frame.get(), kChildFrameRemoteToken, self->mock_graph.child_frame.get(), self->mock_graph.frame.get(),
GetFakeIframeAttributionDataPtr()); kChildFrameRemoteToken, GetFakeIframeAttributionDataPtr());
}; };
// Detaches a child iframe. This message is sent over the interface associated // Detaches a child iframe. This message is sent over the interface associated
...@@ -757,9 +764,9 @@ TEST_F(V8ContextTrackerTest, PublicApi) { ...@@ -757,9 +764,9 @@ TEST_F(V8ContextTrackerTest, PublicApi) {
// Provide iframe data for the child frame. // Provide iframe data for the child frame.
ASSERT_FALSE(ec_state->iframe_attribution_data); ASSERT_FALSE(ec_state->iframe_attribution_data);
tracker->OnRemoteIframeAttachedForTesting(mock_graph.child_frame.get(), tracker->OnRemoteIframeAttachedForTesting(
kChildFrameRemoteToken, mock_graph.child_frame.get(), mock_graph.frame.get(),
GetFakeIframeAttributionDataPtr()); kChildFrameRemoteToken, GetFakeIframeAttributionDataPtr());
const auto& iad = ec_state->iframe_attribution_data; const auto& iad = ec_state->iframe_attribution_data;
ASSERT_TRUE(iad); ASSERT_TRUE(iad);
......
...@@ -28,9 +28,9 @@ namespace blink { ...@@ -28,9 +28,9 @@ namespace blink {
// counterpart in the browser. // counterpart in the browser.
using LocalFrameToken = util::TokenType<class LocalFrameTokenTypeMarker>; using LocalFrameToken = util::TokenType<class LocalFrameTokenTypeMarker>;
// Uniquely identifies an blink::RemoteFrame / blink::WebRemoteFrame / // Uniquely identifies a blink::RemoteFrame / blink::WebRemoteFrame /
// content::RenderFrameProxy in a renderer process, and its // content::RenderFrameProxy in a renderer process, and its
// ontent::RenderFrameProxyHost counterpart in the browser. There can be // content::RenderFrameProxyHost counterpart in the browser. There can be
// multiple RemoteFrames corresponding to a single LocalFrame, and each token // multiple RemoteFrames corresponding to a single LocalFrame, and each token
// will be distinct. // will be distinct.
using RemoteFrameToken = util::TokenType<class RemoteFrameTokenTypeMarker>; using RemoteFrameToken = util::TokenType<class RemoteFrameTokenTypeMarker>;
......
...@@ -195,6 +195,7 @@ source_set("blink_unittests_sources") { ...@@ -195,6 +195,7 @@ source_set("blink_unittests_sources") {
testonly = true testonly = true
sources = [ sources = [
"performance_manager/renderer_resource_coordinator_impl_test.cc",
"performance_manager/v8_detailed_memory_reporter_impl_test.cc", "performance_manager/v8_detailed_memory_reporter_impl_test.cc",
"performance_manager/v8_worker_memory_reporter_test.cc", "performance_manager/v8_worker_memory_reporter_test.cc",
"tests/run_all_tests.cc", "tests/run_all_tests.cc",
...@@ -224,6 +225,7 @@ source_set("blink_unittests_sources") { ...@@ -224,6 +225,7 @@ source_set("blink_unittests_sources") {
"//base:i18n", "//base:i18n",
"//base/test:test_support", "//base/test:test_support",
"//components/payments/mojom:mojom_blink", "//components/payments/mojom:mojom_blink",
"//components/performance_manager/public/mojom:mojom_blink",
"//content/test:test_support", "//content/test:test_support",
"//gpu:test_support", "//gpu:test_support",
"//testing/gmock", "//testing/gmock",
......
...@@ -4,13 +4,20 @@ ...@@ -4,13 +4,20 @@
#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 <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/check.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/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/frame/frame_owner_element_type.mojom-blink.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/dom/document.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/frame.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.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/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/remote_frame.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.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/core/html_names.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
...@@ -18,24 +25,30 @@ ...@@ -18,24 +25,30 @@
#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/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
using performance_manager::mojom::blink::IframeAttributionData;
using performance_manager::mojom::blink::IframeAttributionDataPtr;
using performance_manager::mojom::blink::ProcessCoordinationUnit;
using performance_manager::mojom::blink::V8ContextDescription;
using performance_manager::mojom::blink::V8ContextDescriptionPtr;
using performance_manager::mojom::blink::V8ContextWorldType;
namespace WTF { namespace WTF {
// Copies the data by move. // Copies the data by move.
template <> template <>
struct CrossThreadCopier< struct CrossThreadCopier<V8ContextDescriptionPtr>
performance_manager::mojom::blink::V8ContextDescriptionPtr> : public WTF::CrossThreadCopierByValuePassThrough<V8ContextDescriptionPtr> {
: public WTF::CrossThreadCopierByValuePassThrough< };
performance_manager::mojom::blink::V8ContextDescriptionPtr> {};
// Copies the data by move. // Copies the data by move.
template <> template <>
struct CrossThreadCopier< struct CrossThreadCopier<IframeAttributionDataPtr>
performance_manager::mojom::blink::IframeAttributionDataPtr>
: public WTF::CrossThreadCopierByValuePassThrough< : public WTF::CrossThreadCopierByValuePassThrough<
performance_manager::mojom::blink::IframeAttributionDataPtr> {}; IframeAttributionDataPtr> {};
// Copies the data using the copy constructor. // Copies the data using the copy constructor.
template <> template <>
...@@ -46,9 +59,9 @@ struct CrossThreadCopier<blink::V8ContextToken> ...@@ -46,9 +59,9 @@ struct CrossThreadCopier<blink::V8ContextToken>
namespace blink { namespace blink {
namespace { using mojom::blink::FrameOwnerElementType;
using performance_manager::mojom::blink::V8ContextWorldType; namespace {
// Determines if the given stable world ID is an extension world ID. // Determines if the given stable world ID is an extension world ID.
// Extensions IDs are 32-character strings containing characters in the range of // Extensions IDs are 32-character strings containing characters in the range of
...@@ -67,6 +80,33 @@ bool IsExtensionStableWorldId(const String& stable_world_id) { ...@@ -67,6 +80,33 @@ bool IsExtensionStableWorldId(const String& stable_world_id) {
return true; return true;
} }
// Returns true if |owner| is an iframe, false otherwise.
// This will also return true for custom elements built on iframe, like
// <webview> and <guestview>. Since the renderer has no knowledge of these they
// must be filtered out on the browser side.
bool ShouldSendIframeNotificationsFor(const HTMLFrameOwnerElement& owner) {
return owner.OwnerType() == FrameOwnerElementType::kIframe;
}
// If |frame| is a RemoteFrame with a local parent, returns the parent.
// Otherwise returns nullptr.
LocalFrame* GetLocalParentOfRemoteFrame(const Frame& frame) {
if (IsA<RemoteFrame>(frame)) {
if (Frame* parent = frame.Tree().Parent()) {
return DynamicTo<LocalFrame>(parent);
}
}
return nullptr;
}
IframeAttributionDataPtr AttributionDataForOwner(
const HTMLFrameOwnerElement& owner) {
auto attribution_data = IframeAttributionData::New();
attribution_data->id = owner.FastGetAttribute(html_names::kIdAttr);
attribution_data->src = owner.FastGetAttribute(html_names::kSrcAttr);
return attribution_data;
}
} // namespace } // namespace
RendererResourceCoordinatorImpl::~RendererResourceCoordinatorImpl() = default; RendererResourceCoordinatorImpl::~RendererResourceCoordinatorImpl() = default;
...@@ -80,9 +120,7 @@ void RendererResourceCoordinatorImpl::MaybeInitialize() { ...@@ -80,9 +120,7 @@ void RendererResourceCoordinatorImpl::MaybeInitialize() {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
DCHECK(platform); DCHECK(platform);
mojo::PendingRemote< mojo::PendingRemote<ProcessCoordinationUnit> remote;
performance_manager::mojom::blink::ProcessCoordinationUnit>
remote;
platform->GetBrowserInterfaceBroker()->GetInterface( platform->GetBrowserInterfaceBroker()->GetInterface(
remote.InitWithNewPipeAndPassReceiver()); remote.InitWithNewPipeAndPassReceiver());
RendererResourceCoordinator::Set( RendererResourceCoordinator::Set(
...@@ -91,8 +129,7 @@ void RendererResourceCoordinatorImpl::MaybeInitialize() { ...@@ -91,8 +129,7 @@ void RendererResourceCoordinatorImpl::MaybeInitialize() {
void RendererResourceCoordinatorImpl::SetMainThreadTaskLoadIsLow( void RendererResourceCoordinatorImpl::SetMainThreadTaskLoadIsLow(
bool main_thread_task_load_is_low) { bool main_thread_task_load_is_low) {
if (!service_) DCHECK(service_);
return;
service_->SetMainThreadTaskLoadIsLow(main_thread_task_load_is_low); service_->SetMainThreadTaskLoadIsLow(main_thread_task_load_is_low);
} }
...@@ -100,14 +137,12 @@ void RendererResourceCoordinatorImpl::OnScriptStateCreated( ...@@ -100,14 +137,12 @@ void RendererResourceCoordinatorImpl::OnScriptStateCreated(
ScriptState* script_state, ScriptState* script_state,
ExecutionContext* execution_context) { ExecutionContext* execution_context) {
DCHECK(script_state); DCHECK(script_state);
if (!service_) DCHECK(service_);
return;
auto v8_desc = performance_manager::mojom::blink::V8ContextDescription::New(); auto v8_desc = V8ContextDescription::New();
v8_desc->token = script_state->GetToken(); v8_desc->token = script_state->GetToken();
performance_manager::mojom::blink::IframeAttributionDataPtr IframeAttributionDataPtr iframe_attribution_data;
iframe_attribution_data;
// Default the world name to being empty. // Default the world name to being empty.
...@@ -162,12 +197,7 @@ void RendererResourceCoordinatorImpl::OnScriptStateCreated( ...@@ -162,12 +197,7 @@ void RendererResourceCoordinatorImpl::OnScriptStateCreated(
// frame. // frame.
auto* owner = To<HTMLFrameOwnerElement>(local_frame->Owner()); auto* owner = To<HTMLFrameOwnerElement>(local_frame->Owner());
DCHECK(owner); DCHECK(owner);
iframe_attribution_data = iframe_attribution_data = AttributionDataForOwner(*owner);
performance_manager::mojom::blink::IframeAttributionData::New();
iframe_attribution_data->id =
owner->FastGetAttribute(html_names::kIdAttr);
iframe_attribution_data->src =
owner->FastGetAttribute(html_names::kSrcAttr);
} }
} }
} }
...@@ -189,18 +219,42 @@ void RendererResourceCoordinatorImpl::OnScriptStateDestroyed( ...@@ -189,18 +219,42 @@ void RendererResourceCoordinatorImpl::OnScriptStateDestroyed(
DispatchOnV8ContextDestroyed(script_state->GetToken()); DispatchOnV8ContextDestroyed(script_state->GetToken());
} }
void RendererResourceCoordinatorImpl::OnBeforeContentFrameAttached(
const Frame& frame,
const HTMLFrameOwnerElement& owner) {
DCHECK(service_);
if (!ShouldSendIframeNotificationsFor(owner))
return;
LocalFrame* parent = GetLocalParentOfRemoteFrame(frame);
if (!parent)
return;
service_->OnRemoteIframeAttached(LocalFrameToken(parent->GetFrameToken()),
RemoteFrameToken(frame.GetFrameToken()),
AttributionDataForOwner(owner));
}
void RendererResourceCoordinatorImpl::OnBeforeContentFrameDetached(
const Frame& frame,
const HTMLFrameOwnerElement& owner) {
DCHECK(service_);
if (!ShouldSendIframeNotificationsFor(owner))
return;
LocalFrame* parent = GetLocalParentOfRemoteFrame(frame);
if (!parent)
return;
service_->OnRemoteIframeDetached(LocalFrameToken(parent->GetFrameToken()),
RemoteFrameToken(frame.GetFrameToken()));
}
RendererResourceCoordinatorImpl::RendererResourceCoordinatorImpl( RendererResourceCoordinatorImpl::RendererResourceCoordinatorImpl(
mojo::PendingRemote< mojo::PendingRemote<ProcessCoordinationUnit> remote) {
performance_manager::mojom::blink::ProcessCoordinationUnit> remote) {
service_.Bind(std::move(remote)); service_.Bind(std::move(remote));
} }
void RendererResourceCoordinatorImpl::DispatchOnV8ContextCreated( void RendererResourceCoordinatorImpl::DispatchOnV8ContextCreated(
performance_manager::mojom::blink::V8ContextDescriptionPtr v8_desc, V8ContextDescriptionPtr v8_desc,
performance_manager::mojom::blink::IframeAttributionDataPtr IframeAttributionDataPtr iframe_attribution_data) {
iframe_attribution_data) { DCHECK(service_);
if (!service_)
return;
// Calls to this can arrive on any thread (due to workers, etc), but the // 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 // 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 // collated the necessary data we bounce over to the main thread. Note that
...@@ -221,8 +275,7 @@ void RendererResourceCoordinatorImpl::DispatchOnV8ContextCreated( ...@@ -221,8 +275,7 @@ void RendererResourceCoordinatorImpl::DispatchOnV8ContextCreated(
void RendererResourceCoordinatorImpl::DispatchOnV8ContextDetached( void RendererResourceCoordinatorImpl::DispatchOnV8ContextDetached(
const blink::V8ContextToken& token) { const blink::V8ContextToken& token) {
if (!service_) DCHECK(service_);
return;
// See DispatchOnV8ContextCreated for why this is both needed and safe. // See DispatchOnV8ContextCreated for why this is both needed and safe.
if (!IsMainThread()) { if (!IsMainThread()) {
blink::PostCrossThreadTask( blink::PostCrossThreadTask(
...@@ -236,8 +289,7 @@ void RendererResourceCoordinatorImpl::DispatchOnV8ContextDetached( ...@@ -236,8 +289,7 @@ void RendererResourceCoordinatorImpl::DispatchOnV8ContextDetached(
} }
void RendererResourceCoordinatorImpl::DispatchOnV8ContextDestroyed( void RendererResourceCoordinatorImpl::DispatchOnV8ContextDestroyed(
const blink::V8ContextToken& token) { const blink::V8ContextToken& token) {
if (!service_) DCHECK(service_);
return;
// See DispatchOnV8ContextCreated for why this is both needed and safe. // See DispatchOnV8ContextCreated for why this is both needed and safe.
if (!IsMainThread()) { if (!IsMainThread()) {
blink::PostCrossThreadTask( blink::PostCrossThreadTask(
......
...@@ -37,8 +37,14 @@ class CONTROLLER_EXPORT RendererResourceCoordinatorImpl final ...@@ -37,8 +37,14 @@ class CONTROLLER_EXPORT RendererResourceCoordinatorImpl final
ExecutionContext* execution_context) final; ExecutionContext* execution_context) final;
void OnScriptStateDetached(ScriptState* script_state) final; void OnScriptStateDetached(ScriptState* script_state) final;
void OnScriptStateDestroyed(ScriptState* script_state) final; void OnScriptStateDestroyed(ScriptState* script_state) final;
void OnBeforeContentFrameAttached(const Frame& frame,
const HTMLFrameOwnerElement& owner) final;
void OnBeforeContentFrameDetached(const Frame& frame,
const HTMLFrameOwnerElement& owner) final;
private: private:
friend class RendererResourceCoordinatorImplTest;
explicit RendererResourceCoordinatorImpl( explicit RendererResourceCoordinatorImpl(
mojo::PendingRemote< mojo::PendingRemote<
performance_manager::mojom::blink::ProcessCoordinationUnit> remote); performance_manager::mojom::blink::ProcessCoordinationUnit> remote);
......
...@@ -253,7 +253,7 @@ WebLocalFrameImpl* CreateLocalChild( ...@@ -253,7 +253,7 @@ WebLocalFrameImpl* CreateLocalChild(
return frame; return frame;
} }
WebLocalFrameImpl* CreateProvisional(WebRemoteFrame& old_frame, WebLocalFrameImpl* CreateProvisional(WebFrame& old_frame,
TestWebFrameClient* client) { TestWebFrameClient* client) {
std::unique_ptr<TestWebFrameClient> owned_client; std::unique_ptr<TestWebFrameClient> owned_client;
client = CreateDefaultClientIfNeeded(client, owned_client); client = CreateDefaultClientIfNeeded(client, owned_client);
......
...@@ -161,9 +161,9 @@ WebLocalFrameImpl* CreateLocalChild(WebLocalFrame& parent, ...@@ -161,9 +161,9 @@ WebLocalFrameImpl* CreateLocalChild(WebLocalFrame& parent,
blink::mojom::blink::TreeScopeType, blink::mojom::blink::TreeScopeType,
std::unique_ptr<TestWebFrameClient>); std::unique_ptr<TestWebFrameClient>);
// Helper for creating a provisional local frame that can replace a remote // Helper for creating a provisional local frame that can replace a local or
// frame. // remote frame.
WebLocalFrameImpl* CreateProvisional(WebRemoteFrame& old_frame, WebLocalFrameImpl* CreateProvisional(WebFrame& old_frame,
TestWebFrameClient* = nullptr); TestWebFrameClient* = nullptr);
// Helper for creating a remote frame. Generally used when creating a remote // Helper for creating a remote frame. Generally used when creating a remote
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include "third_party/blink/renderer/core/timing/window_performance.h" #include "third_party/blink/renderer/core/timing/window_performance.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/heap_allocator.h" #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/network/network_state_notifier.h" #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
...@@ -197,6 +198,12 @@ void HTMLFrameOwnerElement::SetContentFrame(Frame& frame) { ...@@ -197,6 +198,12 @@ void HTMLFrameOwnerElement::SetContentFrame(Frame& frame) {
DCHECK(!lazy_load_frame_observer_ || DCHECK(!lazy_load_frame_observer_ ||
!lazy_load_frame_observer_->IsLazyLoadPending()); !lazy_load_frame_observer_->IsLazyLoadPending());
DCHECK_NE(content_frame_, &frame);
auto* resource_coordinator = RendererResourceCoordinator::Get();
if (content_frame_)
resource_coordinator->OnBeforeContentFrameDetached(*content_frame_, *this);
resource_coordinator->OnBeforeContentFrameAttached(frame, *this);
content_frame_ = &frame; content_frame_ = &frame;
// Invalidate compositing inputs, because a remote frame child can cause the // Invalidate compositing inputs, because a remote frame child can cause the
...@@ -225,6 +232,9 @@ void HTMLFrameOwnerElement::ClearContentFrame() { ...@@ -225,6 +232,9 @@ void HTMLFrameOwnerElement::ClearContentFrame() {
CancelPendingLazyLoad(); CancelPendingLazyLoad();
DCHECK_EQ(content_frame_->Owner(), this); DCHECK_EQ(content_frame_->Owner(), this);
RendererResourceCoordinator::Get()->OnBeforeContentFrameDetached(
*content_frame_, *this);
content_frame_ = nullptr; content_frame_ = nullptr;
for (ContainerNode* node = this; node; node = node->ParentOrShadowHostNode()) for (ContainerNode* node = this; node; node = node->ParentOrShadowHostNode())
......
...@@ -34,6 +34,10 @@ class DummyRendererResourceCoordinator final ...@@ -34,6 +34,10 @@ class DummyRendererResourceCoordinator final
ExecutionContext* execution_context) final {} ExecutionContext* execution_context) final {}
void OnScriptStateDetached(ScriptState* script_state) final {} void OnScriptStateDetached(ScriptState* script_state) final {}
void OnScriptStateDestroyed(ScriptState* script_state) final {} void OnScriptStateDestroyed(ScriptState* script_state) final {}
void OnBeforeContentFrameAttached(const Frame& frame,
const HTMLFrameOwnerElement& owner) final {}
void OnBeforeContentFrameDetached(const Frame& frame,
const HTMLFrameOwnerElement& owner) final {}
}; };
} // namespace } // namespace
......
...@@ -11,6 +11,8 @@ namespace blink { ...@@ -11,6 +11,8 @@ namespace blink {
// TODO(chrisha): Remove knowledge of ExecutionContext class from this code! // TODO(chrisha): Remove knowledge of ExecutionContext class from this code!
class ExecutionContext; class ExecutionContext;
class Frame;
class HTMLFrameOwnerElement;
class ScriptState; class ScriptState;
class PLATFORM_EXPORT RendererResourceCoordinator { class PLATFORM_EXPORT RendererResourceCoordinator {
...@@ -48,6 +50,15 @@ class PLATFORM_EXPORT RendererResourceCoordinator { ...@@ -48,6 +50,15 @@ class PLATFORM_EXPORT RendererResourceCoordinator {
virtual void OnScriptStateDetached(ScriptState* script_state) = 0; virtual void OnScriptStateDetached(ScriptState* script_state) = 0;
// Called when the |script_state| itself is garbage collected. // Called when the |script_state| itself is garbage collected.
virtual void OnScriptStateDestroyed(ScriptState* script_state) = 0; virtual void OnScriptStateDestroyed(ScriptState* script_state) = 0;
// Called when |frame| is about to be set as the ContentFrame of |owner|.
virtual void OnBeforeContentFrameAttached(
const Frame& frame,
const HTMLFrameOwnerElement& owner) = 0;
// Called when |frame| is about to be unset as the ContentFrame of |owner|.
virtual void OnBeforeContentFrameDetached(
const Frame& frame,
const HTMLFrameOwnerElement& owner) = 0;
}; };
} // namespace blink } // namespace blink
......
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