blob: ce94ea047507839e24df4a5803dcb40537c4a543 [file] [log] [blame]
// Copyright 2020 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 <memory>
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/test/test_utils.h"
#include "content/test/navigation_simulator_impl.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
#include "net/base/isolation_info.h"
#include "net/cookies/site_for_cookies.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
class RenderFrameHostImplTest : public RenderViewHostImplTestHarness {
public:
void SetUp() override {
RenderViewHostImplTestHarness::SetUp();
contents()->GetMainFrame()->InitializeRenderFrameIfNeeded();
}
};
TEST_F(RenderFrameHostImplTest, ExpectedMainWorldOrigin) {
GURL initial_url = GURL("https://initial.example.test/");
GURL final_url = GURL("https://final.example.test/");
auto get_expected_main_world_origin = [](RenderFrameHost* rfh) {
NavigationRequest* in_flight_request =
static_cast<RenderFrameHostImpl*>(rfh)
->FindLatestNavigationRequestThatIsStillCommitting();
return in_flight_request ? in_flight_request->GetOriginToCommit()
: rfh->GetLastCommittedOrigin();
};
// Start the test with a simple navigation.
{
std::unique_ptr<NavigationSimulator> simulator =
NavigationSimulator::CreateRendererInitiated(initial_url, main_rfh());
simulator->Start();
simulator->Commit();
}
RenderFrameHostImpl* initial_rfh = main_test_rfh();
// This test is for a bug that only happens when there is no RFH swap on
// same-site navigations, so we should disable same-site proactive
// BrowsingInstance for |initial_rfh| before continiung.
// Note: this will not disable RenderDocument.
// TODO(crbug.com/936696): Skip this test when main-frame RenderDocument is
// enabled.
DisableProactiveBrowsingInstanceSwapFor(initial_rfh);
// Verify expected main world origin in a steady state - after a commit it
// should be the same as the last committed origin.
EXPECT_EQ(url::Origin::Create(initial_url),
get_expected_main_world_origin(main_rfh()));
EXPECT_EQ(url::Origin::Create(initial_url),
main_rfh()->GetLastCommittedOrigin());
EXPECT_EQ(blink::StorageKey(url::Origin::Create(initial_url)),
main_test_rfh()->storage_key());
// Verify expected main world origin when a pending navigation was started but
// hasn't yet reached the ready-to-commit state.
std::unique_ptr<NavigationSimulator> simulator2 =
NavigationSimulator::CreateRendererInitiated(final_url, main_rfh());
simulator2->Start();
EXPECT_EQ(url::Origin::Create(initial_url),
get_expected_main_world_origin(main_rfh()));
// Verify expected main world origin when a pending navigation has reached the
// ready-to-commit state. Note that the last committed origin shouldn't
// change yet at this point.
simulator2->ReadyToCommit();
simulator2->Wait();
EXPECT_EQ(url::Origin::Create(final_url),
get_expected_main_world_origin(main_rfh()));
EXPECT_EQ(url::Origin::Create(initial_url),
main_rfh()->GetLastCommittedOrigin());
EXPECT_EQ(blink::StorageKey(url::Origin::Create(initial_url)),
main_test_rfh()->storage_key());
// Verify expected main world origin once we are again in a steady state -
// after a commit.
simulator2->Commit();
EXPECT_EQ(url::Origin::Create(final_url),
get_expected_main_world_origin(main_rfh()));
EXPECT_EQ(url::Origin::Create(final_url),
main_rfh()->GetLastCommittedOrigin());
EXPECT_EQ(blink::StorageKey(url::Origin::Create(final_url)),
main_test_rfh()->storage_key());
// As a test correctness check, verify that there was no RFH swap (the bug
// this test protects against would only happen if there is no swap). In
// fact, FindLatestNavigationRequestThatIsStillCommitting might possibly be
// removed entirely once we swap on all document changes.
EXPECT_EQ(initial_rfh, main_rfh());
}
// Test the IsolationInfo and related fields of a request during the various
// phases of a commit, when a RenderFrameHost is reused. Once RenderDocument
// ships, this test may no longer be needed.
TEST_F(RenderFrameHostImplTest, IsolationInfoDuringCommit) {
GURL initial_url = GURL("https://initial.example.test/");
url::Origin expected_initial_origin = url::Origin::Create(initial_url);
blink::StorageKey expected_initial_storage_key =
blink::StorageKey(expected_initial_origin);
net::IsolationInfo expected_initial_isolation_info =
net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther, expected_initial_origin,
expected_initial_origin,
net::SiteForCookies::FromOrigin(expected_initial_origin),
std::set<net::SchemefulSite>());
GURL final_url = GURL("https://final.example.test/");
url::Origin expected_final_origin = url::Origin::Create(final_url);
blink::StorageKey expected_final_storage_key =
blink::StorageKey(expected_final_origin);
net::IsolationInfo expected_final_isolation_info = net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther, expected_final_origin,
expected_final_origin,
net::SiteForCookies::FromOrigin(expected_final_origin),
std::set<net::SchemefulSite>());
// Start the test with a simple navigation.
{
std::unique_ptr<NavigationSimulator> simulator =
NavigationSimulator::CreateRendererInitiated(initial_url, main_rfh());
simulator->Start();
simulator->Commit();
}
// This test is targetted at the case an RFH is reused between navigations.
RenderFrameHost* initial_rfh = main_rfh();
DisableProactiveBrowsingInstanceSwapFor(main_rfh());
// Check values for the initial commit.
EXPECT_EQ(expected_initial_origin, main_rfh()->GetLastCommittedOrigin());
EXPECT_EQ(expected_initial_storage_key, main_test_rfh()->storage_key());
EXPECT_TRUE(expected_initial_isolation_info.IsEqualForTesting(
main_rfh()->GetIsolationInfoForSubresources()));
EXPECT_EQ(expected_initial_isolation_info.network_isolation_key(),
main_rfh()->GetNetworkIsolationKey());
EXPECT_TRUE(expected_initial_isolation_info.site_for_cookies().IsEquivalent(
static_cast<RenderFrameHostImpl*>(main_rfh())->ComputeSiteForCookies()));
EXPECT_TRUE(expected_initial_isolation_info.IsEqualForTesting(
main_rfh()->GetPendingIsolationInfoForSubresources()));
// Values should be the same when a pending navigation was started but
// hasn't yet reached the ready-to-commit state.
std::unique_ptr<NavigationSimulator> simulator2 =
NavigationSimulator::CreateRendererInitiated(final_url, main_rfh());
simulator2->Start();
EXPECT_EQ(expected_initial_origin, main_rfh()->GetLastCommittedOrigin());
EXPECT_EQ(expected_initial_storage_key, main_test_rfh()->storage_key());
EXPECT_TRUE(expected_initial_isolation_info.IsEqualForTesting(
main_rfh()->GetIsolationInfoForSubresources()));
EXPECT_EQ(expected_initial_isolation_info.network_isolation_key(),
main_rfh()->GetNetworkIsolationKey());
EXPECT_TRUE(expected_initial_isolation_info.site_for_cookies().IsEquivalent(
static_cast<RenderFrameHostImpl*>(main_rfh())->ComputeSiteForCookies()));
EXPECT_TRUE(expected_initial_isolation_info.IsEqualForTesting(
main_rfh()->GetPendingIsolationInfoForSubresources()));
// Only the GetPendingIsolationInfoForSubresources() should change when a
// pending navigation has reached the ready-to-commit state.
simulator2->ReadyToCommit();
simulator2->Wait();
EXPECT_EQ(expected_initial_origin, main_rfh()->GetLastCommittedOrigin());
EXPECT_EQ(expected_initial_storage_key, main_test_rfh()->storage_key());
EXPECT_TRUE(expected_initial_isolation_info.IsEqualForTesting(
main_rfh()->GetIsolationInfoForSubresources()));
EXPECT_EQ(expected_initial_isolation_info.network_isolation_key(),
main_rfh()->GetNetworkIsolationKey());
EXPECT_TRUE(expected_initial_isolation_info.site_for_cookies().IsEquivalent(
static_cast<RenderFrameHostImpl*>(main_rfh())->ComputeSiteForCookies()));
EXPECT_TRUE(expected_final_isolation_info.IsEqualForTesting(
main_rfh()->GetPendingIsolationInfoForSubresources()));
// Verify expected main world origin once we are again in a steady state -
// after a commit.
simulator2->Commit();
EXPECT_EQ(expected_final_origin, main_rfh()->GetLastCommittedOrigin());
EXPECT_EQ(expected_final_storage_key, main_test_rfh()->storage_key());
EXPECT_TRUE(expected_final_isolation_info.IsEqualForTesting(
main_rfh()->GetIsolationInfoForSubresources()));
EXPECT_EQ(expected_final_isolation_info.network_isolation_key(),
main_rfh()->GetNetworkIsolationKey());
EXPECT_TRUE(expected_final_isolation_info.site_for_cookies().IsEquivalent(
static_cast<RenderFrameHostImpl*>(main_rfh())->ComputeSiteForCookies()));
EXPECT_TRUE(expected_final_isolation_info.IsEqualForTesting(
main_rfh()->GetPendingIsolationInfoForSubresources()));
// As a test correctness check, verify that there was no RFH swap. When
// there's always an RFH swap, this test will likely no longer be useful.
EXPECT_EQ(initial_rfh, main_rfh());
}
TEST_F(RenderFrameHostImplTest, PolicyContainerLifecycle) {
TestRenderFrameHost* main_rfh = contents()->GetMainFrame();
ASSERT_NE(main_rfh->policy_container_host(), nullptr);
EXPECT_EQ(main_rfh->policy_container_host()->referrer_policy(),
network::mojom::ReferrerPolicy::kDefault);
static_cast<blink::mojom::PolicyContainerHost*>(
main_rfh->policy_container_host())
->SetReferrerPolicy(network::mojom::ReferrerPolicy::kAlways);
EXPECT_EQ(main_rfh->policy_container_host()->referrer_policy(),
network::mojom::ReferrerPolicy::kAlways);
// Create a child frame and check that it inherits the PolicyContainerHost
// from the parent frame.
auto* child_frame = static_cast<TestRenderFrameHost*>(
content::RenderFrameHostTester::For(main_test_rfh())
->AppendChild("child"));
ASSERT_NE(child_frame->policy_container_host(), nullptr);
EXPECT_EQ(child_frame->policy_container_host()->referrer_policy(),
network::mojom::ReferrerPolicy::kAlways);
// Create a new WebContents with opener and test that the new main frame
// inherits the PolicyContainerHost from the opener.
static_cast<blink::mojom::PolicyContainerHost*>(
child_frame->policy_container_host())
->SetReferrerPolicy(network::mojom::ReferrerPolicy::kNever);
WebContents::CreateParams params(browser_context());
std::unique_ptr<WebContentsImpl> new_contents(
WebContentsImpl::CreateWithOpener(params, child_frame));
RenderFrameHostImpl* new_frame =
new_contents->GetFrameTree()->root()->current_frame_host();
ASSERT_NE(new_frame->policy_container_host(), nullptr);
EXPECT_EQ(new_frame->policy_container_host()->referrer_policy(),
network::mojom::ReferrerPolicy::kNever);
}
TEST_F(RenderFrameHostImplTest, FaviconURLsSet) {
TestRenderFrameHost* main_rfh = contents()->GetMainFrame();
const auto kFavicon =
blink::mojom::FaviconURL(GURL("https://example.com/favicon.ico"),
blink::mojom::FaviconIconType::kFavicon, {});
std::unique_ptr<NavigationSimulator> navigation =
NavigationSimulator::CreateBrowserInitiated(GURL("https://example.com"),
contents());
ui::PageTransition transition = ui::PAGE_TRANSITION_LINK;
navigation->SetTransition(transition);
navigation->Commit();
EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
std::vector<blink::mojom::FaviconURLPtr> one_favicon_url;
one_favicon_url.push_back(blink::mojom::FaviconURL::New(kFavicon));
main_rfh->UpdateFaviconURL(std::move(one_favicon_url));
EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
std::vector<blink::mojom::FaviconURLPtr> two_favicon_urls;
two_favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
two_favicon_urls.push_back(blink::mojom::FaviconURL::New(kFavicon));
main_rfh->UpdateFaviconURL(std::move(two_favicon_urls));
EXPECT_EQ(2u, contents()->GetFaviconURLs().size());
std::vector<blink::mojom::FaviconURLPtr> another_one_favicon_url;
another_one_favicon_url.push_back(blink::mojom::FaviconURL::New(kFavicon));
main_rfh->UpdateFaviconURL(std::move(another_one_favicon_url));
EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
}
TEST_F(RenderFrameHostImplTest, FaviconURLsResetWithNavigation) {
TestRenderFrameHost* main_rfh = contents()->GetMainFrame();
std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
favicon_urls.push_back(blink::mojom::FaviconURL::New(
GURL("https://example.com/favicon.ico"),
blink::mojom::FaviconIconType::kFavicon, std::vector<gfx::Size>()));
std::unique_ptr<NavigationSimulator> navigation =
NavigationSimulator::CreateBrowserInitiated(GURL("https://example.com"),
contents());
ui::PageTransition transition = ui::PAGE_TRANSITION_LINK;
navigation->SetTransition(transition);
navigation->Commit();
EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
main_rfh->UpdateFaviconURL(std::move(favicon_urls));
EXPECT_EQ(1u, contents()->GetFaviconURLs().size());
navigation = NavigationSimulator::CreateBrowserInitiated(
GURL("https://example.com/navigation.html"), contents());
navigation->SetTransition(transition);
navigation->Commit();
EXPECT_EQ(0u, contents()->GetFaviconURLs().size());
}
TEST_F(RenderFrameHostImplTest, ChildOfAnonymousIsAnonymous) {
EXPECT_FALSE(main_test_rfh()->anonymous());
auto* child_frame = static_cast<TestRenderFrameHost*>(
content::RenderFrameHostTester::For(main_test_rfh())
->AppendChild("child"));
EXPECT_FALSE(child_frame->anonymous());
EXPECT_FALSE(child_frame->storage_key().nonce().has_value());
child_frame->frame_tree_node()->set_anonymous(true);
EXPECT_FALSE(child_frame->anonymous());
EXPECT_FALSE(child_frame->storage_key().nonce().has_value());
// A navigation in the anonymous iframe commits an anonymous RFH.
std::unique_ptr<NavigationSimulator> navigation =
NavigationSimulator::CreateRendererInitiated(
GURL("https://example.com/navigation.html"), child_frame);
navigation->Commit();
child_frame =
static_cast<TestRenderFrameHost*>(navigation->GetFinalRenderFrameHost());
EXPECT_TRUE(child_frame->anonymous());
EXPECT_TRUE(child_frame->storage_key().nonce().has_value());
// An anonymous document sets a nonce on its network isolation key.
EXPECT_TRUE(child_frame->GetNetworkIsolationKey().GetNonce().has_value());
EXPECT_EQ(main_test_rfh()->GetPage().anonymous_iframes_nonce(),
child_frame->GetNetworkIsolationKey().GetNonce().value());
// A child of an anonymous RFH is anonymous.
auto* grandchild_frame = static_cast<TestRenderFrameHost*>(
content::RenderFrameHostTester::For(child_frame)
->AppendChild("grandchild"));
EXPECT_TRUE(grandchild_frame->anonymous());
EXPECT_TRUE(child_frame->storage_key().nonce().has_value());
// The two anonymous RFH's storage keys should have the same nonce.
EXPECT_EQ(child_frame->storage_key().nonce().value(),
grandchild_frame->storage_key().nonce().value());
// Also the anonymous initial empty document sets a nonce on its network
// isolation key.
EXPECT_TRUE(
grandchild_frame->GetNetworkIsolationKey().GetNonce().has_value());
EXPECT_EQ(main_test_rfh()->GetPage().anonymous_iframes_nonce(),
grandchild_frame->GetNetworkIsolationKey().GetNonce().value());
}
} // namespace content