performance_manager_tab_helper_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/performance_manager/performance_manager_tab_helper.h"
  5. #include <set>
  6. #include <utility>
  7. #include "base/containers/contains.h"
  8. #include "base/run_loop.h"
  9. #include "base/test/bind.h"
  10. #include "components/performance_manager/graph/frame_node_impl.h"
  11. #include "components/performance_manager/graph/graph_impl_operations.h"
  12. #include "components/performance_manager/graph/page_node_impl.h"
  13. #include "components/performance_manager/performance_manager_impl.h"
  14. #include "components/performance_manager/public/graph/page_node.h"
  15. #include "components/performance_manager/render_process_user_data.h"
  16. #include "components/performance_manager/test_support/performance_manager_test_harness.h"
  17. #include "content/public/browser/render_process_host.h"
  18. #include "content/public/common/content_features.h"
  19. #include "content/public/test/navigation_simulator.h"
  20. #include "content/public/test/render_frame_host_test_support.h"
  21. #include "content/public/test/web_contents_tester.h"
  22. #include "testing/gmock/include/gmock/gmock.h"
  23. #include "testing/gtest/include/gtest/gtest.h"
  24. #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
  25. namespace performance_manager {
  26. namespace {
  27. const char kParentUrl[] = "https://parent.com/";
  28. const char kChild1Url[] = "https://child1.com/";
  29. const char kChild2Url[] = "https://child2.com/";
  30. const char kGrandchildUrl[] = "https://grandchild.com/";
  31. const char kNewGrandchildUrl[] = "https://newgrandchild.com/";
  32. const char kCousinFreddyUrl[] = "https://cousinfreddy.com/";
  33. class PerformanceManagerTabHelperTest : public PerformanceManagerTestHarness {
  34. public:
  35. PerformanceManagerTabHelperTest() = default;
  36. void TearDown() override {
  37. // Clean up the web contents, which should dispose of the page and frame
  38. // nodes involved.
  39. DeleteContents();
  40. PerformanceManagerTestHarness::TearDown();
  41. }
  42. // A helper function for checking that the graph matches the topology of
  43. // stuff in content. The graph should reflect the set of processes provided
  44. // by |hosts|, even though content may actually have other processes lying
  45. // around.
  46. void CheckGraphTopology(const std::set<content::RenderProcessHost*>& hosts,
  47. const char* grandchild_url);
  48. protected:
  49. static size_t CountAllRenderProcessHosts() {
  50. size_t num_hosts = 0;
  51. for (auto it = content::RenderProcessHost::AllHostsIterator();
  52. !it.IsAtEnd(); it.Advance()) {
  53. ++num_hosts;
  54. }
  55. return num_hosts;
  56. }
  57. };
  58. void CallOnGraphSync(PerformanceManagerImpl::GraphImplCallback callback) {
  59. base::RunLoop run_loop;
  60. PerformanceManagerImpl::CallOnGraphImpl(
  61. FROM_HERE,
  62. base::BindLambdaForTesting([&run_loop, &callback](GraphImpl* graph) {
  63. std::move(callback).Run(graph);
  64. run_loop.Quit();
  65. }));
  66. run_loop.Run();
  67. }
  68. void PerformanceManagerTabHelperTest::CheckGraphTopology(
  69. const std::set<content::RenderProcessHost*>& hosts,
  70. const char* grandchild_url) {
  71. // There may be more RenderProcessHosts in existence than those used from
  72. // the RFHs above. The graph may not reflect all of them, as only those
  73. // observed through the TabHelper will have been reflected in the graph.
  74. size_t num_hosts = CountAllRenderProcessHosts();
  75. EXPECT_LE(hosts.size(), num_hosts);
  76. EXPECT_NE(0u, hosts.size());
  77. // Convert the RPHs to ProcessNodeImpls so we can check they match.
  78. std::set<ProcessNodeImpl*> process_nodes;
  79. for (auto* host : hosts) {
  80. auto* data = RenderProcessUserData::GetForRenderProcessHost(host);
  81. EXPECT_TRUE(data);
  82. process_nodes.insert(data->process_node());
  83. }
  84. EXPECT_EQ(process_nodes.size(), hosts.size());
  85. // Check out the graph itself.
  86. CallOnGraphSync(base::BindLambdaForTesting(
  87. [&process_nodes, num_hosts, grandchild_url](GraphImpl* graph) {
  88. EXPECT_GE(num_hosts, graph->GetAllProcessNodeImpls().size());
  89. EXPECT_EQ(4u, graph->GetAllFrameNodeImpls().size());
  90. // Expect all frame nodes to be current. This fails if our
  91. // implementation of RenderFrameHostChanged is borked.
  92. for (auto* frame : graph->GetAllFrameNodeImpls())
  93. EXPECT_TRUE(frame->is_current());
  94. ASSERT_EQ(1u, graph->GetAllPageNodeImpls().size());
  95. auto* page = graph->GetAllPageNodeImpls()[0];
  96. // Extra RPHs can and most definitely do exist.
  97. auto associated_process_nodes =
  98. GraphImplOperations::GetAssociatedProcessNodes(page);
  99. EXPECT_GE(graph->GetAllProcessNodeImpls().size(),
  100. associated_process_nodes.size());
  101. EXPECT_GE(num_hosts, associated_process_nodes.size());
  102. for (auto* process_node : associated_process_nodes)
  103. EXPECT_TRUE(base::Contains(process_nodes, process_node));
  104. EXPECT_EQ(4u, GraphImplOperations::GetFrameNodes(page).size());
  105. ASSERT_EQ(1u, page->main_frame_nodes().size());
  106. auto* main_frame = page->GetMainFrameNodeImpl();
  107. EXPECT_EQ(kParentUrl, main_frame->url().spec());
  108. EXPECT_EQ(2u, main_frame->child_frame_nodes().size());
  109. for (auto* child_frame : main_frame->child_frame_nodes()) {
  110. if (child_frame->url().spec() == kChild1Url) {
  111. ASSERT_EQ(1u, child_frame->child_frame_nodes().size());
  112. auto* grandchild_frame =
  113. *(child_frame->child_frame_nodes().begin());
  114. EXPECT_EQ(grandchild_url, grandchild_frame->url().spec());
  115. } else if (child_frame->url().spec() == kChild2Url) {
  116. EXPECT_TRUE(child_frame->child_frame_nodes().empty());
  117. } else {
  118. FAIL() << "Unexpected child frame: " << child_frame->url().spec();
  119. }
  120. }
  121. }));
  122. }
  123. } // namespace
  124. TEST_F(PerformanceManagerTabHelperTest, FrameHierarchyReflectsToGraph) {
  125. SetContents(CreateTestWebContents());
  126. auto* parent = content::NavigationSimulator::NavigateAndCommitFromBrowser(
  127. web_contents(), GURL(kParentUrl));
  128. DCHECK(parent);
  129. auto* parent_tester = content::RenderFrameHostTester::For(parent);
  130. auto* child1 = content::NavigationSimulator::NavigateAndCommitFromDocument(
  131. GURL(kChild1Url), parent_tester->AppendChild("child1"));
  132. auto* grandchild =
  133. content::NavigationSimulator::NavigateAndCommitFromDocument(
  134. GURL(kGrandchildUrl),
  135. content::RenderFrameHostTester::For(child1)->AppendChild(
  136. "grandchild"));
  137. auto* child2 = content::NavigationSimulator::NavigateAndCommitFromDocument(
  138. GURL(kChild2Url), parent_tester->AppendChild("child2"));
  139. // Count the RFHs referenced.
  140. std::set<content::RenderProcessHost*> hosts;
  141. auto* grandchild_process = grandchild->GetProcess();
  142. hosts.insert(main_rfh()->GetProcess());
  143. hosts.insert(child1->GetProcess());
  144. hosts.insert(grandchild->GetProcess());
  145. hosts.insert(child2->GetProcess());
  146. CheckGraphTopology(hosts, kGrandchildUrl);
  147. // Navigate the grand-child frame. This tests that we accurately observe the
  148. // new RFH being created and marked current, with the old one being marked not
  149. // current and torn down. Note that the old RPH doesn't get torn down.
  150. auto* new_grandchild =
  151. content::NavigationSimulator::NavigateAndCommitFromDocument(
  152. GURL(kNewGrandchildUrl), grandchild);
  153. auto* new_grandchild_process = new_grandchild->GetProcess();
  154. // Update the set of processes we expect to be associated with the page.
  155. hosts.erase(grandchild_process);
  156. hosts.insert(new_grandchild_process);
  157. CheckGraphTopology(hosts, kNewGrandchildUrl);
  158. // Clean up the web contents, which should dispose of the page and frame nodes
  159. // involved.
  160. DeleteContents();
  161. // Allow content/ to settle.
  162. task_environment()->RunUntilIdle();
  163. size_t num_hosts = CountAllRenderProcessHosts();
  164. PerformanceManagerImpl::CallOnGraphImpl(
  165. FROM_HERE, base::BindLambdaForTesting([num_hosts](GraphImpl* graph) {
  166. EXPECT_GE(num_hosts, graph->GetAllProcessNodeImpls().size());
  167. EXPECT_EQ(0u, graph->GetAllFrameNodeImpls().size());
  168. ASSERT_EQ(0u, graph->GetAllPageNodeImpls().size());
  169. }));
  170. task_environment()->RunUntilIdle();
  171. }
  172. namespace {
  173. void ExpectPageIsAudible(bool is_audible) {
  174. CallOnGraphSync(base::BindLambdaForTesting([&](GraphImpl* graph) {
  175. ASSERT_EQ(1u, graph->GetAllPageNodeImpls().size());
  176. auto* page = graph->GetAllPageNodeImpls()[0];
  177. EXPECT_EQ(is_audible, page->is_audible());
  178. }));
  179. }
  180. } // namespace
  181. TEST_F(PerformanceManagerTabHelperTest, PageIsAudible) {
  182. SetContents(CreateTestWebContents());
  183. ExpectPageIsAudible(false);
  184. content::WebContentsTester::For(web_contents())->SetIsCurrentlyAudible(true);
  185. ExpectPageIsAudible(true);
  186. content::WebContentsTester::For(web_contents())->SetIsCurrentlyAudible(false);
  187. ExpectPageIsAudible(false);
  188. }
  189. TEST_F(PerformanceManagerTabHelperTest, GetFrameNode) {
  190. SetContents(CreateTestWebContents());
  191. auto* tab_helper =
  192. PerformanceManagerTabHelper::FromWebContents(web_contents());
  193. ASSERT_TRUE(tab_helper);
  194. // GetFrameNode() can return nullptr. In this test, it is achieved by using an
  195. // empty RenderFrameHost.
  196. auto* empty_frame = web_contents()->GetPrimaryMainFrame();
  197. DCHECK(empty_frame);
  198. auto* empty_frame_node = tab_helper->GetFrameNode(empty_frame);
  199. EXPECT_FALSE(empty_frame_node);
  200. // This navigation will create a frame node.
  201. auto* new_frame = content::NavigationSimulator::NavigateAndCommitFromBrowser(
  202. web_contents(), GURL(kParentUrl));
  203. DCHECK(new_frame);
  204. auto* new_frame_node = tab_helper->GetFrameNode(new_frame);
  205. EXPECT_TRUE(new_frame_node);
  206. }
  207. namespace {
  208. class LenientMockPageNodeObserver : public PageNode::ObserverDefaultImpl {
  209. public:
  210. LenientMockPageNodeObserver() = default;
  211. ~LenientMockPageNodeObserver() override = default;
  212. LenientMockPageNodeObserver(const LenientMockPageNodeObserver& other) =
  213. delete;
  214. LenientMockPageNodeObserver& operator=(const LenientMockPageNodeObserver&) =
  215. delete;
  216. MOCK_METHOD1(OnFaviconUpdated, void(const PageNode*));
  217. };
  218. using MockPageNodeObserver = ::testing::StrictMock<LenientMockPageNodeObserver>;
  219. } // namespace
  220. TEST_F(PerformanceManagerTabHelperTest,
  221. NotificationsFromInactiveFrameTreeAreIgnored) {
  222. SetContents(CreateTestWebContents());
  223. content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
  224. GURL(kParentUrl));
  225. auto* first_nav_main_rfh = web_contents()->GetPrimaryMainFrame();
  226. content::LeaveInPendingDeletionState(first_nav_main_rfh);
  227. content::NavigationSimulator::NavigateAndCommitFromBrowser(
  228. web_contents(), GURL(kCousinFreddyUrl));
  229. EXPECT_NE(web_contents()->GetPrimaryMainFrame(), first_nav_main_rfh);
  230. // Mock observer, this can only be used from the PM sequence.
  231. MockPageNodeObserver observer;
  232. {
  233. base::RunLoop run_loop;
  234. auto quit_closure = run_loop.QuitClosure();
  235. PerformanceManager::CallOnGraph(
  236. FROM_HERE, base::BindLambdaForTesting([&](Graph* graph) {
  237. graph->AddPageNodeObserver(&observer);
  238. std::move(quit_closure).Run();
  239. }));
  240. run_loop.Run();
  241. }
  242. auto* tab_helper =
  243. PerformanceManagerTabHelper::FromWebContents(web_contents());
  244. ASSERT_TRUE(tab_helper);
  245. // The first favicon change is always ignored, call DidUpdateFaviconURL twice
  246. // to ensure that the test doesn't pass simply because of that.
  247. tab_helper->DidUpdateFaviconURL(first_nav_main_rfh, {});
  248. tab_helper->DidUpdateFaviconURL(first_nav_main_rfh, {});
  249. {
  250. base::RunLoop run_loop;
  251. auto quit_closure = run_loop.QuitClosure();
  252. PerformanceManager::CallOnGraph(
  253. FROM_HERE, base::BindLambdaForTesting([&]() {
  254. // The observer shouldn't have been called at this point.
  255. testing::Mock::VerifyAndClear(&observer);
  256. std::move(quit_closure).Run();
  257. // Set the expectation for the next check.
  258. EXPECT_CALL(observer, OnFaviconUpdated(::testing::_));
  259. }));
  260. run_loop.Run();
  261. }
  262. // Sanity check to ensure that notification sent to the active main frame are
  263. // forwarded. DidUpdateFaviconURL needs to be called twice as the first
  264. // favicon change is always ignored.
  265. tab_helper->DidUpdateFaviconURL(web_contents()->GetPrimaryMainFrame(), {});
  266. tab_helper->DidUpdateFaviconURL(web_contents()->GetPrimaryMainFrame(), {});
  267. {
  268. base::RunLoop run_loop;
  269. auto quit_closure = run_loop.QuitClosure();
  270. PerformanceManager::CallOnGraph(
  271. FROM_HERE, base::BindLambdaForTesting([&](Graph* graph) {
  272. testing::Mock::VerifyAndClear(&observer);
  273. graph->RemovePageNodeObserver(&observer);
  274. std::move(quit_closure).Run();
  275. }));
  276. run_loop.Run();
  277. }
  278. }
  279. } // namespace performance_manager