#include <atomic>
#include "base/path_service.h"
#include "base/strings/pattern.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/back_forward_cache_disable.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/renderer.mojom.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_paths.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/resource_load_observer.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/base/features.h"
#include "net/base/network_isolation_key.h"
#include "net/base/schemeful_site.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/scoped_mutually_exclusive_feature_list.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
#include "url/gurl.h"
namespace content {
namespace {
class SplitCacheContentBrowserTestBase : public ContentBrowserTest {
public:
enum class Context { kMainFrame, kSameOriginFrame, kCrossOriginFrame };
SplitCacheContentBrowserTestBase() = default;
SplitCacheContentBrowserTestBase(const SplitCacheContentBrowserTestBase&) =
delete;
SplitCacheContentBrowserTestBase& operator=(
const SplitCacheContentBrowserTestBase&) = delete;
void SetUp() override {
RenderWidgetHostImpl::DisableResizeAckCheckForTesting();
ContentBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&SplitCacheContentBrowserTestBase::CachedScriptHandler,
base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->Start());
}
std::unique_ptr<net::test_server::HttpResponse> CachedScriptHandler(
const net::test_server::HttpRequest& request) {
GURL absolute_url = embedded_test_server()->GetURL(request.relative_url);
if (absolute_url.GetPath() == "/redirect_to_d") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_SEE_OTHER);
http_response->AddCustomHeader(
"Location",
embedded_test_server()->GetURL("d.com", "/title1.html").spec());
return http_response;
}
if (absolute_url.GetPath() == "/script") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->set_content("console.log(\"Hello World\");");
http_response->set_content_type("application/javascript");
http_response->AddCustomHeader("Cache-Control", "max-age=1000");
return http_response;
}
if (absolute_url.GetPath() == "/worker.js") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
GURL resource = GenURL("3p.com", "/script");
std::string content = base::StringPrintf("importScripts('%s');\nclose();",
resource.spec().c_str());
http_response->set_content(content);
http_response->set_content_type("application/javascript");
http_response->AddCustomHeader("Cache-Control", "max-age=100000");
return http_response;
}
if (absolute_url.GetPath() == "/title1.html") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->AddCustomHeader("Cache-Control", "max-age=100000");
return http_response;
}
if (absolute_url.GetPath() == "/embedding_worker.js") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
GURL resource =
GenURL(base::StringPrintf("%s.com", absolute_url.GetQuery().c_str()),
"/worker.js");
const char kLoadWorkerScript[] = "let w = new Worker('%s');";
std::string content =
base::StringPrintf(kLoadWorkerScript, resource.spec().c_str());
http_response->set_content(content);
http_response->set_content_type("application/javascript");
http_response->AddCustomHeader("Cache-Control", "max-age=100000");
return http_response;
}
return nullptr;
}
protected:
RenderFrameHost* CreateSubframe(const GURL& sub_frame) {
EXPECT_TRUE(ExecJs(shell(), GetSubframeScript(sub_frame)));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
return static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root()
->child_at(0)
->current_frame_host();
}
bool TestResourceLoad(const GURL& url, const GURL& sub_frame) {
return TestResourceLoadHelper(url, sub_frame, GURL());
}
bool TestResourceLoadFromDedicatedWorker(const GURL& url,
const GURL& worker) {
DCHECK(worker.is_valid());
return TestResourceLoadHelper(url, GURL(), worker);
}
bool TestResourceLoadFromDedicatedWorkerInIframe(const GURL& url,
const GURL& sub_frame,
const GURL& worker) {
DCHECK(sub_frame.is_valid());
DCHECK(worker.is_valid());
return TestResourceLoadHelper(url, sub_frame, worker);
}
bool TestResourceLoadFromPopup(const GURL& url, const GURL& popup) {
DCHECK(popup.is_valid());
return TestResourceLoadHelper(url, popup, GURL(), true);
}
bool TestResourceLoadHelper(const GURL& url,
const GURL& new_frame,
const GURL& worker,
bool use_popup = false) {
DCHECK(url.is_valid());
DisableBFCacheForRFHForTesting(
shell()->web_contents()->GetPrimaryMainFrame());
EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL("blob-internals")));
if (base::StartsWith(url.GetPath(), "/redirect",
base::CompareCase::SENSITIVE)) {
EXPECT_FALSE(NavigateToURL(shell(), url));
} else {
EXPECT_TRUE(NavigateToURL(shell(), url));
}
RenderFrameHost* host_to_load_resource =
shell()->web_contents()->GetPrimaryMainFrame();
RenderFrameHostImpl* main_frame =
static_cast<RenderFrameHostImpl*>(host_to_load_resource);
Shell* shell_to_observe = shell();
if (new_frame.is_valid()) {
if (use_popup) {
shell_to_observe = OpenPopup(main_frame, new_frame, "");
host_to_load_resource =
static_cast<WebContentsImpl*>(shell_to_observe->web_contents())
->GetPrimaryMainFrame();
} else {
host_to_load_resource = CreateSubframe(new_frame);
}
}
base::RunLoop loop;
shell_to_observe->web_contents()
->GetPrimaryMainFrame()
->GetProcess()
->GetRendererInterface()
->PurgeResourceCache(loop.QuitClosure());
loop.Run();
ResourceLoadObserver observer(shell_to_observe);
GURL resource = GenURL("3p.com", "/script");
if (worker.is_valid()) {
EXPECT_TRUE(ExecJs(host_to_load_resource, GetWorkerScript(worker)));
} else {
EXPECT_TRUE(
ExecJs(host_to_load_resource, GetLoadResourceScript(resource)));
}
observer.WaitForResourceCompletion(resource);
url::Origin top_frame_origin =
main_frame->frame_tree_node()->current_origin();
RenderFrameHostImpl* frame_host =
static_cast<RenderFrameHostImpl*>(host_to_load_resource);
url::Origin frame_origin;
if (new_frame.is_empty()) {
frame_origin = top_frame_origin;
} else {
frame_origin = url::Origin::Create(new_frame);
if (use_popup && !frame_origin.opaque()) {
top_frame_origin = frame_origin;
} else {
frame_origin = frame_host->GetLastCommittedOrigin();
}
}
if (!top_frame_origin.opaque() && !frame_origin.opaque()) {
EXPECT_EQ(net::NetworkIsolationKey(net::SchemefulSite(top_frame_origin),
net::SchemefulSite(frame_origin)),
frame_host->GetNetworkIsolationKey());
} else {
EXPECT_TRUE(frame_host->GetNetworkIsolationKey().IsTransient());
}
return (*observer.GetResource(resource))->was_cached;
}
bool NavigationResourceCached(const GURL& url,
const GURL& sub_frame,
bool subframe_navigation_resource_cached) {
return NavigationResourceCached(url, url, sub_frame,
subframe_navigation_resource_cached);
}
bool NavigationResourceCached(const GURL& url,
const GURL& expected_commit_url,
const GURL& sub_frame,
bool subframe_navigation_resource_cached) {
EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL("blob-internals")));
ResourceLoadObserver observer(shell());
EXPECT_TRUE(NavigateToURL(shell(), url, expected_commit_url));
RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame());
observer.WaitForResourceCompletion(url);
if (sub_frame.is_valid()) {
EXPECT_EQ(1U, main_frame->frame_tree_node()->child_count());
NavigateFrameToURL(main_frame->frame_tree_node()->child_at(0), sub_frame);
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
observer.WaitForResourceCompletion(sub_frame);
EXPECT_EQ(subframe_navigation_resource_cached,
(*observer.GetResource(sub_frame))->was_cached);
}
return (*observer.GetResource(url))->was_cached;
}
bool DedicatedWorkerScriptCached(const GURL& url,
const GURL& sub_frame,
const GURL& worker) {
DCHECK(url.is_valid());
DCHECK(worker.is_valid());
EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL("blob-internals")));
ResourceLoadObserver observer(shell());
EXPECT_TRUE(NavigateToURL(shell(), url));
RenderFrameHost* host_to_load_resource =
shell()->web_contents()->GetPrimaryMainFrame();
if (sub_frame.is_valid()) {
host_to_load_resource = CreateSubframe(sub_frame);
}
EXPECT_TRUE(ExecJs(host_to_load_resource, GetWorkerScript(worker)));
observer.WaitForResourceCompletion(GenURL("3p.com", "/script"));
observer.WaitForResourceCompletion(worker);
return (*observer.GetResource(worker))->was_cached;
}
bool NavigationRedirectCached(const GURL& url, const GURL& redirect_url) {
EXPECT_TRUE(NavigateToURL(shell(), GetWebUIURL("blob-internals")));
EXPECT_TRUE(NavigateToURL(shell(), url));
ResourceLoadObserver observer(shell());
EXPECT_TRUE(ExecJs(shell(), GetRedirectScript(redirect_url)));
WaitForLoadStop(shell()->web_contents());
observer.WaitForResourceCompletion(redirect_url);
return (*observer.GetResource(redirect_url))->was_cached;
}
std::string GetSubframeScript(const GURL& sub_frame) {
const char kLoadIframeScript[] = R"(
let iframe = document.createElement('iframe');
iframe.src = $1;
document.body.appendChild(iframe);
)";
return JsReplace(kLoadIframeScript, sub_frame);
}
std::string GetWorkerScript(const GURL& worker) {
const char kLoadWorkerScript[] = "let w = new Worker($1);";
return JsReplace(kLoadWorkerScript, worker);
}
std::string GetLoadResourceScript(const GURL& resource) {
const char kLoadResourceScript[] = R"(
let script = document.createElement('script');
script.src = $1;
document.body.appendChild(script);
)";
return JsReplace(kLoadResourceScript, resource);
}
std::string GetRedirectScript(const GURL& location) {
const char kRedirectScript[] = R"(
window.location.href = $1;
)";
return JsReplace(kRedirectScript, location);
}
GURL GenURL(const std::string& host, const std::string& path) {
return embedded_test_server()->GetURL(host, path);
}
};
class SplitCacheEnabledContentBrowserTest
: public SplitCacheContentBrowserTestBase {
public:
SplitCacheEnabledContentBrowserTest() {
split_cache_enabled_feature_list_.InitAndEnableFeature(
net::features::kSplitCacheByNetworkIsolationKey);
}
private:
base::test::ScopedFeatureList split_cache_enabled_feature_list_;
};
class SplitCacheDisabledContentBrowserTest
: public SplitCacheContentBrowserTestBase {
public:
SplitCacheDisabledContentBrowserTest() {
split_cache_disabled_feature_list_.InitAndDisableFeature(
net::features::kSplitCacheByNetworkIsolationKey);
}
private:
base::test::ScopedFeatureList split_cache_disabled_feature_list_;
};
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest, MainFrame) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
EXPECT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
EXPECT_FALSE(TestResourceLoad(GenURL("b.com", "/title1.html"), GURL()));
EXPECT_FALSE(TestResourceLoad(GenURL("3p.com", "/title1.html"), GURL()));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest, MainFrameRedirect) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
ASSERT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/redirect_to_d"), GURL()));
EXPECT_TRUE(TestResourceLoad(GenURL("d.com", "/title1.html"), GURL()));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest, Subframe) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
ASSERT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
EXPECT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"),
GenURL("a.com", "/title1.html")));
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"),
GenURL("e.com", "/title1.html")));
EXPECT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"),
GenURL("e.com", "/title1.html")));
EXPECT_FALSE(TestResourceLoad(GenURL("e.com", "/title1.html"),
GenURL("e.com", "/title1.html")));
EXPECT_TRUE(TestResourceLoad(GenURL("e.com", "/title1.html"),
GenURL("e.com", "/title1.html")));
EXPECT_FALSE(TestResourceLoad(GenURL("f.com", "/title1.html"),
GenURL("a.com", "/title1.html")));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest, MainFrameDataUrl) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
ASSERT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
GURL data_url("data:text/html,<body>Hello World</body>");
EXPECT_FALSE(TestResourceLoad(data_url, GURL()));
EXPECT_FALSE(TestResourceLoad(data_url, GURL()));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
MainFrameAboutBlank) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
ASSERT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
GURL blank_url(url::kAboutBlankURL);
EXPECT_FALSE(TestResourceLoad(blank_url, GURL()));
EXPECT_FALSE(TestResourceLoad(blank_url, GURL()));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest, SubFrameRedirect) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
ASSERT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"),
GenURL("a.com", "/redirect_to_d")));
EXPECT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"),
GenURL("d.com", "/title1.html")));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest, SubFrameDataUrl) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
ASSERT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
GURL data_url("data:text/html,<body>Hello World</body>");
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), data_url));
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), data_url));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
SubframeAboutBlank) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
ASSERT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
GURL blank_url(url::kAboutBlankURL);
EXPECT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), blank_url));
EXPECT_TRUE(
TestResourceLoadFromPopup(GenURL("a.com", "/title1.html"), blank_url));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest, Popup) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
ASSERT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
EXPECT_FALSE(TestResourceLoadFromPopup(GenURL("a.com", "/title1.html"),
GenURL("g.com", "/title1.html")));
EXPECT_FALSE(TestResourceLoad(GenURL("a.foo.com", "/title1.html"), GURL()));
EXPECT_TRUE(TestResourceLoad(GenURL("b.foo.com", "/title1.html"), GURL()));
EXPECT_FALSE(TestResourceLoad(GenURL("b.com", "/title1.html"), GURL()));
EXPECT_FALSE(TestResourceLoad(GenURL("3p.com", "/title1.html"), GURL()));
EXPECT_TRUE(TestResourceLoad(GenURL("a.foo.com", "/title1.html"),
GenURL("iframe.foo.com", "/title1.html")));
}
IN_PROC_BROWSER_TEST_F(SplitCacheDisabledContentBrowserTest, NonSplitCache) {
EXPECT_FALSE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
EXPECT_TRUE(TestResourceLoad(GenURL("a.com", "/title1.html"), GURL()));
EXPECT_TRUE(TestResourceLoad(GenURL("b.com", "/title1.html"), GURL()));
EXPECT_TRUE(TestResourceLoad(GenURL("b.com", "/title1.html"),
GenURL("c.com", "/title1.html")));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
NavigationResources) {
EXPECT_FALSE(
NavigationResourceCached(GenURL("a.com", "/title1.html"), GURL(), false));
EXPECT_TRUE(
NavigationResourceCached(GenURL("a.com", "/title1.html"), GURL(), false));
EXPECT_FALSE(NavigationResourceCached(GenURL("a.com", "/redirect_to_d"),
GenURL("d.com", "/title1.html"), GURL(),
false));
EXPECT_TRUE(
NavigationResourceCached(GenURL("d.com", "/title1.html"), GURL(), false));
EXPECT_FALSE(NavigationResourceCached(
GenURL("a.com", "/navigation_controller/page_with_iframe.html"),
GenURL("a.com", "/title1.html"),
false));
EXPECT_TRUE(NavigationResourceCached(
GenURL("a.com", "/navigation_controller/page_with_iframe.html"),
GenURL("a.com", "/title1.html"), true));
EXPECT_FALSE(NavigationResourceCached(
GenURL("b.com", "/navigation_controller/page_with_iframe.html"),
GenURL("a.com", "/title1.html"), false));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
SubframeNavigationResources) {
NavigationResourceCached(
GenURL("a.com", "/navigation_controller/page_with_iframe.html"),
GenURL("a.com", "/title1.html"), false);
NavigationResourceCached(
GenURL("a.com", "/navigation_controller/page_with_iframe.html"),
GenURL("a.com", "/title1.html"), true);
NavigationResourceCached(
GenURL("b.com", "/navigation_controller/page_with_iframe.html"),
GenURL("a.com", "/title1.html"), false);
NavigationResourceCached(
GenURL("a.com", "/navigation_controller/page_with_iframe.html"),
GenURL("a.com", "/redirect_to_d"), false);
NavigationResourceCached(
GenURL("a.com", "/navigation_controller/page_with_iframe.html"),
GenURL("d.com", "/title1.html"), true);
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
SubframeNavigationResource) {
TestResourceLoad(GenURL("main.com", "/title1.html") ,
GenURL("3p.com", "/title1.html") );
NavigationResourceCached(
GenURL("main.com", "/navigation_controller/page_with_iframe.html"),
GenURL("evil.com", "/title1.html"), false);
RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame());
EXPECT_EQ(1U, main_frame->frame_tree_node()->child_count());
ResourceLoadObserver observer(shell());
GURL subframe_url = GenURL("3p.com", "/script");
EXPECT_TRUE(ExecJs(main_frame->frame_tree_node()->child_at(0),
GetSubframeScript(subframe_url)));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
observer.WaitForResourceCompletion(subframe_url);
EXPECT_EQ(false, (*observer.GetResource(subframe_url))->was_cached);
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
CrossSiteNavigation) {
EXPECT_FALSE(
NavigationResourceCached(GenURL("a.com", "/title1.html"), GURL(), false));
EXPECT_TRUE(
NavigationResourceCached(GenURL("a.com", "/title1.html"), GURL(), false));
bool evil_com_initiator_first_navigation_result = NavigationRedirectCached(
GenURL("evil.com", "/title1.html"), GenURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigationRedirectCached(GenURL("evil.com", "/title1.html"),
GenURL("a.com", "/title1.html")));
bool evil2_com_initiator_navigation_result = NavigationRedirectCached(
GenURL("evil2.com", "/title1.html"), GenURL("a.com", "/title1.html"));
EXPECT_FALSE(evil_com_initiator_first_navigation_result);
EXPECT_TRUE(evil2_com_initiator_navigation_result);
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
CrossOriginSameSiteNavigation) {
EXPECT_FALSE(
NavigationResourceCached(GenURL("a.com", "/title1.html"), GURL(), false));
EXPECT_TRUE(
NavigationResourceCached(GenURL("a.com", "/title1.html"), GURL(), false));
bool a_example_com_initiator_first_navigation_result =
NavigationRedirectCached(GenURL("a.example.com", "/title1.html"),
GenURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigationRedirectCached(GenURL("a.example.com", "/title1.html"),
GenURL("a.com", "/title1.html")));
bool b_example_com_initiator_navigation_result = NavigationRedirectCached(
GenURL("b.example.com", "/title1.html"), GenURL("a.com", "/title1.html"));
EXPECT_FALSE(a_example_com_initiator_first_navigation_result);
EXPECT_TRUE(b_example_com_initiator_navigation_result);
}
class SplitCacheComputeHttpCacheSize {
public:
SplitCacheComputeHttpCacheSize() = default;
~SplitCacheComputeHttpCacheSize() = default;
int64_t ComputeHttpCacheSize(BrowserContext* context,
base::Time start_time,
base::Time end_time) {
last_computed_cache_size_ = -1;
auto* network_context =
context->GetDefaultStoragePartition()->GetNetworkContext();
network::mojom::NetworkContext::ComputeHttpCacheSizeCallback size_callback =
base::BindOnce(
&SplitCacheComputeHttpCacheSize::ComputeCacheSizeCallback,
base::Unretained(this));
network_context->ComputeHttpCacheSize(start_time, end_time,
std::move(size_callback));
runloop_ = std::make_unique<base::RunLoop>();
runloop_->Run();
return last_computed_cache_size_;
}
void ComputeCacheSizeCallback(bool is_upper_bound, int64_t size) {
last_computed_cache_size_ = size;
runloop_->Quit();
}
SplitCacheComputeHttpCacheSize(const SplitCacheComputeHttpCacheSize&) =
delete;
SplitCacheComputeHttpCacheSize& operator=(
const SplitCacheComputeHttpCacheSize&) = delete;
private:
std::unique_ptr<base::RunLoop> runloop_;
int64_t last_computed_cache_size_ = -1;
};
#if BUILDFLAG(IS_WIN)
#define MAYBE_NotifyExternalCacheHitCheckSubframeBit \
DISABLED_NotifyExternalCacheHitCheckSubframeBit
#else
#define MAYBE_NotifyExternalCacheHitCheckSubframeBit \
NotifyExternalCacheHitCheckSubframeBit
#endif
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
MAYBE_NotifyExternalCacheHitCheckSubframeBit) {
ResourceLoadObserver observer(shell());
BrowserContext* context = shell()->web_contents()->GetBrowserContext();
std::unique_ptr<SplitCacheComputeHttpCacheSize> http_cache_size =
std::make_unique<SplitCacheComputeHttpCacheSize>();
EXPECT_EQ(0, http_cache_size->ComputeHttpCacheSize(context, base::Time(),
base::Time::Max()));
GURL page_url(
embedded_test_server()->GetURL("/page_with_cached_subresource.html"));
EXPECT_TRUE(NavigateToURL(shell(), page_url));
int64_t size1 = http_cache_size->ComputeHttpCacheSize(context, base::Time(),
base::Time::Max());
EXPECT_GT(size1, 0);
ASSERT_EQ(2U, observer.resource_load_entries().size());
EXPECT_TRUE(observer.memory_cached_loaded_urls().empty());
observer.Reset();
base::Time start = base::Time::Now();
while (start == base::Time::Now()) {
}
base::Time after_first = base::Time::Now();
EXPECT_TRUE(NavigateToURL(shell(), page_url));
ASSERT_EQ(1U, observer.resource_load_entries().size());
ASSERT_EQ(1U, observer.memory_cached_loaded_urls().size());
int64_t size2 = http_cache_size->ComputeHttpCacheSize(context, after_first,
base::Time::Max());
EXPECT_EQ(size1, size2);
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest, DedicatedWorkers) {
EXPECT_FALSE(TestResourceLoadFromDedicatedWorker(
GenURL("a.com", "/title1.html"), GenURL("a.com", "/worker.js")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
GenURL("a.com", "/title1.html"), GenURL("a.com", "/worker.js")));
EXPECT_FALSE(TestResourceLoadFromDedicatedWorker(
GenURL("b.com", "/title1.html"), GenURL("b.com", "/worker.js")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
GenURL("b.com", "/title1.html"), GenURL("b.com", "/worker.js")));
EXPECT_FALSE(TestResourceLoadFromDedicatedWorker(
GenURL("c.com", "/title1.html"),
GenURL("c.com", "/embedding_worker.js?c")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
GenURL("c.com", "/title1.html"),
GenURL("c.com", "/embedding_worker.js?c")));
EXPECT_FALSE(TestResourceLoadFromDedicatedWorkerInIframe(
GenURL("d.com", "/title1.html"), GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorkerInIframe(
GenURL("d.com", "/title1.html"), GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
EXPECT_FALSE(TestResourceLoadFromDedicatedWorkerInIframe(
GenURL("f.com", "/title1.html"), GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorkerInIframe(
GenURL("f.com", "/title1.html"), GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
}
IN_PROC_BROWSER_TEST_F(SplitCacheEnabledContentBrowserTest,
DISABLED_DedicatedWorkersScripts) {
EXPECT_FALSE(DedicatedWorkerScriptCached(
GenURL("a.com", "/title1.html"), GURL(), GenURL("a.com", "/worker.js")));
EXPECT_TRUE(DedicatedWorkerScriptCached(
GenURL("a.com", "/title1.html"), GURL(), GenURL("a.com", "/worker.js")));
EXPECT_FALSE(
DedicatedWorkerScriptCached(GenURL("c.com", "/title1.html"), GURL(),
GenURL("c.com", "/embedding_worker.js?c")));
EXPECT_TRUE(
DedicatedWorkerScriptCached(GenURL("c.com", "/title1.html"), GURL(),
GenURL("c.com", "/embedding_worker.js?c")));
EXPECT_FALSE(DedicatedWorkerScriptCached(GenURL("d.com", "/title1.html"),
GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
EXPECT_TRUE(DedicatedWorkerScriptCached(GenURL("d.com", "/title1.html"),
GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
EXPECT_FALSE(DedicatedWorkerScriptCached(GenURL("f.com", "/title1.html"),
GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
EXPECT_TRUE(DedicatedWorkerScriptCached(GenURL("f.com", "/title1.html"),
GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
}
IN_PROC_BROWSER_TEST_F(SplitCacheDisabledContentBrowserTest, DedicatedWorkers) {
EXPECT_FALSE(TestResourceLoadFromDedicatedWorker(
GenURL("a.com", "/title1.html"), GenURL("a.com", "/worker.js")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
GenURL("a.com", "/title1.html"), GenURL("a.com", "/worker.js")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
GenURL("b.com", "/title1.html"), GenURL("b.com", "/worker.js")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorker(
GenURL("c.com", "/title1.html"),
GenURL("c.com", "/embedding_worker.js?c")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorkerInIframe(
GenURL("d.com", "/title1.html"), GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
EXPECT_TRUE(TestResourceLoadFromDedicatedWorkerInIframe(
GenURL("f.com", "/title1.html"), GenURL("e.com", "/title1.html"),
GenURL("e.com", "/worker.js")));
}
class SplitCacheByIncludeCredentialsTest : public ContentBrowserTest {
public:
SplitCacheByIncludeCredentialsTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
feature_list_.InitAndEnableFeature(
net::features::kSplitCacheByIncludeCredentials);
}
int cacheable_request_count() const { return cacheable_request_count_; }
net::EmbeddedTestServer* https_server() { return &https_server_; }
GURL CacheableUrl() { return https_server()->GetURL("b.test", "/cacheable"); }
void RequestAnonymous(Shell* shell) {
EXPECT_TRUE(ExecJs(shell, JsReplace(R"(
new Promise(resolve => {
const image = new Image();
image.src = $1;
image.crossOrigin = "anonymous";
image.onload = resolve;
document.body.appendChild(image);
});
)",
CacheableUrl())));
}
void RequestUseCredentials(Shell* shell) {
EXPECT_TRUE(ExecJs(shell, JsReplace(R"(
new Promise(resolve => {
const image = new Image();
image.src = $1;
image.crossOrigin = "use-credentials";
image.onload = resolve;
document.body.appendChild(image);
});
)",
CacheableUrl())));
}
private:
void SetUpOnMainThread() final {
ContentBrowserTest::SetUpOnMainThread();
cacheable_request_count_ = 0;
host_resolver()->AddRule("*", "127.0.0.1");
https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server()->RegisterRequestHandler(
base::BindRepeating(&SplitCacheByIncludeCredentialsTest::RequestHandler,
base::Unretained(this)));
https_server()->ServeFilesFromSourceDirectory(GetTestDataFilePath());
net::test_server::RegisterDefaultHandlers(https_server());
SetupCrossSiteRedirector(https_server());
CHECK(https_server()->Start());
}
std::unique_ptr<net::test_server::HttpResponse> RequestHandler(
const net::test_server::HttpRequest& request) {
if (request.relative_url == "/cacheable") {
cacheable_request_count_++;
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content_type("image/svg+xml");
response->set_content("<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>");
response->AddCustomHeader("Cache-Control", "max-age=3600");
response->AddCustomHeader("Cross-Origin-Resource-Policy", "cross-origin");
response->AddCustomHeader("Access-Control-Allow-Credentials", "true");
response->AddCustomHeader(
"Access-Control-Allow-Origin",
https_server()->GetOrigin("a.test").Serialize());
return response;
}
return nullptr;
}
base::test::ScopedFeatureList feature_list_;
net::EmbeddedTestServer https_server_;
std::atomic<int> cacheable_request_count_;
};
IN_PROC_BROWSER_TEST_F(SplitCacheByIncludeCredentialsTest, SameProcess) {
GURL page_url(https_server()->GetURL("a.test", "/empty.html"));
EXPECT_TRUE(NavigateToURL(shell(), page_url));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_EQ(0, cacheable_request_count());
RequestAnonymous(shell());
EXPECT_EQ(1, cacheable_request_count());
RequestAnonymous(shell());
EXPECT_EQ(1, cacheable_request_count());
RequestUseCredentials(shell());
EXPECT_EQ(2, cacheable_request_count());
RequestUseCredentials(shell());
EXPECT_EQ(2, cacheable_request_count());
RequestAnonymous(shell());
EXPECT_EQ(2, cacheable_request_count());
}
IN_PROC_BROWSER_TEST_F(SplitCacheByIncludeCredentialsTest, SameProcessVariant) {
GURL page_url(https_server()->GetURL("a.test", "/empty.html"));
EXPECT_TRUE(NavigateToURL(shell(), page_url));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_EQ(0, cacheable_request_count());
RequestUseCredentials(shell());
EXPECT_EQ(1, cacheable_request_count());
RequestUseCredentials(shell());
EXPECT_EQ(1, cacheable_request_count());
RequestAnonymous(shell());
EXPECT_EQ(2, cacheable_request_count());
RequestAnonymous(shell());
EXPECT_EQ(2, cacheable_request_count());
RequestUseCredentials(shell());
EXPECT_EQ(2, cacheable_request_count());
}
IN_PROC_BROWSER_TEST_F(SplitCacheByIncludeCredentialsTest, DifferentProcess) {
GURL page_1_url(https_server()->GetURL("a.test", "/empty.html"));
EXPECT_TRUE(NavigateToURL(shell(), page_1_url));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
GURL page_2_url =
https_server()->GetURL("a.test",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
ShellAddedObserver shell_observer;
EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1)", page_2_url)));
Shell* new_shell = shell_observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(new_shell->web_contents()));
EXPECT_NE(static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryMainFrame()
->GetProcess(),
static_cast<WebContentsImpl*>(new_shell->web_contents())
->GetPrimaryMainFrame()
->GetProcess());
EXPECT_EQ(0, cacheable_request_count());
RequestAnonymous(shell());
EXPECT_EQ(1, cacheable_request_count());
RequestAnonymous(new_shell);
EXPECT_EQ(1, cacheable_request_count());
RequestUseCredentials(shell());
EXPECT_EQ(2, cacheable_request_count());
RequestUseCredentials(new_shell);
EXPECT_EQ(2, cacheable_request_count());
RequestAnonymous(shell());
EXPECT_EQ(2, cacheable_request_count());
}
IN_PROC_BROWSER_TEST_F(SplitCacheByIncludeCredentialsTest,
DifferentProcessVariant) {
GURL page_1_url(https_server()->GetURL("a.test", "/empty.html"));
EXPECT_TRUE(NavigateToURL(shell(), page_1_url));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
GURL page_2_url =
https_server()->GetURL("a.test",
"/set-header?"
"Cross-Origin-Opener-Policy: same-origin&"
"Cross-Origin-Embedder-Policy: require-corp");
ShellAddedObserver shell_observer;
EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1)", page_2_url)));
Shell* new_shell = shell_observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(new_shell->web_contents()));
EXPECT_NE(static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryMainFrame()
->GetProcess(),
static_cast<WebContentsImpl*>(new_shell->web_contents())
->GetPrimaryMainFrame()
->GetProcess());
EXPECT_EQ(0, cacheable_request_count());
RequestUseCredentials(shell());
EXPECT_EQ(1, cacheable_request_count());
RequestUseCredentials(new_shell);
EXPECT_EQ(1, cacheable_request_count());
RequestAnonymous(shell());
EXPECT_EQ(2, cacheable_request_count());
RequestAnonymous(new_shell);
EXPECT_EQ(2, cacheable_request_count());
RequestUseCredentials(shell());
EXPECT_EQ(2, cacheable_request_count());
}
}
}