[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}
diff --git a/components/performance_manager/graph/graph_impl_operations.cc b/components/performance_manager/graph/graph_impl_operations.cc
index 2883d1f..9957ab04 100644
--- a/components/performance_manager/graph/graph_impl_operations.cc
+++ b/components/performance_manager/graph/graph_impl_operations.cc
@@ -167,6 +167,31 @@
 }
 
 // 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) {
   bool has_frame = false;
diff --git a/components/performance_manager/graph/graph_impl_operations.h b/components/performance_manager/graph/graph_impl_operations.h
index ff67c60..344e342 100644
--- a/components/performance_manager/graph/graph_impl_operations.h
+++ b/components/performance_manager/graph/graph_impl_operations.h
@@ -20,6 +20,7 @@
 // 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 @@
   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);
diff --git a/components/performance_manager/graph/graph_operations.cc b/components/performance_manager/graph/graph_operations.cc
index 342ba76..adfdc511 100644
--- a/components/performance_manager/graph/graph_operations.cc
+++ b/components/performance_manager/graph/graph_operations.cc
@@ -58,6 +58,14 @@
 }
 
 // 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),
                                        FrameNodeImpl::FromNode(frame));
diff --git a/components/performance_manager/graph/graph_operations_unittest.cc b/components/performance_manager/graph/graph_operations_unittest.cc
index 9fde47a3..0690b59 100644
--- a/components/performance_manager/graph/graph_operations_unittest.cc
+++ b/components/performance_manager/graph/graph_operations_unittest.cc
@@ -26,6 +26,7 @@
     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 @@
         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 @@
   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()));
diff --git a/components/performance_manager/public/graph/graph_operations.h b/components/performance_manager/public/graph/graph_operations.h
index df00f00..1dc019f 100644
--- a/components/performance_manager/public/graph/graph_operations.h
+++ b/components/performance_manager/public/graph/graph_operations.h
@@ -19,6 +19,7 @@
 // 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 @@
   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);