| // Copyright 2021 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 <string> |
| |
| #include "base/bind.h" |
| #include "base/memory/weak_ptr.h" |
| #include "components/performance_manager/public/graph/frame_node.h" |
| #include "components/performance_manager/public/graph/page_node.h" |
| #include "components/performance_manager/public/performance_manager.h" |
| #include "components/performance_manager/test_support/performance_manager_browsertest_harness.h" |
| #include "content/public/browser/back_forward_cache.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/back_forward_cache_util.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/prerender_test_util.h" |
| #include "content/public/test/test_utils.h" |
| #include "content/shell/browser/shell.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace performance_manager { |
| |
| class Graph; |
| |
| // Tests that the PerformanceManager node states are updated correctly during |
| // prerendering. |
| // |
| // TODO(crbug.com/1211368): These tests assume prerendering frames are added as |
| // extra FrameNodes on the existing PageNode. Update this logic once |
| // prerendering frame trees have their own PageNode. |
| class PerformanceManagerPrerenderingBrowserTest |
| : public PerformanceManagerBrowserTestHarness { |
| public: |
| using Super = PerformanceManagerBrowserTestHarness; |
| |
| PerformanceManagerPrerenderingBrowserTest() |
| : prerender_helper_(base::BindRepeating( |
| &PerformanceManagerPrerenderingBrowserTest::web_contents, |
| base::Unretained(this))) { |
| prerender_helper_.SetUp(&ssl_server_); |
| } |
| |
| void SetUpOnMainThread() override { |
| Super::SetUpOnMainThread(); |
| ssl_server_.AddDefaultHandlers(GetTestDataFilePath()); |
| ssl_server_.SetSSLConfig( |
| net::test_server::EmbeddedTestServer::CERT_TEST_NAMES); |
| ASSERT_TRUE(ssl_server_.Start()); |
| |
| // TODO(https://crbug.com/1186893): PrerenderHost is not deleted when the |
| // page enters BackForwardCache, though it should be. While this |
| // functionality is not implemented, disable BackForwardCache for testing |
| // and wait for the old RenderFrameHost to be deleted after we navigate away |
| // from it. |
| content::DisableBackForwardCacheForTesting( |
| web_contents(), content::BackForwardCache::TEST_REQUIRES_NO_CACHING); |
| } |
| |
| void TearDownOnMainThread() override { |
| EXPECT_TRUE(ssl_server_.ShutdownAndWaitUntilComplete()); |
| Super::TearDownOnMainThread(); |
| } |
| |
| content::WebContents* web_contents() { return shell()->web_contents(); } |
| |
| GURL GetUrl(const std::string& path) { |
| return ssl_server_.GetURL("a.test", path); |
| } |
| |
| protected: |
| net::test_server::EmbeddedTestServer ssl_server_{ |
| net::test_server::EmbeddedTestServer::TYPE_HTTPS}; |
| content::test::PrerenderTestHelper prerender_helper_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PerformanceManagerPrerenderingBrowserTest, |
| PrerenderingFinishes) { |
| const GURL kInitialUrl = GetUrl("/empty.html"); |
| const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); |
| |
| // Navigate to an initial page. Test that PM has a PageNode for it, and |
| // GetMainFrameNode returns its main frame. |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kInitialUrl)); |
| base::WeakPtr<PageNode> page_node = |
| PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents()); |
| const FrameNode* initial_main_frame_node = nullptr; |
| int64_t initial_navigation_id = 0; |
| RunInGraph([&](Graph*) { |
| ASSERT_TRUE(page_node); |
| EXPECT_EQ(page_node->GetMainFrameNodes().size(), 1U); |
| initial_main_frame_node = page_node->GetMainFrameNode(); |
| initial_navigation_id = page_node->GetNavigationID(); |
| EXPECT_EQ(page_node->GetMainFrameUrl(), kInitialUrl); |
| EXPECT_TRUE(initial_main_frame_node->IsCurrent()); |
| }); |
| |
| // Start prerendering a document. Test that the prerendering frame tree is |
| // added as additional frame nodes, but GetMainFrameNode is unchanged. |
| prerender_helper_.AddPrerender(kPrerenderingUrl); |
| base::WeakPtr<PageNode> page_node2 = |
| PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents()); |
| const FrameNode* prerender_main_frame_node = nullptr; |
| RunInGraph([&](Graph*) { |
| ASSERT_TRUE(page_node); |
| ASSERT_EQ(page_node.get(), page_node2.get()); |
| EXPECT_EQ(page_node->GetMainFrameNodes().size(), 2U); |
| EXPECT_EQ(page_node->GetMainFrameNode(), initial_main_frame_node); |
| EXPECT_TRUE(initial_main_frame_node->IsCurrent()); |
| |
| // Find the prerendering MainFrameNode. |
| for (const FrameNode* frame_node : page_node->GetMainFrameNodes()) { |
| if (frame_node != initial_main_frame_node) { |
| prerender_main_frame_node = frame_node; |
| break; |
| } |
| } |
| ASSERT_TRUE(prerender_main_frame_node); |
| EXPECT_EQ(prerender_main_frame_node->GetURL(), kPrerenderingUrl); |
| EXPECT_FALSE(prerender_main_frame_node->IsCurrent()); |
| |
| // The prerendering navigation should not be reflected in the PageNode. |
| EXPECT_EQ(page_node->GetNavigationID(), initial_navigation_id); |
| EXPECT_EQ(page_node->GetMainFrameUrl(), kInitialUrl); |
| }); |
| |
| // Activate the prerendered document. Test that GetMainFrameNode now returns |
| // its main frame, and the original frame tree is gone. |
| content::RenderFrameDeletedObserver deleted_observer( |
| web_contents()->GetMainFrame()); |
| content::test::PrerenderHostObserver prerender_observer(*web_contents(), |
| kPrerenderingUrl); |
| prerender_helper_.NavigatePrimaryPage(kPrerenderingUrl); |
| ASSERT_TRUE(prerender_observer.was_activated()); |
| deleted_observer.WaitUntilDeleted(); |
| RunInGraph([&](Graph*) { |
| ASSERT_TRUE(page_node); |
| EXPECT_EQ(page_node->GetMainFrameNodes().size(), 1U); |
| EXPECT_EQ(page_node->GetMainFrameNode(), prerender_main_frame_node); |
| EXPECT_TRUE(prerender_main_frame_node->IsCurrent()); |
| |
| // Now the PageNode should reflect the prerendering navigation. |
| EXPECT_NE(page_node->GetNavigationID(), initial_navigation_id); |
| EXPECT_EQ(page_node->GetMainFrameUrl(), kPrerenderingUrl); |
| }); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PerformanceManagerPrerenderingBrowserTest, |
| PrerenderingCancelled) { |
| const GURL kInitialUrl = GetUrl("/empty.html"); |
| const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); |
| const GURL kFinalUrl = GetUrl("/empty.html?elsewhere"); |
| |
| // Navigate to an initial page. Test that PM has a PageNode for it, and |
| // GetMainFrameNode returns its main frame. |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kInitialUrl)); |
| base::WeakPtr<PageNode> page_node = |
| PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents()); |
| const FrameNode* initial_main_frame_node = nullptr; |
| int64_t initial_navigation_id = 0; |
| RunInGraph([&](Graph*) { |
| ASSERT_TRUE(page_node); |
| EXPECT_EQ(page_node->GetMainFrameNodes().size(), 1U); |
| initial_main_frame_node = page_node->GetMainFrameNode(); |
| initial_navigation_id = page_node->GetNavigationID(); |
| EXPECT_EQ(page_node->GetMainFrameUrl(), kInitialUrl); |
| EXPECT_TRUE(initial_main_frame_node->IsCurrent()); |
| }); |
| |
| // Start prerendering a document. Test that the prerendering frame tree is |
| // added as additional frame nodes, but GetMainFrameNode is unchanged. |
| int prerender_host = prerender_helper_.AddPrerender(kPrerenderingUrl); |
| base::WeakPtr<PageNode> page_node2 = |
| PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents()); |
| const FrameNode* prerender_main_frame_node = nullptr; |
| RunInGraph([&](Graph*) { |
| ASSERT_TRUE(page_node); |
| ASSERT_EQ(page_node.get(), page_node2.get()); |
| EXPECT_EQ(page_node->GetMainFrameNodes().size(), 2U); |
| EXPECT_EQ(page_node->GetMainFrameNode(), initial_main_frame_node); |
| EXPECT_TRUE(initial_main_frame_node->IsCurrent()); |
| |
| // Find the prerendering MainFrameNode. |
| for (const FrameNode* frame_node : page_node->GetMainFrameNodes()) { |
| if (frame_node != initial_main_frame_node) { |
| prerender_main_frame_node = frame_node; |
| break; |
| } |
| } |
| ASSERT_TRUE(prerender_main_frame_node); |
| EXPECT_EQ(prerender_main_frame_node->GetURL(), kPrerenderingUrl); |
| EXPECT_FALSE(prerender_main_frame_node->IsCurrent()); |
| |
| // The prerendering navigation should not be reflected in the PageNode. |
| EXPECT_EQ(page_node->GetNavigationID(), initial_navigation_id); |
| EXPECT_EQ(page_node->GetMainFrameUrl(), kInitialUrl); |
| }); |
| |
| // Navigate the main frame to another page. Test that the prerendering frame |
| // tree is removed from PerformanceManager. |
| content::RenderFrameDeletedObserver deleted_observer( |
| prerender_helper_.GetPrerenderedMainFrameHost(prerender_host)); |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kFinalUrl)); |
| deleted_observer.WaitUntilDeleted(); |
| RunInGraph([&](Graph*) { |
| ASSERT_TRUE(page_node); |
| EXPECT_EQ(page_node->GetMainFrameNodes().size(), 1U); |
| EXPECT_EQ(page_node->GetMainFrameNode(), initial_main_frame_node); |
| EXPECT_EQ(page_node->GetMainFrameUrl(), kFinalUrl); |
| EXPECT_TRUE(initial_main_frame_node->IsCurrent()); |
| }); |
| } |
| |
| } // namespace performance_manager |