Commit cc2829a1 authored by Joe Mason's avatar Joe Mason Committed by Chromium LUCI CQ

[PM] Move WebMemoryAggregator result state into a Visitor helper class

Moves the code that statefully updates the aggregation result into an
AggregationPointVisitor helper class. This will make it easier to
implement new algorithms that gather different information from each
aggregation point (eg. finding the set of processes spanned by all
aggregation points) by subclassing AggregationPointVisitor.

R=siggi

Bug: 1168573
Change-Id: I0b674fdc4282e164e9541ed17ca6b53483bb525e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2640675
Commit-Queue: Joe Mason <joenotcharles@chromium.org>
Reviewed-by: default avatarSigurður Ásgeirsson <siggi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845713}
parent 109dcdb8
......@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/check.h"
#include "base/containers/stack.h"
#include "base/stl_util.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/page_node.h"
......@@ -21,6 +22,51 @@ namespace performance_manager {
namespace v8_memory {
// A visitor that visits every node that can be aggregated into an aggregation
// point.
//
// TODO(joenotcharles): If we ever need to aggregate different data for each
// aggregation point, turn this into an interface and add a subclass for each
// type of data to aggregate.
class WebMemoryAggregator::AggregationPointVisitor {
public:
AggregationPointVisitor(const FrameNode* aggregation_start_node,
const url::Origin& requesting_origin);
~AggregationPointVisitor();
AggregationPointVisitor(const AggregationPointVisitor& other) = delete;
AggregationPointVisitor& operator=(const AggregationPointVisitor& other) =
delete;
mojom::WebMemoryMeasurementPtr TakeAggregationResult();
// Called on first visiting |frame_node| in a depth-first traversal.
// |aggregation_type| specificies how to treat the node in the aggregation.
void OnFrameEntered(const FrameNode* frame_node,
NodeAggregationType aggregation_type);
// Called after visiting |frame_node| and all its children in a depth-first
// traversal.
void OnFrameExited(const FrameNode* frame_node);
// Called on first visiting |worker_node| in a depth-first traversal.
// |aggregation_type| specificies how to treat the node in the aggregation.
void OnWorkerEntered(const WorkerNode* worker_node,
NodeAggregationType aggregation_type);
// Called after visiting |worker_node| and all its children in a depth-first
// traversal.
void OnWorkerExited(const WorkerNode* worker_node);
private:
const FrameNode* aggregation_start_node_;
const url::Origin requesting_origin_;
mojom::WebMemoryMeasurementPtr aggregation_result_ =
mojom::WebMemoryMeasurement::New();
base::stack<mojom::WebMemoryBreakdownEntry*> enclosing_aggregation_points_;
};
namespace {
using AttributionScope = mojom::WebMemoryAttribution::Scope;
......@@ -87,6 +133,19 @@ const mojom::WebMemoryAttribution* GetAttributionFromBreakdown(
return const_cast<mojom::WebMemoryAttribution*>(mutable_attribution);
}
AttributionScope AttributionScopeFromWorkerType(
WorkerNode::WorkerType worker_type) {
switch (worker_type) {
case WorkerNode::WorkerType::kDedicated:
return AttributionScope::kDedicatedWorker;
case WorkerNode::WorkerType::kShared:
case WorkerNode::WorkerType::kService:
// TODO(crbug.com/1169168): Support service and shared workers.
NOTREACHED();
return AttributionScope::kDedicatedWorker;
}
}
void AddMemoryBytes(mojom::WebMemoryBreakdownEntry* aggregation_point,
const V8DetailedMemoryExecutionContextData* data,
bool is_same_process) {
......@@ -105,19 +164,184 @@ void AddMemoryBytes(mojom::WebMemoryBreakdownEntry* aggregation_point,
} // anonymous namespace
////////////////////////////////////////////////////////////////////////////////
// AggregationPointVisitor
WebMemoryAggregator::AggregationPointVisitor::AggregationPointVisitor(
const FrameNode* aggregation_start_node,
const url::Origin& requesting_origin)
: aggregation_start_node_(aggregation_start_node),
requesting_origin_(requesting_origin) {}
WebMemoryAggregator::AggregationPointVisitor::~AggregationPointVisitor() =
default;
mojom::WebMemoryMeasurementPtr
WebMemoryAggregator::AggregationPointVisitor::TakeAggregationResult() {
DCHECK(aggregation_result_);
auto result = std::move(aggregation_result_);
aggregation_result_ = nullptr;
return result;
}
void WebMemoryAggregator::AggregationPointVisitor::OnFrameEntered(
const FrameNode* frame_node,
NodeAggregationType aggregation_type) {
DCHECK(frame_node);
DCHECK_EQ(enclosing_aggregation_points_.empty(),
frame_node == aggregation_start_node_);
mojom::WebMemoryBreakdownEntry* aggregation_point = nullptr;
switch (aggregation_type) {
case NodeAggregationType::kInvisible:
NOTREACHED();
return;
case NodeAggregationType::kSameOriginAggregationPoint:
// Create a new aggregation point with window scope. Since this node is
// same-origin to the start node, the start node can view its current
// url.
aggregation_point = CreateBreakdownEntry(AttributionScope::kWindow,
frame_node->GetURL().spec(),
aggregation_result_.get());
if (frame_node->IsMainFrame() || frame_node == aggregation_start_node_) {
// There should be no id or src attribute since there is no visible
// parent to take them from. Do nothing.
} else if (GetSameOriginParentOrOpener(frame_node, requesting_origin_)) {
// The parent or opener is also same-origin so the start node can view
// its attributes. Add the id and src recorded for the node in
// V8ContextTracker to the new breakdown entry.
SetBreakdownAttributionFromFrame(frame_node, aggregation_point);
} else {
// Some grandparent node is the most recent aggregation point whose
// attributes are visible to the start node, and
// |enclosing_aggregation_point| includes those attributes. Copy the
// id and src attributes from there.
CopyBreakdownAttribution(enclosing_aggregation_points_.top(),
aggregation_point);
}
break;
case NodeAggregationType::kCrossOriginAggregationPoint:
// Create a new aggregation point with cross-origin-aggregated scope.
// Since this node is NOT same-origin to the start node, the start node
// CANNOT view its current url.
aggregation_point =
CreateBreakdownEntry(AttributionScope::kCrossOriginAggregated,
base::nullopt, aggregation_result_.get());
// This is cross-origin but not being aggregated into another
// aggregation point, so its parent or opener must be same-origin to the
// start node, which can therefore view its attributes. Add the id and
// src recorded for the node in V8ContextTracker to the new breakdown
// entry.
SetBreakdownAttributionFromFrame(frame_node, aggregation_point);
break;
case NodeAggregationType::kCrossOriginAggregated:
// Update the enclosing aggregation point in-place.
aggregation_point = enclosing_aggregation_points_.top();
break;
}
// Now update the memory used in the chosen aggregation point.
DCHECK(aggregation_point);
AddMemoryBytes(aggregation_point,
V8DetailedMemoryExecutionContextData::ForFrameNode(frame_node),
frame_node->GetProcessNode() ==
aggregation_start_node_->GetProcessNode());
enclosing_aggregation_points_.push(aggregation_point);
}
void WebMemoryAggregator::AggregationPointVisitor::OnFrameExited(
const FrameNode* frame_node) {
DCHECK(!enclosing_aggregation_points_.empty());
enclosing_aggregation_points_.pop();
}
void WebMemoryAggregator::AggregationPointVisitor::OnWorkerEntered(
const WorkerNode* worker_node,
NodeAggregationType aggregation_type) {
DCHECK(worker_node);
// Aggregation starts from a frame node, so the enclosing aggregation point
// is guaranteed to exist.
DCHECK(!enclosing_aggregation_points_.empty());
mojom::WebMemoryBreakdownEntry* aggregation_point = nullptr;
switch (aggregation_type) {
case NodeAggregationType::kSameOriginAggregationPoint:
// Create a new aggregation point with window scope. Since this node is
// same-origin to the start node, the start node can view its current
// url.
aggregation_point = CreateBreakdownEntry(
AttributionScopeFromWorkerType(worker_node->GetWorkerType()),
worker_node->GetURL().spec(), aggregation_result_.get());
CopyBreakdownAttribution(enclosing_aggregation_points_.top(),
aggregation_point);
break;
case NodeAggregationType::kCrossOriginAggregated:
// Update the enclosing aggregation point in-place.
aggregation_point = enclosing_aggregation_points_.top();
break;
case NodeAggregationType::kInvisible:
case NodeAggregationType::kCrossOriginAggregationPoint:
NOTREACHED();
return;
}
// Now update the memory used in the chosen aggregation point.
DCHECK(aggregation_point);
AddMemoryBytes(
aggregation_point,
V8DetailedMemoryExecutionContextData::ForWorkerNode(worker_node),
worker_node->GetProcessNode() ==
aggregation_start_node_->GetProcessNode());
enclosing_aggregation_points_.push(aggregation_point);
}
void WebMemoryAggregator::AggregationPointVisitor::OnWorkerExited(
const WorkerNode* worker_node) {
DCHECK(!enclosing_aggregation_points_.empty());
enclosing_aggregation_points_.pop();
}
////////////////////////////////////////////////////////////////////////////////
// WebMemoryAggregator
WebMemoryAggregator::WebMemoryAggregator(const FrameNode* requesting_node)
: requesting_origin_(GetOrigin(requesting_node)),
aggregation_start_node_(
internal::FindAggregationStartNode(requesting_node)) {
aggregation_start_node_(FindAggregationStartNode(requesting_node)) {
DCHECK(aggregation_start_node_);
}
WebMemoryAggregator::~WebMemoryAggregator() = default;
mojom::WebMemoryMeasurementPtr
WebMemoryAggregator::AggregateMeasureMemoryResult() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AggregationPointVisitor ap_visitor(aggregation_start_node_,
requesting_origin_);
VisitFrame(&ap_visitor, aggregation_start_node_);
mojom::WebMemoryMeasurementPtr aggregation_result =
ap_visitor.TakeAggregationResult();
auto* process_data = V8DetailedMemoryProcessData::ForProcessNode(
aggregation_start_node_->GetProcessNode());
if (process_data) {
aggregation_result->detached_memory = mojom::WebMemoryUsage::New();
aggregation_result->detached_memory->bytes =
process_data->detached_v8_bytes_used();
aggregation_result->shared_memory = mojom::WebMemoryUsage::New();
aggregation_result->shared_memory->bytes =
process_data->shared_v8_bytes_used();
}
return aggregation_result;
}
WebMemoryAggregator::NodeAggregationType
WebMemoryAggregator::FindNodeAggregationType(const FrameNode* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -174,15 +398,15 @@ WebMemoryAggregator::NodeAggregationType
WebMemoryAggregator::FindNodeAggregationType(const WorkerNode* worker_node,
NodeAggregationType parent_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(1085129): Support service and shared workers.
// TODO(crbug.com/1169168): Support service and shared workers.
DCHECK_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kDedicated);
// A dedicated worker is guaranteed to have the same origin as its parent,
// which means that a dedicated worker cannot be a cross-origin aggregation
// point.
#if DCHECK_IS_ON()
// TODO(1085129): The URL of a worker node is currently not available without
// PlzDedicatedWorker, which is disabled by default. Remove this guard once
// the URL is properly propagated to PM.
// TODO(crbug.com/1169178): The URL of a worker node is currently not
// available without PlzDedicatedWorker, which is disabled by default. Remove
// this guard once the URL is properly propagated to PM.
if (!worker_node->GetURL().is_empty()) {
auto worker_origin = GetOrigin(worker_node);
auto client_frames = worker_node->GetClientFrames();
......@@ -212,32 +436,9 @@ WebMemoryAggregator::FindNodeAggregationType(const WorkerNode* worker_node,
}
}
mojom::WebMemoryMeasurementPtr
WebMemoryAggregator::AggregateMeasureMemoryResult() {
bool WebMemoryAggregator::VisitFrame(AggregationPointVisitor* ap_visitor,
const FrameNode* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
aggregation_result_ = mojom::WebMemoryMeasurement::New();
VisitFrame(nullptr, aggregation_start_node_);
auto* process_data = V8DetailedMemoryProcessData::ForProcessNode(
aggregation_start_node_->GetProcessNode());
if (process_data) {
aggregation_result_->detached_memory = mojom::WebMemoryUsage::New();
aggregation_result_->detached_memory->bytes =
process_data->detached_v8_bytes_used();
aggregation_result_->shared_memory = mojom::WebMemoryUsage::New();
aggregation_result_->shared_memory->bytes =
process_data->shared_v8_bytes_used();
}
return std::move(aggregation_result_);
}
bool WebMemoryAggregator::VisitFrame(
mojom::WebMemoryBreakdownEntry* enclosing_aggregation_point,
const FrameNode* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(aggregation_result_);
DCHECK(enclosing_aggregation_point || frame_node == aggregation_start_node_);
DCHECK(frame_node);
// An aggregation point is a node in the graph that holds a memory breakdown
......@@ -245,170 +446,72 @@ bool WebMemoryAggregator::VisitFrame(
// breakdown. It is represented directly by the WebMemoryBreakdownEntry
// object the describes the breakdown since there is no extra information to
// store about the aggregation point.
mojom::WebMemoryBreakdownEntry* aggregation_point = nullptr;
NodeAggregationType aggregation_type = FindNodeAggregationType(frame_node);
switch (aggregation_type) {
case NodeAggregationType::kInvisible:
// Ignore this node, continue iterating its siblings.
return true;
case NodeAggregationType::kSameOriginAggregationPoint:
// Create a new aggregation point with window scope. Since this node is
// same-origin to the start node, the start node can view its current
// url.
aggregation_point = internal::CreateBreakdownEntry(
AttributionScope::kWindow, frame_node->GetURL().spec(),
aggregation_result_.get());
if (frame_node->IsMainFrame() || frame_node == aggregation_start_node_) {
// There should be no id or src attribute since there is no visible
// parent to take them from. Do nothing.
} else if (internal::GetSameOriginParentOrOpener(frame_node,
requesting_origin_)) {
// The parent or opener is also same-origin so the start node can view
// its attributes. Add the id and src recorded for the node in
// V8ContextTracker to the new breakdown entry.
internal::SetBreakdownAttributionFromFrame(frame_node,
aggregation_point);
} else {
// Some grandparent node is the most recent aggregation point whose
// attributes are visible to the start node, and
// |enclosing_aggregation_point| includes those attributes. Copy the id
// and src attributes from there.
internal::CopyBreakdownAttribution(enclosing_aggregation_point,
aggregation_point);
}
break;
case NodeAggregationType::kCrossOriginAggregationPoint:
// Create a new aggregation point with cross-origin-aggregated scope.
// Since this node is NOT same-origin to the start node, the start node
// CANNOT view its current url.
aggregation_point = internal::CreateBreakdownEntry(
AttributionScope::kCrossOriginAggregated, base::nullopt,
aggregation_result_.get());
// This is cross-origin but not being aggregated into another aggregation
// point, so its parent or opener must be same-origin to the start node,
// which can therefore view its attributes. Add the id and src recorded
// for the node in V8ContextTracker to the new breakdown entry.
internal::SetBreakdownAttributionFromFrame(frame_node, aggregation_point);
break;
case NodeAggregationType::kCrossOriginAggregated:
// Update the enclosing aggregation point in-place.
aggregation_point = enclosing_aggregation_point;
break;
auto aggregation_type = FindNodeAggregationType(frame_node);
if (aggregation_type == NodeAggregationType::kInvisible) {
// Ignore this node, continue iterating its siblings.
return true;
}
// Now update the memory used in the chosen aggregation point.
DCHECK(aggregation_point);
AddMemoryBytes(aggregation_point,
V8DetailedMemoryExecutionContextData::ForFrameNode(frame_node),
frame_node->GetProcessNode() ==
aggregation_start_node_->GetProcessNode());
ap_visitor->OnFrameEntered(frame_node, aggregation_type);
// Recurse into children and opened pages. This node's aggregation point
// becomes the enclosing aggregation point for those nodes. Unretained is safe
// because the Visit* functions are synchronous.
// Recurse into children and opened pages. Unretained is safe because the
// Visit* functions are synchronous.
frame_node->VisitOpenedPageNodes(
base::BindRepeating(&WebMemoryAggregator::VisitOpenedPage,
base::Unretained(this), aggregation_point));
base::Unretained(this), ap_visitor));
frame_node->VisitChildDedicatedWorkers(base::BindRepeating(
&WebMemoryAggregator::VisitWorker, base::Unretained(this),
aggregation_point, aggregation_type));
return frame_node->VisitChildFrameNodes(
base::BindRepeating(&WebMemoryAggregator::VisitFrame,
base::Unretained(this), aggregation_point));
}
&WebMemoryAggregator::VisitWorker, base::Unretained(this), ap_visitor,
aggregation_type));
frame_node->VisitChildFrameNodes(base::BindRepeating(
&WebMemoryAggregator::VisitFrame, base::Unretained(this), ap_visitor));
namespace {
ap_visitor->OnFrameExited(frame_node);
AttributionScope AttributionScopeFromWorkerType(
WorkerNode::WorkerType worker_type) {
switch (worker_type) {
case WorkerNode::WorkerType::kDedicated:
return AttributionScope::kDedicatedWorker;
case WorkerNode::WorkerType::kShared:
case WorkerNode::WorkerType::kService:
// TODO(1085129): Support service and shared workers.
NOTREACHED();
return AttributionScope::kDedicatedWorker;
}
return true;
}
} // anonymous namespace
bool WebMemoryAggregator::VisitWorker(
mojom::WebMemoryBreakdownEntry* enclosing_aggregation_point,
AggregationPointVisitor* ap_visitor,
NodeAggregationType parent_aggregation_type,
const WorkerNode* worker_node) {
// TODO(1085129): Support service and shared workers.
// TODO(crbug.com/1169168): Support service and shared workers.
DCHECK_EQ(worker_node->GetWorkerType(), WorkerNode::WorkerType::kDedicated);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(aggregation_result_);
// Aggregation starts from a frame node, so the enclosing aggregation point
// is guaranteed to exist.
DCHECK(enclosing_aggregation_point);
DCHECK(worker_node);
mojom::WebMemoryBreakdownEntry* aggregation_point = nullptr;
NodeAggregationType aggregation_type =
FindNodeAggregationType(worker_node, parent_aggregation_type);
switch (aggregation_type) {
case NodeAggregationType::kSameOriginAggregationPoint:
// Create a new aggregation point with window scope. Since this node is
// same-origin to the start node, the start node can view its current
// url.
aggregation_point = internal::CreateBreakdownEntry(
AttributionScopeFromWorkerType(worker_node->GetWorkerType()),
worker_node->GetURL().spec(), aggregation_result_.get());
internal::CopyBreakdownAttribution(enclosing_aggregation_point,
aggregation_point);
break;
case NodeAggregationType::kCrossOriginAggregated:
// Update the enclosing aggregation point in-place.
aggregation_point = enclosing_aggregation_point;
break;
case NodeAggregationType::kInvisible:
case NodeAggregationType::kCrossOriginAggregationPoint:
NOTREACHED();
return true;
if (aggregation_type == NodeAggregationType::kInvisible) {
// Ignore this node, continue iterating its siblings.
return true;
}
// Now update the memory used in the chosen aggregation point.
DCHECK(aggregation_point);
AddMemoryBytes(
aggregation_point,
V8DetailedMemoryExecutionContextData::ForWorkerNode(worker_node),
worker_node->GetProcessNode() ==
aggregation_start_node_->GetProcessNode());
ap_visitor->OnWorkerEntered(worker_node, aggregation_type);
worker_node->VisitChildDedicatedWorkers(base::BindRepeating(
&WebMemoryAggregator::VisitWorker, base::Unretained(this), ap_visitor,
aggregation_type));
ap_visitor->OnWorkerExited(worker_node);
return worker_node->VisitChildDedicatedWorkers(base::BindRepeating(
&WebMemoryAggregator::VisitWorker, base::Unretained(this),
aggregation_point, aggregation_type));
return true;
}
bool WebMemoryAggregator::VisitOpenedPage(
mojom::WebMemoryBreakdownEntry* enclosing_aggregation_point,
const PageNode* page_node) {
bool WebMemoryAggregator::VisitOpenedPage(AggregationPointVisitor* ap_visitor,
const PageNode* page_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ShouldFollowOpenerLink(page_node)) {
// Visit only the "current" main frame instead of all of the main frames
// (non-current ones are either about to die, or represent an ongoing
// navigation).
return VisitFrame(enclosing_aggregation_point,
page_node->GetMainFrameNode());
return VisitFrame(ap_visitor, page_node->GetMainFrameNode());
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Free functions
namespace internal {
const FrameNode* GetSameOriginParentOrOpener(const FrameNode* frame_node,
const url::Origin& origin) {
// static
const FrameNode* WebMemoryAggregator::GetSameOriginParentOrOpener(
const FrameNode* frame_node,
const url::Origin& origin) {
if (auto* parent_or_opener = GetParentOrOpener(frame_node)) {
if (origin.IsSameOriginWith(GetOrigin(parent_or_opener)))
return parent_or_opener;
......@@ -416,7 +519,9 @@ const FrameNode* GetSameOriginParentOrOpener(const FrameNode* frame_node,
return nullptr;
}
const FrameNode* FindAggregationStartNode(const FrameNode* requesting_node) {
// static
const FrameNode* WebMemoryAggregator::FindAggregationStartNode(
const FrameNode* requesting_node) {
DCHECK(requesting_node);
auto requesting_origin = GetOrigin(requesting_node);
DCHECK(!requesting_origin.opaque());
......@@ -444,7 +549,8 @@ const FrameNode* FindAggregationStartNode(const FrameNode* requesting_node) {
return start_node;
}
mojom::WebMemoryBreakdownEntry* CreateBreakdownEntry(
// static
mojom::WebMemoryBreakdownEntry* WebMemoryAggregator::CreateBreakdownEntry(
AttributionScope scope,
base::Optional<std::string> url,
mojom::WebMemoryMeasurement* measurement) {
......@@ -457,7 +563,8 @@ mojom::WebMemoryBreakdownEntry* CreateBreakdownEntry(
return measurement->breakdown.back().get();
}
void SetBreakdownAttributionFromFrame(
// static
void WebMemoryAggregator::SetBreakdownAttributionFromFrame(
const FrameNode* frame_node,
mojom::WebMemoryBreakdownEntry* breakdown) {
DCHECK(breakdown);
......@@ -478,8 +585,10 @@ void SetBreakdownAttributionFromFrame(
attribution->src = ec_attribution->src;
}
void CopyBreakdownAttribution(const mojom::WebMemoryBreakdownEntry* from,
mojom::WebMemoryBreakdownEntry* to) {
// static
void WebMemoryAggregator::CopyBreakdownAttribution(
const mojom::WebMemoryBreakdownEntry* from,
mojom::WebMemoryBreakdownEntry* to) {
DCHECK(from);
DCHECK(to);
const auto* from_attribution = GetAttributionFromBreakdown(from);
......@@ -488,8 +597,6 @@ void CopyBreakdownAttribution(const mojom::WebMemoryBreakdownEntry* from,
to_attribution->src = from_attribution->src;
}
} // namespace internal
} // namespace v8_memory
} // namespace performance_manager
......@@ -41,10 +41,22 @@ class WebMemoryAggregator {
WebMemoryAggregator(const WebMemoryAggregator& other) = delete;
WebMemoryAggregator& operator=(const WebMemoryAggregator& other) = delete;
// Returns the origin of |requesting_node|.
const url::Origin& requesting_origin() const { return requesting_origin_; }
// Performs the aggregation.
mojom::WebMemoryMeasurementPtr AggregateMeasureMemoryResult();
private:
friend class WebMemoryAggregatorTest;
class AggregationPointVisitor;
// The various ways a node can be treated during the aggregation.
enum class NodeAggregationType {
// Node is same-origin to |requesting_node|; will be a new aggregation
// point with scope "Window".
// point with a scope depending on the node type (eg. "Window" or
// "DedicatedWorker").
kSameOriginAggregationPoint,
// Node is cross-origin with |requesting_node| but its parent is not; will
// be a new aggregation point with scope
......@@ -58,9 +70,6 @@ class WebMemoryAggregator {
kInvisible,
};
// Returns the origin of |requesting_node|.
const url::Origin& requesting_origin() const { return requesting_origin_; }
// Returns the way that |frame_node| should be treated during the
// aggregation. |aggregation_start_node_| must be reachable from
// |frame_node| by following parent/child or opener links. This will always
......@@ -72,33 +81,58 @@ class WebMemoryAggregator {
const WorkerNode* worker_node,
NodeAggregationType parent_aggregation_type);
// Performs the aggregation.
mojom::WebMemoryMeasurementPtr AggregateMeasureMemoryResult();
private:
// FrameNodeVisitor that recursively adds |frame_node| and its children to
// the aggregation. |enclosing_aggregation_point| is the aggregation point
// that |frame_node|'s parent or opener is in. Always returns true to
// continue traversal.
bool VisitFrame(mojom::WebMemoryBreakdownEntry* enclosing_aggregation_point,
// the aggregation using |ap_visitor|. Always returns true to continue
// traversal.
bool VisitFrame(AggregationPointVisitor* ap_visitor,
const FrameNode* frame_node);
// WorkerNodeVisitor that recursively adds |worker_node| and its children to
// the aggregation. |enclosing_aggregation_point| is the aggregation point
// that |worker_node|'s parent is in. Similarly |enclosing_aggregation_type|
// is the aggregation type of the parent.
// Always returns true to continue traversal.
bool VisitWorker(mojom::WebMemoryBreakdownEntry* enclosing_aggregation_point,
// the aggregation using |ap_visitor|. |enclosing_aggregation_type| is the
// type of the aggregation point that |worker_node|'s parent is in. Always
// returns true to continue traversal.
bool VisitWorker(AggregationPointVisitor* ap_visitor,
NodeAggregationType enclosing_aggregation_type,
const WorkerNode* worker_node);
// PageNodeVisitor that recursively adds |page_node|'s main frames and their
// children to the aggregation. |enclosing_aggregation_point| is the
// aggregation point that |page_node|'s opener is in. Always returns true to
// children to the aggregation using |ap_visitor|. Always returns true to
// continue traversal.
bool VisitOpenedPage(
mojom::WebMemoryBreakdownEntry* enclosing_aggregation_point,
const PageNode* page_node);
bool VisitOpenedPage(AggregationPointVisitor* ap_visitor,
const PageNode* page_node);
// Static private methods are implementation details, but can be accessed from
// friend classes for testing.
// Returns |frame_node|'s parent or opener if the parent or opener is
// same-origin with |origin|, nullptr otherwise.
static const FrameNode* GetSameOriginParentOrOpener(
const FrameNode* frame_node,
const url::Origin& origin);
// Walks back the chain of parents and openers from |requesting_node| to find
// the farthest ancestor that should be visible to it (all intermediate nodes
// in the chain are same-origin).
static const FrameNode* FindAggregationStartNode(
const FrameNode* requesting_node);
// Creates a new breakdown entry with the given |scope| and |url|, and adds it
// to the list in |measurement|. Returns a pointer to the newly created entry.
static mojom::WebMemoryBreakdownEntry* CreateBreakdownEntry(
mojom::WebMemoryAttribution::Scope scope,
base::Optional<std::string> url,
mojom::WebMemoryMeasurement* measurement);
// Sets the id and src attributes of |breakdown| using those stored in the
// V8ContextTracker for the given |frame_node|.
static void SetBreakdownAttributionFromFrame(
const FrameNode* frame_node,
mojom::WebMemoryBreakdownEntry* breakdown);
// Copies the id and src attributes from |from| to |to|.
static void CopyBreakdownAttribution(
const mojom::WebMemoryBreakdownEntry* from,
mojom::WebMemoryBreakdownEntry* to);
// The origin of |requesting_node|. Cached so it doesn't have to be
// recalculated in each call to VisitFrame.
......@@ -108,48 +142,9 @@ class WebMemoryAggregator {
// |requesting_node| using FindAggregationStartNode.
const FrameNode* aggregation_start_node_;
// Stores the result of the aggregation. This is populated by
// AggregateMeasureMemoryResult.
mojom::WebMemoryMeasurementPtr aggregation_result_
GUARDED_BY_CONTEXT(sequence_checker_);
SEQUENCE_CHECKER(sequence_checker_);
};
namespace internal {
// These functions are used in the implementation and exposed in the header for
// testing.
// Returns |frame_node|'s parent or opener if the parent or opener is
// same-origin with |origin|, nullptr otherwise.
const FrameNode* GetSameOriginParentOrOpener(const FrameNode* frame_node,
const url::Origin& origin);
// Walks back the chain of parents and openers from |requesting_node| to find
// the farthest ancestor that should be visible to it (all intermediate nodes
// in the chain are same-origin).
const FrameNode* FindAggregationStartNode(const FrameNode* requesting_node);
// Creates a new breakdown entry with the given |scope| and |url|, and adds it
// to the list in |measurement|. Returns a pointer to the newly created entry.
mojom::WebMemoryBreakdownEntry* CreateBreakdownEntry(
mojom::WebMemoryAttribution::Scope scope,
base::Optional<std::string> url,
mojom::WebMemoryMeasurement* measurement);
// Sets the id and src attributes of |breakdown| using those stored in the
// V8ContextTracker for the given |frame_node|.
void SetBreakdownAttributionFromFrame(
const FrameNode* frame_node,
mojom::WebMemoryBreakdownEntry* breakdown);
// Copies the id and src attributes from |from| to |to|.
void CopyBreakdownAttribution(const mojom::WebMemoryBreakdownEntry* from,
mojom::WebMemoryBreakdownEntry* to);
} // namespace internal
} // namespace v8_memory
} // namespace performance_manager
......
......@@ -25,9 +25,6 @@ namespace v8_memory {
namespace {
using AttributionScope = mojom::WebMemoryAttribution::Scope;
using NodeAggregationType = WebMemoryAggregator::NodeAggregationType;
using WebMemoryAggregatorTest = WebMemoryTestHarness;
struct ExpectedMemoryBreakdown {
WebMemoryTestHarness::Bytes bytes;
......@@ -97,15 +94,70 @@ std::string MeasurementToJSON(
return json_value.ToJSON();
}
} // namespace
class WebMemoryAggregatorTest : public WebMemoryTestHarness {
protected:
using NodeAggregationType = WebMemoryAggregator::NodeAggregationType;
// Allow individual test subclasses to access private members of
// WebMemoryAggregator.
static NodeAggregationType FindNodeAggregationType(
WebMemoryAggregator* aggregator,
const FrameNode* frame_node) {
return aggregator->FindNodeAggregationType(frame_node);
}
static NodeAggregationType FindNodeAggregationType(
WebMemoryAggregator* aggregator,
const WorkerNode* worker_node,
NodeAggregationType parent_aggregation_type) {
return aggregator->FindNodeAggregationType(worker_node,
parent_aggregation_type);
}
static const FrameNode* GetSameOriginParentOrOpener(
const FrameNode* frame_node,
const url::Origin& origin) {
return WebMemoryAggregator::GetSameOriginParentOrOpener(frame_node, origin);
}
static const FrameNode* FindAggregationStartNode(
const FrameNode* requesting_node) {
return WebMemoryAggregator::FindAggregationStartNode(requesting_node);
}
static mojom::WebMemoryBreakdownEntry* CreateBreakdownEntry(
mojom::WebMemoryAttribution::Scope scope,
base::Optional<std::string> url,
mojom::WebMemoryMeasurement* measurement) {
return WebMemoryAggregator::CreateBreakdownEntry(scope, url, measurement);
}
static void SetBreakdownAttributionFromFrame(
const FrameNode* frame_node,
mojom::WebMemoryBreakdownEntry* breakdown) {
WebMemoryAggregator::SetBreakdownAttributionFromFrame(frame_node,
breakdown);
}
static void CopyBreakdownAttribution(
const mojom::WebMemoryBreakdownEntry* from,
mojom::WebMemoryBreakdownEntry* to) {
WebMemoryAggregator::CopyBreakdownAttribution(from, to);
}
};
TEST_F(WebMemoryAggregatorTest, CreateBreakdownEntry) {
auto measurement = mojom::WebMemoryMeasurement::New();
auto* breakdown_with_no_url =
internal::CreateBreakdownEntry(AttributionScope::kCrossOriginAggregated,
base::nullopt, measurement.get());
auto* breakdown_with_url = internal::CreateBreakdownEntry(
CreateBreakdownEntry(AttributionScope::kCrossOriginAggregated,
base::nullopt, measurement.get());
auto* breakdown_with_url = CreateBreakdownEntry(
AttributionScope::kWindow, "https://example.com", measurement.get());
auto* breakdown_with_empty_url = internal::CreateBreakdownEntry(
AttributionScope::kWindow, "", measurement.get());
auto* breakdown_with_empty_url =
CreateBreakdownEntry(AttributionScope::kWindow, "", measurement.get());
// Ensure breakdowns were added to measurement.
EXPECT_EQ(measurement->breakdown.size(), 3U);
......@@ -128,9 +180,8 @@ TEST_F(WebMemoryAggregatorTest, CreateBreakdownEntry) {
: nullptr;
FrameNodeImpl* frame = AddFrameNode("https://example.com", Bytes{1},
parent_frame, attribute, attribute);
internal::SetBreakdownAttributionFromFrame(frame, breakdown_with_url);
internal::CopyBreakdownAttribution(breakdown_with_url,
breakdown_with_empty_url);
SetBreakdownAttributionFromFrame(frame, breakdown_with_url);
CopyBreakdownAttribution(breakdown_with_url, breakdown_with_empty_url);
// All measurements should be created without measurement results.
auto expected_result = CreateExpectedMemoryMeasurement({
......@@ -159,7 +210,7 @@ TEST_F(WebMemoryAggregatorTest, AggregateSingleFrame) {
ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
"https://example.com/"),
});
EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
WebMemoryAggregator aggregator(main_frame);
auto result = aggregator.AggregateMeasureMemoryResult();
EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
......@@ -172,17 +223,17 @@ TEST_F(WebMemoryAggregatorTest, AggregateSingleSiteMultiFrame) {
AddFrameNode("https://example.com/iframe.html", Bytes{5}, main_frame,
"example-id", "redirect.html?target=iframe.html");
EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
WebMemoryAggregator aggregator(main_frame);
// Test the relationships of each node in the graph.
EXPECT_EQ(aggregator.FindNodeAggregationType(main_frame),
EXPECT_EQ(FindNodeAggregationType(&aggregator, main_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(aggregator.FindNodeAggregationType(child_frame),
EXPECT_EQ(FindNodeAggregationType(&aggregator, child_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
child_frame, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(
GetSameOriginParentOrOpener(child_frame, aggregator.requesting_origin()),
main_frame);
auto expected_result = CreateExpectedMemoryMeasurement({
ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
......@@ -222,32 +273,32 @@ TEST_F(WebMemoryAggregatorTest, AggregateCrossOrigin) {
FrameNodeImpl* grandchild3 =
AddFrameNode("https://foo.com/worker.js", Bytes{4}, child_frame);
EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
WebMemoryAggregator aggregator(main_frame);
// Test the relationships of each node in the graph.
EXPECT_EQ(aggregator.FindNodeAggregationType(main_frame),
EXPECT_EQ(FindNodeAggregationType(&aggregator, main_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(aggregator.FindNodeAggregationType(child_frame),
EXPECT_EQ(FindNodeAggregationType(&aggregator, child_frame),
NodeAggregationType::kCrossOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
child_frame, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(aggregator.FindNodeAggregationType(grandchild1),
EXPECT_EQ(
GetSameOriginParentOrOpener(child_frame, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(FindNodeAggregationType(&aggregator, grandchild1),
NodeAggregationType::kCrossOriginAggregated);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
grandchild1, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(aggregator.FindNodeAggregationType(grandchild2),
EXPECT_EQ(
GetSameOriginParentOrOpener(grandchild1, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(FindNodeAggregationType(&aggregator, grandchild2),
NodeAggregationType::kCrossOriginAggregated);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
grandchild2, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(aggregator.FindNodeAggregationType(grandchild3),
EXPECT_EQ(
GetSameOriginParentOrOpener(grandchild2, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(FindNodeAggregationType(&aggregator, grandchild3),
NodeAggregationType::kCrossOriginAggregated);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
grandchild3, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(
GetSameOriginParentOrOpener(grandchild3, aggregator.requesting_origin()),
nullptr);
auto expected_result = CreateExpectedMemoryMeasurement({
ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
......@@ -315,56 +366,56 @@ TEST_F(WebMemoryAggregatorTest, AggregateNestedCrossOrigin) {
FrameNodeImpl* empty_frame =
AddFrameNode("https://example.com/empty_frame", base::nullopt, subframe3);
EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
WebMemoryAggregator aggregator(main_frame);
// Test the relationships of each node in the graph.
EXPECT_EQ(aggregator.FindNodeAggregationType(main_frame),
EXPECT_EQ(FindNodeAggregationType(&aggregator, main_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(aggregator.FindNodeAggregationType(subframe),
EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe),
NodeAggregationType::kCrossOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
subframe, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(aggregator.FindNodeAggregationType(subframe2),
EXPECT_EQ(
GetSameOriginParentOrOpener(subframe, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe2),
NodeAggregationType::kCrossOriginAggregated);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
subframe2, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(aggregator.FindNodeAggregationType(subframe3),
EXPECT_EQ(
GetSameOriginParentOrOpener(subframe2, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe3),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
subframe3, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(aggregator.FindNodeAggregationType(subframe4),
EXPECT_EQ(
GetSameOriginParentOrOpener(subframe3, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe4),
NodeAggregationType::kCrossOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
subframe4, aggregator.requesting_origin()),
subframe3);
EXPECT_EQ(aggregator.FindNodeAggregationType(subframe5),
EXPECT_EQ(
GetSameOriginParentOrOpener(subframe4, aggregator.requesting_origin()),
subframe3);
EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe5),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
subframe5, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(aggregator.FindNodeAggregationType(subframe6),
EXPECT_EQ(
GetSameOriginParentOrOpener(subframe5, aggregator.requesting_origin()),
nullptr);
EXPECT_EQ(FindNodeAggregationType(&aggregator, subframe6),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
subframe6, aggregator.requesting_origin()),
subframe3);
EXPECT_EQ(aggregator.FindNodeAggregationType(empty_frame),
EXPECT_EQ(
GetSameOriginParentOrOpener(subframe6, aggregator.requesting_origin()),
subframe3);
EXPECT_EQ(FindNodeAggregationType(&aggregator, empty_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
empty_frame, aggregator.requesting_origin()),
subframe3);
EXPECT_EQ(aggregator.FindNodeAggregationType(cross_process_frame),
EXPECT_EQ(
GetSameOriginParentOrOpener(empty_frame, aggregator.requesting_origin()),
subframe3);
EXPECT_EQ(FindNodeAggregationType(&aggregator, cross_process_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
cross_process_frame, aggregator.requesting_origin()),
EXPECT_EQ(GetSameOriginParentOrOpener(cross_process_frame,
aggregator.requesting_origin()),
subframe3);
EXPECT_EQ(aggregator.FindNodeAggregationType(cross_process_frame2),
EXPECT_EQ(FindNodeAggregationType(&aggregator, cross_process_frame2),
NodeAggregationType::kCrossOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
cross_process_frame2, aggregator.requesting_origin()),
EXPECT_EQ(GetSameOriginParentOrOpener(cross_process_frame2,
aggregator.requesting_origin()),
subframe3);
auto expected_result = CreateExpectedMemoryMeasurement({
......@@ -406,7 +457,7 @@ TEST_F(WebMemoryAggregatorTest, AggregateSameOriginAboutBlank) {
"https://example.com/"),
ExpectedMemoryBreakdown(20, AttributionScope::kWindow, "about:blank"),
});
EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
WebMemoryAggregator aggregator(main_frame);
auto result = aggregator.AggregateMeasureMemoryResult();
EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
......@@ -424,7 +475,7 @@ TEST_F(WebMemoryAggregatorTest, SkipCrossOriginAboutBlank) {
ExpectedMemoryBreakdown(50, AttributionScope::kCrossOriginAggregated,
base::nullopt),
});
EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
WebMemoryAggregator aggregator(main_frame);
auto result = aggregator.AggregateMeasureMemoryResult();
EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
......@@ -441,10 +492,8 @@ TEST_F(WebMemoryAggregatorTest, FindAggregationStartNode) {
// FindAggregationStartNode should return the parent foo.com frame for either
// foo.com child. It should not return the main frame since it's cross-site
// from the requesting frames.
EXPECT_EQ(internal::FindAggregationStartNode(cross_site_child),
cross_site_child);
EXPECT_EQ(internal::FindAggregationStartNode(same_site_child),
cross_site_child);
EXPECT_EQ(FindAggregationStartNode(cross_site_child), cross_site_child);
EXPECT_EQ(FindAggregationStartNode(same_site_child), cross_site_child);
// When aggregation starts at |cross_site_child| it should not include any
// memory from the main frame.
......@@ -461,7 +510,7 @@ TEST_F(WebMemoryAggregatorTest, FindAggregationStartNode) {
// When the main frame requests a measurement of the same tree it should
// aggregate the children, which are cross-site from it.
EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
EXPECT_EQ(FindAggregationStartNode(main_frame), main_frame);
auto main_frame_expected_result = CreateExpectedMemoryMeasurement({
ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
"https://example.com/"),
......@@ -482,19 +531,18 @@ TEST_F(WebMemoryAggregatorTest, FindCrossProcessAggregationStartNode) {
"https://example.com/same_process.html", Bytes{3}, cross_process_child);
auto origin = url::Origin::Create(GURL("https://example.com"));
ASSERT_EQ(internal::GetSameOriginParentOrOpener(cross_process_child, origin),
ASSERT_EQ(GetSameOriginParentOrOpener(cross_process_child, origin),
main_frame);
ASSERT_EQ(internal::GetSameOriginParentOrOpener(same_process_child, origin),
ASSERT_EQ(GetSameOriginParentOrOpener(same_process_child, origin),
cross_process_child);
// |cross_process_child| has no ancestor in the same process as it.
EXPECT_EQ(internal::FindAggregationStartNode(cross_process_child),
cross_process_child);
EXPECT_EQ(FindAggregationStartNode(cross_process_child), cross_process_child);
// The search starting from |same_process_child| should skip over
// |cross_process_child|, which is in a different process, and find
// |main_frame| which is in the same process.
EXPECT_EQ(internal::FindAggregationStartNode(same_process_child), main_frame);
EXPECT_EQ(FindAggregationStartNode(same_process_child), main_frame);
}
TEST_F(WebMemoryAggregatorTest, AggregateWindowOpener) {
......@@ -519,39 +567,38 @@ TEST_F(WebMemoryAggregatorTest, AggregateWindowOpener) {
// same-site frames.
for (auto* frame :
{main_frame, child_frame, opened_frame, child_of_opened_frame}) {
EXPECT_EQ(internal::FindAggregationStartNode(frame), main_frame)
<< frame->url();
EXPECT_EQ(FindAggregationStartNode(frame), main_frame) << frame->url();
}
WebMemoryAggregator aggregator(main_frame);
// Test the relationships of each node in the graph.
EXPECT_EQ(aggregator.FindNodeAggregationType(main_frame),
EXPECT_EQ(FindNodeAggregationType(&aggregator, main_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(aggregator.FindNodeAggregationType(child_frame),
EXPECT_EQ(FindNodeAggregationType(&aggregator, child_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
child_frame, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(aggregator.FindNodeAggregationType(opened_frame),
EXPECT_EQ(
GetSameOriginParentOrOpener(child_frame, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(FindNodeAggregationType(&aggregator, opened_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
opened_frame, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(aggregator.FindNodeAggregationType(child_of_opened_frame),
EXPECT_EQ(
GetSameOriginParentOrOpener(opened_frame, aggregator.requesting_origin()),
main_frame);
EXPECT_EQ(FindNodeAggregationType(&aggregator, child_of_opened_frame),
NodeAggregationType::kSameOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
child_of_opened_frame, aggregator.requesting_origin()),
EXPECT_EQ(GetSameOriginParentOrOpener(child_of_opened_frame,
aggregator.requesting_origin()),
opened_frame);
EXPECT_EQ(aggregator.FindNodeAggregationType(cross_site_child),
EXPECT_EQ(FindNodeAggregationType(&aggregator, cross_site_child),
NodeAggregationType::kCrossOriginAggregationPoint);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
cross_site_child, aggregator.requesting_origin()),
EXPECT_EQ(GetSameOriginParentOrOpener(cross_site_child,
aggregator.requesting_origin()),
opened_frame);
EXPECT_EQ(aggregator.FindNodeAggregationType(cross_site_popup),
EXPECT_EQ(FindNodeAggregationType(&aggregator, cross_site_popup),
NodeAggregationType::kInvisible);
EXPECT_EQ(internal::GetSameOriginParentOrOpener(
cross_site_popup, aggregator.requesting_origin()),
EXPECT_EQ(GetSameOriginParentOrOpener(cross_site_popup,
aggregator.requesting_origin()),
main_frame);
auto expected_result = CreateExpectedMemoryMeasurement({
......@@ -577,13 +624,13 @@ TEST_F(WebMemoryAggregatorTest, AggregateWindowOpener) {
const std::string url = frame->url().spec();
SCOPED_TRACE(url);
const FrameNode* start_node = internal::FindAggregationStartNode(frame);
const FrameNode* start_node = FindAggregationStartNode(frame);
EXPECT_EQ(start_node, frame);
WebMemoryAggregator aggregator(start_node);
// Only check the NodeAggregationType of the single node that's iterated
// over. Parents of the start node have an undefined aggregation type.
EXPECT_EQ(aggregator.FindNodeAggregationType(start_node),
EXPECT_EQ(FindNodeAggregationType(&aggregator, start_node),
NodeAggregationType::kSameOriginAggregationPoint);
auto expected_cross_site_result = CreateExpectedMemoryMeasurement({
......@@ -607,7 +654,7 @@ TEST_F(WebMemoryAggregatorTest, AggregateProvisionalWindowOpener) {
WebMemoryAggregator aggregator(main_frame);
EXPECT_EQ(aggregator.FindNodeAggregationType(pending_frame),
EXPECT_EQ(FindNodeAggregationType(&aggregator, pending_frame),
NodeAggregationType::kInvisible);
auto expected_result = CreateExpectedMemoryMeasurement({
......@@ -672,8 +719,6 @@ TEST_F(WebMemoryAggregatorTest, AggregateCrossOriginWorker) {
worker1->RemoveClientFrame(child_frame);
}
} // namespace
} // namespace v8_memory
} // namespace performance_manager
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