#include "base/test/scoped_feature_list.h"
#include "base/unguessable_token.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.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/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
class EmbeddingTokenBrowserTest : public ContentBrowserTest {
public:
EmbeddingTokenBrowserTest() = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
scoped_feature_list_.InitWithFeaturesAndParameters(
GetDefaultEnabledBackForwardCacheFeaturesForTesting(
false),
GetDefaultDisabledBackForwardCacheFeaturesForTesting());
ContentBrowserTest::SetUpCommandLine(command_line);
IsolateAllSitesForTesting(command_line);
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
WebContents* web_contents() { return shell()->web_contents(); }
RenderFrameHostImpl* top_frame_host() {
return static_cast<RenderFrameHostImpl*>(
web_contents()->GetPrimaryMainFrame());
}
EmbeddingTokenBrowserTest(const EmbeddingTokenBrowserTest&) = delete;
EmbeddingTokenBrowserTest& operator=(const EmbeddingTokenBrowserTest&) =
delete;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest, EmbeddingTokenOnMainFrame) {
GURL a_url = embedded_test_server()->GetURL("a.com", "/site_isolation/");
GURL b_url = embedded_test_server()->GetURL("b.com", "/site_isolation/");
EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto first_token = top_frame_host()->GetEmbeddingToken().value();
EXPECT_TRUE(NavigateToURL(shell(), b_url.Resolve("blank.html")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
EXPECT_NE(top_frame_host()->GetEmbeddingToken().value(), first_token);
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
EmbeddingTokensAddedToCrossDocumentIFrames) {
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b(a),c,a)")));
ASSERT_EQ(3U, top_frame_host()->child_count());
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token = top_frame_host()->GetEmbeddingToken().value();
auto child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
EXPECT_NE(top_token, child_0_token);
ASSERT_EQ(1U, top_frame_host()->child_at(0)->child_count());
auto child_0_0_token = top_frame_host()
->child_at(0)
->child_at(0)
->current_frame_host()
->GetEmbeddingToken();
ASSERT_TRUE(child_0_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_0_token);
EXPECT_NE(top_token, child_0_0_token);
EXPECT_NE(child_0_token, child_0_0_token);
auto child_1_token =
top_frame_host()->child_at(1)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(child_1_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_1_token);
EXPECT_NE(top_token, child_1_token);
EXPECT_NE(child_0_token, child_1_token);
EXPECT_NE(child_0_0_token, child_1_token);
auto child_2_token =
top_frame_host()->child_at(2)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(child_2_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_2_token);
EXPECT_NE(top_token, child_2_token);
EXPECT_NE(child_0_token, child_2_token);
EXPECT_NE(child_0_0_token, child_2_token);
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
EmbeddingTokenSwapsOnCrossDocumentNavigation) {
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)")));
ASSERT_EQ(1U, top_frame_host()->child_count());
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token = top_frame_host()->GetEmbeddingToken().value();
RenderFrameHost* target = top_frame_host()->child_at(0)->current_frame_host();
auto child_0_token = target->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
EXPECT_NE(top_token, child_0_token);
NavigateIframeToURL(shell()->web_contents(), "child-0",
embedded_test_server()
->GetURL("b.com", "/site_isolation/")
.Resolve("blank.html"));
auto same_site_new_child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(same_site_new_child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), same_site_new_child_0_token);
EXPECT_NE(top_token, same_site_new_child_0_token);
EXPECT_NE(child_0_token, same_site_new_child_0_token);
{
if (ShouldCreateNewHostForSameSiteSubframe()) {
target = top_frame_host()->child_at(0)->current_frame_host();
}
RenderFrameDeletedObserver deleted_observer(target);
NavigateIframeToURL(shell()->web_contents(), "child-0",
embedded_test_server()
->GetURL("c.com", "/site_isolation/")
.Resolve("blank.html"));
deleted_observer.WaitUntilDeleted();
}
auto new_site_child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(same_site_new_child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), new_site_child_0_token);
EXPECT_NE(top_token, new_site_child_0_token);
EXPECT_NE(child_0_token, new_site_child_0_token);
EXPECT_NE(same_site_new_child_0_token, new_site_child_0_token);
}
IN_PROC_BROWSER_TEST_F(
EmbeddingTokenBrowserTest,
EmbeddingTokenNotChangedOnSubframeSameDocumentNavigation) {
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(a)")));
ASSERT_EQ(1U, top_frame_host()->child_count());
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token = top_frame_host()->GetEmbeddingToken().value();
RenderFrameHost* target = top_frame_host()->child_at(0)->current_frame_host();
auto child_0_token = target->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
EXPECT_NE(top_token, child_0_token);
auto b_url = embedded_test_server()->GetURL("b.com", "/site_isolation/");
{
RenderFrameDeletedObserver deleted_observer(
top_frame_host()->child_at(0)->current_frame_host());
NavigateIframeToURL(web_contents(), "child-0", b_url.Resolve("blank.html"));
deleted_observer.WaitUntilDeleted();
}
auto new_child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), new_child_0_token);
EXPECT_NE(top_token, new_child_0_token);
EXPECT_NE(child_0_token, new_child_0_token);
NavigateIframeToURL(web_contents(), "child-0",
b_url.Resolve("blank.html#foo"));
auto same_document_new_child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(same_document_new_child_0_token.has_value());
EXPECT_EQ(new_child_0_token, same_document_new_child_0_token);
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
EmbeddingTokenChangedOnSubframeNavigationToNewDocument) {
auto a_url = embedded_test_server()->GetURL("a.com", "/");
EXPECT_TRUE(NavigateToURL(
shell(), a_url.Resolve("cross_site_iframe_factory.html?a(b)")));
ASSERT_EQ(1U, top_frame_host()->child_count());
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token = top_frame_host()->GetEmbeddingToken().value();
RenderFrameHost* target = top_frame_host()->child_at(0)->current_frame_host();
auto child_0_token = target->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
EXPECT_NE(top_token, child_0_token);
{
RenderFrameDeletedObserver deleted_observer(target);
NavigateIframeToURL(web_contents(), "child-0",
a_url.Resolve("site_isolation/").Resolve("blank.html"));
deleted_observer.WaitUntilDeleted();
}
auto new_child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(new_child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), new_child_0_token);
EXPECT_NE(top_token, new_child_0_token);
EXPECT_NE(child_0_token, new_child_0_token);
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
BackForwardCacheCrossDocument) {
auto a_url = embedded_test_server()->GetURL("a.com", "/site_isolation/");
auto b_url = embedded_test_server()->GetURL("b.com", "/site_isolation/");
EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_a = top_frame_host()->GetEmbeddingToken().value();
EXPECT_TRUE(NavigateToURL(shell(), b_url.Resolve("blank.html")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_b = top_frame_host()->GetEmbeddingToken().value();
EXPECT_NE(top_token_a, top_token_b);
web_contents()->GetController().GoBack();
EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_a_prime = top_frame_host()->GetEmbeddingToken().value();
EXPECT_EQ(top_token_a, top_token_a_prime);
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
BackForwardCacheCrossDocumentAfterSameDocument) {
auto a_url = embedded_test_server()->GetURL("a.com", "/site_isolation/");
auto b_url = embedded_test_server()->GetURL("b.com", "/site_isolation/");
EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_a = top_frame_host()->GetEmbeddingToken().value();
EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html#foo")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
EXPECT_EQ(top_frame_host()->GetEmbeddingToken().value(), top_token_a);
EXPECT_TRUE(NavigateToURL(shell(), b_url.Resolve("blank.html")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_b = top_frame_host()->GetEmbeddingToken().value();
EXPECT_NE(top_token_a, top_token_b);
web_contents()->GetController().GoBack();
EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_a_prime = top_frame_host()->GetEmbeddingToken().value();
EXPECT_EQ(top_token_a, top_token_a_prime);
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
SameDocumentHistoryPreservesTokens) {
auto a_url = embedded_test_server()->GetURL("a.com", "/site_isolation/");
EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_a = top_frame_host()->GetEmbeddingToken().value();
EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html#foo")));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_a_prime = top_frame_host()->GetEmbeddingToken().value();
EXPECT_EQ(top_token_a, top_token_a_prime);
web_contents()->GetController().GoBack();
EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
auto top_token_a_prime_prime = top_frame_host()->GetEmbeddingToken().value();
EXPECT_EQ(top_token_a, top_token_a_prime_prime);
}
}