Commit 48cf6f04 authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

[PM] Create V8ContextTracker helpers.

This defines types and utility functions that will be used by the
V8ContextTracker implementation.

BUG=1085129

Change-Id: I6dec02d26191a60aa3545133bbe826998693a538
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2427849
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Auto-Submit: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarJoe Mason <joenotcharles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812102}
parent ad83cbc3
......@@ -133,6 +133,10 @@ static_library("performance_manager") {
"service_worker_context_adapter.h",
"tab_helper_frame_node_source.cc",
"tab_helper_frame_node_source.h",
"v8_memory/v8_context_tracker_helpers.cc",
"v8_memory/v8_context_tracker_helpers.h",
"v8_memory/v8_context_tracker_types.cc",
"v8_memory/v8_context_tracker_types.h",
"v8_memory/v8_per_frame_memory_decorator.cc",
"web_contents_proxy.cc",
"web_contents_proxy_impl.cc",
......@@ -223,6 +227,7 @@ source_set("unit_tests") {
"performance_manager_tab_helper_unittest.cc",
"performance_manager_unittest.cc",
"registered_objects_unittest.cc",
"v8_memory/v8_context_tracker_helpers_unittest.cc",
"v8_memory/v8_per_frame_memory_decorator_unittest.cc",
"web_contents_proxy_unittest.cc",
"worker_watcher_unittest.cc",
......
......@@ -256,7 +256,7 @@ void GraphImpl::RemoveWorkerNodeObserver(WorkerNodeObserver* observer) {
RemoveObserverImpl(&worker_node_observers_, observer);
}
void GraphImpl::PassToGraph(std::unique_ptr<GraphOwned> graph_owned) {
void GraphImpl::PassToGraphImpl(std::unique_ptr<GraphOwned> graph_owned) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
graph_owned_.PassObject(std::move(graph_owned), this);
}
......
......@@ -68,7 +68,7 @@ class GraphImpl : public Graph {
void RemoveProcessNodeObserver(ProcessNodeObserver* observer) override;
void RemoveSystemNodeObserver(SystemNodeObserver* observer) override;
void RemoveWorkerNodeObserver(WorkerNodeObserver* observer) override;
void PassToGraph(std::unique_ptr<GraphOwned> graph_owned) override;
void PassToGraphImpl(std::unique_ptr<GraphOwned> graph_owned) override;
std::unique_ptr<GraphOwned> TakeFromGraph(GraphOwned* graph_owned) override;
void RegisterObject(GraphRegistered* object) override;
void UnregisterObject(GraphRegistered* object) override;
......
......@@ -67,10 +67,19 @@ class Graph {
// For convenience, allows you to pass ownership of an object to the graph.
// Useful for attaching observers that will live with the graph until it dies.
// If you can name the object you can also take it back via "TakeFromGraph".
virtual void PassToGraph(std::unique_ptr<GraphOwned> graph_owned) = 0;
virtual void PassToGraphImpl(std::unique_ptr<GraphOwned> graph_owned) = 0;
virtual std::unique_ptr<GraphOwned> TakeFromGraph(
GraphOwned* graph_owned) = 0;
// Templated PassToGraph helper that also returns a pointer to the object,
// which makes it easy to use PassToGraph in constructors.
template <typename DerivedType>
DerivedType* PassToGraph(std::unique_ptr<DerivedType> graph_owned) {
DerivedType* object = graph_owned.get();
PassToGraphImpl(std::move(graph_owned));
return object;
}
// A TakeFromGraph helper for taking back the ownership of a GraphOwned
// subclass.
template <typename DerivedType>
......
// 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_helpers.h"
#include "base/check.h"
#include "base/notreached.h"
#include "components/performance_manager/graph/frame_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_registry.h"
#include "components/performance_manager/v8_memory/v8_context_tracker_types.h"
namespace performance_manager {
namespace v8_memory {
namespace {
bool IsSynchronousIframeAttributionDataExpected(
const execution_context::ExecutionContext* ec) {
DCHECK(ec);
auto* frame = ec->GetFrameNode();
if (!frame)
return false;
if (frame->IsMainFrame())
return false;
// Iframe data is expected if this node is in the same process as its
// parent.
return frame->GetProcessNode() ==
frame->GetParentFrameNode()->GetProcessNode();
}
} // namespace
blink::ExecutionContextToken ToExecutionContextToken(
const blink::WorkerToken& token) {
if (token.Is<blink::DedicatedWorkerToken>()) {
return blink::ExecutionContextToken(
token.GetAs<blink::DedicatedWorkerToken>());
}
if (token.Is<blink::ServiceWorkerToken>()) {
return blink::ExecutionContextToken(
token.GetAs<blink::ServiceWorkerToken>());
}
// This will DCHECK for us if the token isn't a SharedWorkerToken.
return blink::ExecutionContextToken(token.GetAs<blink::SharedWorkerToken>());
}
bool HasCrossProcessParent(const FrameNode* frame_node) {
DCHECK(frame_node);
if (frame_node->IsMainFrame())
return false;
const ProcessNode* process = frame_node->GetProcessNode();
const ProcessNode* parent_process =
frame_node->GetParentFrameNode()->GetProcessNode();
return process != parent_process;
}
bool IsValidExtensionId(const std::string& s) {
// Must be a 32-character string with lowercase letters between a and p,
// inclusive.
if (s.size() != 32u)
return false;
for (char c : s) {
if (c < 'a' || c > 'p')
return false;
}
return true;
}
bool IsWorkletToken(const blink::ExecutionContextToken& token) {
return token.Is<blink::AnimationWorkletToken>() ||
token.Is<blink::AudioWorkletToken>() ||
token.Is<blink::LayoutWorkletToken>() ||
token.Is<blink::PaintWorkletToken>();
}
const execution_context::ExecutionContext* GetExecutionContext(
const blink::ExecutionContextToken& token,
Graph* graph) {
auto* registry =
execution_context::ExecutionContextRegistry::GetFromGraph(graph);
DCHECK(registry);
return registry->GetExecutionContextByToken(token);
}
V8ContextDescriptionStatus ValidateV8ContextDescription(
const V8ContextDescription& description) {
switch (description.world_type) {
case V8ContextWorldType::kMain: {
if (description.world_name)
return V8ContextDescriptionStatus::kUnexpectedWorldName;
if (!description.execution_context_token)
return V8ContextDescriptionStatus::kMissingExecutionContextToken;
if (!description.execution_context_token->Is<blink::LocalFrameToken>())
return V8ContextDescriptionStatus::kMissingLocalFrameToken;
} break;
case V8ContextWorldType::kWorkerOrWorklet: {
if (description.world_name)
return V8ContextDescriptionStatus::kUnexpectedWorldName;
if (!description.execution_context_token)
return V8ContextDescriptionStatus::kMissingExecutionContextToken;
if (description.execution_context_token->Is<blink::LocalFrameToken>())
return V8ContextDescriptionStatus::kUnexpectedLocalFrameToken;
} break;
case V8ContextWorldType::kExtension: {
if (!description.world_name)
return V8ContextDescriptionStatus::kMissingWorldName;
if (!IsValidExtensionId(description.world_name.value()))
return V8ContextDescriptionStatus::kInvalidExtensionWorldName;
// Extensions can only inject into frames and workers, *not* worklets.
if (!description.execution_context_token)
return V8ContextDescriptionStatus::kMissingExecutionContextToken;
if (IsWorkletToken(description.execution_context_token.value()))
return V8ContextDescriptionStatus::kUnexpectedWorkletToken;
} break;
case V8ContextWorldType::kIsolated: {
// World names are optional in isolated worlds.
// Only frame and workers can have isolated worlds, *not* worklets.
if (!description.execution_context_token)
return V8ContextDescriptionStatus::kMissingExecutionContextToken;
if (IsWorkletToken(description.execution_context_token.value()))
return V8ContextDescriptionStatus::kUnexpectedWorkletToken;
} break;
case V8ContextWorldType::kInspector: {
if (description.world_name)
return V8ContextDescriptionStatus::kUnexpectedWorldName;
// Devtools can only inject into frames and workers, *not* worklets.
if (!description.execution_context_token)
return V8ContextDescriptionStatus::kMissingExecutionContextToken;
if (IsWorkletToken(description.execution_context_token.value()))
return V8ContextDescriptionStatus::kUnexpectedWorkletToken;
} break;
case V8ContextWorldType::kRegExp: {
// Regexp worlds have no additional data.
if (description.world_name)
return V8ContextDescriptionStatus::kUnexpectedWorldName;
if (description.execution_context_token)
return V8ContextDescriptionStatus::kUnexpectedExecutionContextToken;
} break;
}
return V8ContextDescriptionStatus::kValid;
}
base::Optional<bool> ExpectIframeAttributionDataForV8ContextDescription(
const V8ContextDescription& description,
Graph* graph) {
switch (description.world_type) {
case V8ContextWorldType::kMain: {
// There's no guarantee that the actual ExecutionContext has yet been
// created from our POV as there's a race between V8Context creation
// notifications and node creations. But if it does exist, we sanity check
// that we should in fact be receiving iframe data for this frame.
if (auto* ec = GetExecutionContext(*description.execution_context_token,
graph)) {
return IsSynchronousIframeAttributionDataExpected(ec);
} else {
// Unable to be determined.
return base::nullopt;
}
} break;
case V8ContextWorldType::kWorkerOrWorklet:
case V8ContextWorldType::kExtension:
case V8ContextWorldType::kIsolated:
case V8ContextWorldType::kInspector:
case V8ContextWorldType::kRegExp: {
} break;
}
return false;
}
} // namespace v8_memory
} // namespace performance_manager
// 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.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_HELPERS_H_
#define COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_HELPERS_H_
#include <string>
#include "base/compiler_specific.h"
#include "base/optional.h"
#include "third_party/blink/public/common/tokens/tokens.h"
namespace performance_manager {
class FrameNode;
class Graph;
namespace execution_context {
class ExecutionContext;
} // namespace execution_context
namespace v8_memory {
struct V8ContextDescription;
// Helper function to convert a WorkerToken to an ExecutionContext token.
// TODO(crbug.com/1126285): There should be automatic type conversion for this
// added to MultiToken<>.
blink::ExecutionContextToken ToExecutionContextToken(
const blink::WorkerToken& token) WARN_UNUSED_RESULT;
// Determines if the provided frame has a cross-process parent frame.
bool HasCrossProcessParent(const FrameNode* frame_node) WARN_UNUSED_RESULT;
// Determines if a string is a valid extension ID.
// TODO(crbug.com/1096617): The extension ID should be strongly typed, with
// built-in validation, mojo type-mapping, etc. Ideally this would be done
// directly in extensions/common/extension_id.h.
bool IsValidExtensionId(const std::string& s) WARN_UNUSED_RESULT;
// Returns true if an ExecutionContextToken corresponds to a worklet.
bool IsWorkletToken(const blink::ExecutionContextToken& token)
WARN_UNUSED_RESULT;
// Looks up the execution context corresponding to the given token. Note that
// the ExecutionContextRegistry must be installed on the graph.
const execution_context::ExecutionContext* GetExecutionContext(
const blink::ExecutionContextToken& token,
Graph* graph) WARN_UNUSED_RESULT;
// Return type for V8ContextDescription validation.
enum class V8ContextDescriptionStatus {
kValid,
// World name errors.
kMissingWorldName,
kUnexpectedWorldName,
kInvalidExtensionWorldName,
// ExecutionContextToken errors.
kMissingExecutionContextToken,
kUnexpectedExecutionContextToken,
kMissingLocalFrameToken,
kUnexpectedLocalFrameToken,
kUnexpectedWorkletToken,
};
// Validates the given V8ContextDescription.
V8ContextDescriptionStatus ValidateV8ContextDescription(
const V8ContextDescription& description) WARN_UNUSED_RESULT;
// Determines whether or not IframeAttributionData is expected to accompany the
// provided V8ContextDescription. This is not always able to be determined, in
// which case base::nullopt will be returned. It is assumed that the
// |description| has previously been validated.
base::Optional<bool> ExpectIframeAttributionDataForV8ContextDescription(
const V8ContextDescription& description,
Graph* graph) WARN_UNUSED_RESULT;
} // namespace v8_memory
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_HELPERS_H_
// 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
// 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.
// Defines various types that are used by the V8ContextTracker. Note that all of
// 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_
#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
// document that hosts the iframe. Explicitly allow copy and assign. This is
// used in the performance.measureMemory API.
struct IframeAttributionData {
IframeAttributionData();
IframeAttributionData(const IframeAttributionData&);
IframeAttributionData& operator=(const IframeAttributionData&);
~IframeAttributionData();
static IframeAttributionData Create(const base::Optional<std::string>& id,
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
// world types defined in blink, but with some simplifications.
enum class V8ContextWorldType {
// The main world, corresponding to a frame / document.
kMain,
// Corresponds to the main world of a worker or worklet.
kWorkerOrWorklet,
// Corresponds to an extension.
kExtension,
// Corresponds to a non-extension isolated world.
kIsolated,
// Corresponds to the devtools inspector. Will not have a human readable
// name or a stable id.
kInspector,
// Corresponds to the regexp world. This world is unique in that it is per
// v8::Isolate, and not associated with any individual execution context.
// Will not have a human-readable name or stable id.
kRegExp,
};
// Information describing a V8 Context. Explicitly allow copy and assign. This
// is used in IPC related to the performance.measureMemory API.
struct V8ContextDescription {
V8ContextDescription();
V8ContextDescription(const V8ContextDescription&);
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.
V8ContextWorldType world_type;
// Identifies this world. Only set for extension and isolated worlds. For
// extension worlds this corresponds to the stable extension ID. For other
// isolated worlds this is a human-readable description.
base::Optional<std::string> world_name;
// The identity of the execution context that this V8Context is associated
// with. This is specified for all world types, except kRegExp worlds.
base::Optional<blink::ExecutionContextToken> execution_context_token;
};
} // namespace v8_memory
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_TYPES_H_
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