0

[performance_manager] Add an operation for visiting a page and its embeds

Introduce GraphOperations::VisitPageAndEmbedsPreOrder to run a visitor
for a PageNode and all PageNodes embedded within it. This is useful for
scenarios such as guest views, where one WebContents embeds another.

Bug: 40925658
AX-Relnotes: n/a.
Change-Id: I212a1e98de890e5d3675b2eeee698034dfa2ec0e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5352066
Reviewed-by: Joe Mason <joenotcharles@google.com>
Commit-Queue: Greg Thompson <grt@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1341520}
This commit is contained in:
Greg Thompson
2024-08-14 08:25:33 +00:00
committed by Chromium LUCI CQ
parent e9d9076646
commit 17d11552de
5 changed files with 72 additions and 0 deletions

@ -166,6 +166,31 @@ bool GraphImplOperations::VisitFrameTreePostOrder(
return true;
}
// static
bool GraphImplOperations::VisitPageAndEmbedsPreOrder(
PageNodeImpl* page,
PageNodeImplVisitor visitor) {
if (!visitor(page)) {
return false;
}
for (FrameNodeImpl* main_frame_node : page->main_frame_nodes()) {
if (!VisitFrameAndChildrenPreOrder(
main_frame_node, [&visitor](FrameNodeImpl* frame_node) {
const FrameNode* const node = frame_node;
for (const auto* page_node : node->GetEmbeddedPageNodes()) {
if (!visitor(PageNodeImpl::FromNode(page_node))) {
return false;
}
}
return true;
})) {
return false;
}
}
return true;
}
// static
bool GraphImplOperations::HasFrame(const PageNodeImpl* page,
FrameNodeImpl* frame) {

@ -20,6 +20,7 @@ class WorkerNodeImpl;
// graph.
struct GraphImplOperations {
using FrameNodeImplVisitor = base::FunctionRef<bool(FrameNodeImpl*)>;
using PageNodeImplVisitor = base::FunctionRef<bool(PageNodeImpl*)>;
using WorkerNodeImplVisitor = base::FunctionRef<bool(WorkerNodeImpl*)>;
// Returns the collection of page nodes that are associated with the given
@ -59,6 +60,12 @@ struct GraphImplOperations {
static bool VisitFrameTreePostOrder(const PageNodeImpl* page,
FrameNodeImplVisitor visitor);
// Traverses the tree of embedded pages rooted at `page`, invoking `visitor`
// for each page node. Returns false if `visitor` returns false (stopping the
// traversal at that point); otherwise, returns `true`.
static bool VisitPageAndEmbedsPreOrder(PageNodeImpl* page,
PageNodeImplVisitor visitor);
// Returns true if the given |frame| is in the frame tree associated with the
// given |page|.
static bool HasFrame(const PageNodeImpl* page, FrameNodeImpl* frame);

@ -57,6 +57,14 @@ bool GraphOperations::VisitFrameTreePostOrder(const PageNode* page,
});
}
// static
bool GraphOperations::VisitPageAndEmbedsPreOrder(const PageNode* page,
PageNodeVisitor visitor) {
return GraphImplOperations::VisitPageAndEmbedsPreOrder(
PageNodeImpl::FromNode(page),
[&visitor](PageNodeImpl* page_impl) { return visitor(page_impl); });
}
// static
bool GraphOperations::HasFrame(const PageNode* page, const FrameNode* frame) {
return GraphImplOperations::HasFrame(PageNodeImpl::FromNode(page),

@ -26,6 +26,7 @@ class GraphOperationsTest : public GraphTestHarness {
process2_ = CreateNode<ProcessNodeImpl>();
page1_ = CreateNode<PageNodeImpl>();
page2_ = CreateNode<PageNodeImpl>();
page3_ = CreateNode<PageNodeImpl>();
mainframe1_ = CreateFrameNodeAutoId(process1_.get(), page1_.get(), nullptr);
mainframe2_ = CreateFrameNodeAutoId(process2_.get(), page2_.get(), nullptr);
childframe1a_ =
@ -36,12 +37,15 @@ class GraphOperationsTest : public GraphTestHarness {
CreateFrameNodeAutoId(process1_.get(), page2_.get(), mainframe2_.get());
childframe2b_ =
CreateFrameNodeAutoId(process1_.get(), page2_.get(), mainframe2_.get());
page3_->SetEmbedderFrameNodeAndEmbeddingType(
mainframe1_.get(), PageNode::EmbeddingType::kGuestView);
}
TestNodeWrapper<ProcessNodeImpl> process1_;
TestNodeWrapper<ProcessNodeImpl> process2_;
TestNodeWrapper<PageNodeImpl> page1_;
TestNodeWrapper<PageNodeImpl> page2_;
TestNodeWrapper<PageNodeImpl> page3_; // A guest of `page1_`.
// Root nodes. |mainframeX_| is in |processX_|.
TestNodeWrapper<FrameNodeImpl> mainframe1_;
@ -144,6 +148,27 @@ TEST_F(GraphOperationsTest, VisitFrameTree) {
EXPECT_EQ(1u, visited.size());
}
TEST_F(GraphOperationsTest, VisitPageEmbeds) {
// Pages are visited embedder-to-embedded.
std::vector<const PageNode*> visited;
ASSERT_TRUE(GraphOperations::VisitPageAndEmbedsPreOrder(
page1_.get(), [&visited](const PageNode* page_node) {
visited.push_back(page_node);
return true;
}));
EXPECT_THAT(visited, testing::ElementsAre(ToPublic(page1_.get()),
ToPublic(page3_.get())));
// Stop after the first item.
visited.clear();
ASSERT_FALSE(GraphOperations::VisitPageAndEmbedsPreOrder(
page1_.get(), [&visited](const PageNode* page_node) {
visited.push_back(page_node);
return false;
}));
EXPECT_THAT(visited, testing::ElementsAre(ToPublic(page1_.get())));
}
TEST_F(GraphOperationsTest, HasFrame) {
EXPECT_TRUE(GraphOperations::HasFrame(page1_.get(), childframe1a_.get()));
EXPECT_FALSE(GraphOperations::HasFrame(page1_.get(), childframe2a_.get()));

@ -19,6 +19,7 @@ class WorkerNode;
// graph.
struct GraphOperations {
using FrameNodeVisitor = base::FunctionRef<bool(const FrameNode*)>;
using PageNodeVisitor = base::FunctionRef<bool(const PageNode*)>;
using WorkerNodeVisitor = base::FunctionRef<bool(const WorkerNode*)>;
// Returns the collection of page nodes that are associated with the given
@ -47,6 +48,12 @@ struct GraphOperations {
static bool VisitFrameTreePostOrder(const PageNode* page,
FrameNodeVisitor visitor);
// Traverses the tree of embedded pages rooted at `page`, invoking `visitor`
// for each page node. Returns false if `visitor` returns false (stopping the
// traversal at that point); otherwise, returns `true`.
static bool VisitPageAndEmbedsPreOrder(const PageNode* page,
PageNodeVisitor visitor);
// Returns true if the given |frame| is in the frame tree associated with the
// given |page|.
static bool HasFrame(const PageNode* page, const FrameNode* frame);