blob: dfaf6aa6f4d7036f946c043d4f0906cda8bfb6a1 [file] [log] [blame]
// 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