#include <memory>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "content/browser/download/download_manager_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_util.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.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_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "net/base/filename_util.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_status_code.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/url_request/url_request_failed_job.h"
#include "net/test/url_request/url_request_mock_http_job.h"
#include "services/network/public/cpp/features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
#include "url/gurl.h"
using base::ASCIIToUTF16;
using testing::HasSubstr;
using testing::Not;
namespace content {
class LoaderBrowserTest : public ContentBrowserTest,
public DownloadManager::Observer {
public:
LoaderBrowserTest() : got_downloads_(false) {}
protected:
void SetUpOnMainThread() override {
base::FilePath path = GetTestFilePath("", "");
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&net::URLRequestMockHTTPJob::AddUrlHandlers, path));
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler));
host_resolver()->AddRule("*", "127.0.0.1");
}
void CheckTitleTest(const GURL& url, const std::string& expected_title) {
std::u16string expected_title16(ASCIIToUTF16(expected_title));
TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
}
bool GetPopupTitle(const GURL& url, std::u16string* title) {
EXPECT_TRUE(NavigateToURL(shell(), url));
ShellAddedObserver new_shell_observer;
if (!ExecJs(shell(), "OpenPopup();")) {
return false;
}
Shell* new_shell = new_shell_observer.GetShell();
*title = new_shell->web_contents()->GetTitle();
return true;
}
std::string GetCookies(const GURL& url) {
return content::GetCookies(shell()->web_contents()->GetBrowserContext(),
url);
}
bool got_downloads() const { return got_downloads_; }
private:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
"cors_exempt_header_list", "ExemptFoo");
ContentBrowserTest::SetUp();
}
bool got_downloads_;
};
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, DynamicTitle1) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/dynamic1.html"));
std::u16string title;
ASSERT_TRUE(GetPopupTitle(url, &title));
EXPECT_TRUE(
base::StartsWith(title, u"My Popup Title", base::CompareCase::SENSITIVE))
<< "Actual title: " << title;
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, DynamicTitle2) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/dynamic2.html"));
std::u16string title;
ASSERT_TRUE(GetPopupTitle(url, &title));
EXPECT_TRUE(base::StartsWith(title, u"My Dynamic Title",
base::CompareCase::SENSITIVE))
<< "Actual title: " << title;
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest,
DisableReferrersStaleWhileRevalidate) {
ASSERT_TRUE(embedded_test_server()->Start());
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
ASSERT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/stale-while-revalidate.html")));
web_contents->GetMutableRendererPrefs()->enable_referrers = false;
web_contents->SyncRendererPrefs();
std::u16string expected_title = u"Pass";
TitleWatcher title_watcher(web_contents, expected_title);
EXPECT_TRUE(ExecJs(shell(), "runTest()"));
ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, SniffNoContentTypeNoData) {
shell()
->web_contents()
->GetBrowserContext()
->GetDownloadManager()
->AddObserver(this);
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/content-sniffer-test3.html"));
CheckTitleTest(url, "Content Sniffer Test 3");
EXPECT_EQ(1u, Shell::windows().size());
ASSERT_FALSE(got_downloads());
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, DoNotSniffHTMLFromFileUrl) {
base::FilePath path =
GetTestFilePath(nullptr, "content-sniffer-test5.not-html");
GURL file_url = net::FilePathToFileURL(path);
CheckTitleTest(file_url, path.BaseName().MaybeAsASCII());
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, ContentDispositionEmpty) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/content-disposition-empty.html"));
CheckTitleTest(url, "success");
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, ContentDispositionInline) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/content-disposition-inline.html"));
CheckTitleTest(url, "success");
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, SyncXMLHttpRequest) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/sync_xmlhttprequest.html")));
EXPECT_EQ(true, EvalJs(shell(), "DidSyncRequestSucceed();"));
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, SyncXMLHttpRequest_Disallowed) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(),
embedded_test_server()->GetURL("/sync_xmlhttprequest_disallowed.html")));
EXPECT_EQ(true, EvalJs(shell(), "DidSucceed();"));
}
#if BUILDFLAG(IS_MAC)
#define MAYBE_SyncXMLHttpRequest_DuringUnload \
DISABLED_SyncXMLHttpRequest_DuringUnload
#else
#define MAYBE_SyncXMLHttpRequest_DuringUnload SyncXMLHttpRequest_DuringUnload
#endif
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest,
MAYBE_SyncXMLHttpRequest_DuringUnload) {
ASSERT_TRUE(embedded_test_server()->Start());
shell()
->web_contents()
->GetBrowserContext()
->GetDownloadManager()
->AddObserver(this);
CheckTitleTest(
embedded_test_server()->GetURL("/sync_xmlhttprequest_during_unload.html"),
"sync xhr on unload");
CheckTitleTest(embedded_test_server()->GetURL("/title2.html"),
"Title Of Awesomeness");
ASSERT_FALSE(got_downloads());
}
namespace {
std::unique_ptr<net::test_server::HttpResponse> CancelOnRequest(
const std::string& relative_url,
int child_id,
base::RepeatingClosure crash_network_service_callback,
const net::test_server::HttpRequest& request) {
if (request.relative_url != relative_url) {
return nullptr;
}
GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
crash_network_service_callback);
return std::make_unique<net::test_server::HungResponse>();
}
}
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_SyncXMLHttpRequest_Cancelled DISABLED_SyncXMLHttpRequest_Cancelled
#else
#define MAYBE_SyncXMLHttpRequest_Cancelled SyncXMLHttpRequest_Cancelled
#endif
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, MAYBE_SyncXMLHttpRequest_Cancelled) {
if (IsInProcessNetworkService()) {
return;
}
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CancelOnRequest, "/hung",
shell()
->web_contents()
->GetPrimaryMainFrame()
->GetProcess()
->GetDeprecatedID(),
base::BindRepeating(&BrowserTestBase::SimulateNetworkServiceCrash,
base::Unretained(this))));
ASSERT_TRUE(embedded_test_server()->Start());
WaitForLoadStop(shell()->web_contents());
EXPECT_TRUE(NavigateToURL(
shell(),
embedded_test_server()->GetURL("/sync_xmlhttprequest_cancelled.html")));
EXPECT_EQ(19, EvalJs(shell(), "getErrorCode();"));
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, DISABLED_CrossSiteOnunloadCookie) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/onunload_cookie.html");
CheckTitleTest(url, "set cookie on unload");
CheckTitleTest(
net::URLRequestMockHTTPJob::GetMockUrl("content-sniffer-test0.html"),
"Content Sniffer Test 0");
EXPECT_EQ("onunloadCookie=foo", GetCookies(url));
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest,
DISABLED_CrossSiteImmediateLoadOnunloadCookie) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/onunload_cookie.html");
CheckTitleTest(url, "set cookie on unload");
EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
EXPECT_EQ("onunloadCookie=foo", GetCookies(url));
}
namespace {
std::unique_ptr<net::test_server::HttpResponse> NoContentResponseHandler(
const std::string& path,
const net::test_server::HttpRequest& request) {
if (!base::StartsWith(path, request.relative_url,
base::CompareCase::SENSITIVE)) {
return nullptr;
}
std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
new net::test_server::BasicHttpResponse);
http_response->set_code(net::HTTP_NO_CONTENT);
return std::move(http_response);
}
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, CrossSiteNoUnloadOn204) {
const char kNoContentPath[] = "/nocontent";
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&NoContentResponseHandler, kNoContentPath));
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/onunload_cookie.html");
CheckTitleTest(url, "set cookie on unload");
EXPECT_TRUE(NavigateToURLAndExpectNoCommit(
shell(), embedded_test_server()->GetURL(kNoContentPath)));
EXPECT_EQ("", GetCookies(url));
}
#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_WIN) && !defined(NDEBUG))
#define MAYBE_CrossSiteAfterCrash DISABLED_CrossSiteAfterCrash
#else
#define MAYBE_CrossSiteAfterCrash CrossSiteAfterCrash
#endif
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, MAYBE_CrossSiteAfterCrash) {
EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
RenderProcessHostWatcher crash_observer(
shell()->web_contents(),
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
EXPECT_FALSE(NavigateToURL(shell(), GURL(blink::kChromeUICrashURL)));
crash_observer.Wait();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/content-sniffer-test0.html"));
CheckTitleTest(url, "Content Sniffer Test 0");
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, CrossSiteNavigationNonBuffered) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url1(embedded_test_server()->GetURL("/content-sniffer-test0.html"));
CheckTitleTest(url1, "Content Sniffer Test 0");
GURL url2 = GetTestUrl("", "title2.html");
CheckTitleTest(url2, "Title Of Awesomeness");
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest,
DISABLED_CrossSiteNavigationErrorPage) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/onunload_cookie.html"));
CheckTitleTest(url, "set cookie on unload");
GURL failed_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
std::unique_ptr<URLLoaderInterceptor> url_interceptor =
URLLoaderInterceptor::SetupRequestFailForURL(failed_url,
net::ERR_NAME_NOT_RESOLVED);
EXPECT_FALSE(NavigateToURL(shell(), failed_url));
EXPECT_NE(u"set cookie on unload", shell()->web_contents()->GetTitle());
EXPECT_EQ("onunloadCookie=foo", GetCookies(url));
std::u16string expected_title16(u"Title Of Awesomeness");
TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
GURL test_url(embedded_test_server()->GetURL("/title2.html"));
std::string redirect_script =
"window.location='" + test_url.possibly_invalid_spec() + "';" + "true;";
EXPECT_EQ(true, EvalJs(shell(), redirect_script));
EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, CrossSiteNavigationErrorPage2) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title2.html"));
CheckTitleTest(url, "Title Of Awesomeness");
GURL failed_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
std::unique_ptr<URLLoaderInterceptor> url_interceptor =
URLLoaderInterceptor::SetupRequestFailForURL(failed_url,
net::ERR_NAME_NOT_RESOLVED);
EXPECT_FALSE(NavigateToURL(shell(), failed_url));
EXPECT_NE(u"Title Of Awesomeness", shell()->web_contents()->GetTitle());
EXPECT_FALSE(NavigateToURL(shell(), failed_url));
EXPECT_NE(u"Title Of Awesomeness", shell()->web_contents()->GetTitle());
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, CrossOriginRedirectBlocked) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(
embedded_test_server()->GetURL("/cross-origin-redirect-blocked.html"));
CheckTitleTest(url, "Title Of More Awesomeness");
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, CrossSiteFailedRequest) {
EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "simple_page.html")));
GURL broken_url("chrome://theme");
EXPECT_FALSE(NavigateToURL(shell(), broken_url));
}
namespace {
std::unique_ptr<net::test_server::HttpResponse> HandleRedirectRequest(
const std::string& request_path,
const net::test_server::HttpRequest& request) {
if (!base::StartsWith(request.relative_url, request_path,
base::CompareCase::SENSITIVE)) {
return nullptr;
}
std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
new net::test_server::BasicHttpResponse);
http_response->set_code(net::HTTP_FOUND);
http_response->AddCustomHeader(
"Location", request.relative_url.substr(request_path.length()));
return std::move(http_response);
}
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, CookiePolicy) {
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&HandleRedirectRequest, "/redirect?"));
ASSERT_TRUE(embedded_test_server()->Start());
std::string set_cookie_url(base::StringPrintf(
"http://localhost:%u/set_cookie.html", embedded_test_server()->port()));
GURL url(embedded_test_server()->GetURL("/redirect?" + set_cookie_url));
std::u16string expected_title16(u"cookie set");
TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
EXPECT_TRUE(NavigateToURL(shell(), url,
GURL(set_cookie_url) ));
EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, PageTransitionClientRedirect) {
ASSERT_TRUE(embedded_test_server()->Start());
NavigateToURLBlockUntilNavigationsComplete(
shell(), embedded_test_server()->GetURL("/client_redirect.html"), 2);
NavigationEntry* entry =
shell()->web_contents()->GetController().GetLastCommittedEntry();
EXPECT_TRUE(entry->GetTransitionType() & ui::PAGE_TRANSITION_CLIENT_REDIRECT);
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, SubresourceRedirectToDataURLBlocked) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL("/echo")));
GURL subresource_url = embedded_test_server()->GetURL(
"/server-redirect?data:text/plain,redirected1");
std::string script = R"((url => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
return new Promise(resolve => {
xhr.onload = () => resolve("ALLOWED");
xhr.onerror = () => resolve("BLOCKED");
xhr.send();
});
}))";
EXPECT_EQ("BLOCKED",
EvalJs(shell(), script + "('" + subresource_url.spec() + "')"));
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, RedirectToDataURLBlocked) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_FALSE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"/server-redirect?data:text/plain,redirected1")));
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, RedirectToAboutURLBlocked) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_FALSE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"/server-redirect?" + std::string(url::kAboutBlankURL))));
}
namespace {
GURL CreateFileSystemURL(Shell* window) {
std::string filesystem_url_string = EvalJs(window, R"(
var blob = new Blob(['<html><body>hello</body></html>'],
{type: 'text/html'});
new Promise(resolve => {
window.webkitRequestFileSystem(TEMPORARY, blob.size, fs => {
fs.root.getFile('foo.html', {create: true}, file => {
file.createWriter(writer => {
writer.write(blob);
writer.onwriteend = () => {
resolve(file.toURL());
}
});
});
});
});)")
.ExtractString();
GURL filesystem_url(filesystem_url_string);
EXPECT_TRUE(filesystem_url.is_valid());
EXPECT_TRUE(filesystem_url.SchemeIsFileSystem());
return filesystem_url;
}
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest,
SubresourceRedirectToFileSystemURLBlocked) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL("/echo")));
GURL subresource_url = embedded_test_server()->GetURL(
"/server-redirect?" + CreateFileSystemURL(shell()).spec());
std::string script = R"((url => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
return new Promise(resolve => {
xhr.onload = () => resolve("ALLOWED");
xhr.onerror = () => resolve("BLOCKED");
xhr.send();
});
}))";
EXPECT_EQ("BLOCKED",
EvalJs(shell(), script + "('" + subresource_url.spec() + "')"));
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, RedirectToFileSystemURLBlocked) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL("/echo")));
EXPECT_FALSE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"/server-redirect?" + CreateFileSystemURL(shell()).spec())));
}
namespace {
struct RequestData {
const GURL url;
const net::SiteForCookies site_for_cookies;
const std::optional<url::Origin> initiator;
const int load_flags;
const std::string referrer;
RequestData(const GURL& url,
const net::SiteForCookies& site_for_cookies,
const std::optional<url::Origin>& initiator,
int load_flags,
const std::string& referrer)
: url(url),
site_for_cookies(site_for_cookies),
initiator(initiator),
load_flags(load_flags),
referrer(referrer) {}
};
}
class RequestDataBrowserTest : public ContentBrowserTest {
public:
RequestDataBrowserTest()
: interceptor_(std::make_unique<content::URLLoaderInterceptor>(
base::BindRepeating(&RequestDataBrowserTest::OnRequest,
base::Unretained(this)))) {}
~RequestDataBrowserTest() override {}
std::vector<RequestData> data() {
base::AutoLock auto_lock(requests_lock_);
auto copy = requests_;
return copy;
}
void WaitForRequests(size_t count) {
while (true) {
base::RunLoop run_loop;
{
base::AutoLock auto_lock(requests_lock_);
if (requests_.size() == count) {
return;
}
requests_closure_ = run_loop.QuitClosure();
}
run_loop.Run();
}
}
private:
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
host_resolver()->AddRule("*", "127.0.0.1");
}
void TearDownOnMainThread() override { interceptor_.reset(); }
bool OnRequest(URLLoaderInterceptor::RequestParams* params) {
RequestCreated(RequestData(
params->url_request.url, params->url_request.site_for_cookies,
params->url_request.request_initiator, params->url_request.load_flags,
params->url_request.referrer.spec()));
return false;
}
void RequestCreated(RequestData data) {
base::AutoLock auto_lock(requests_lock_);
requests_.push_back(data);
if (requests_closure_) {
std::move(requests_closure_).Run();
}
}
base::Lock requests_lock_;
std::vector<RequestData> requests_;
base::OnceClosure requests_closure_;
std::unique_ptr<URLLoaderInterceptor> interceptor_;
};
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, Basic) {
GURL top_url(embedded_test_server()->GetURL("/page_with_subresources.html"));
url::Origin top_origin = url::Origin::Create(top_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
WaitForRequests(8u);
auto requests = data();
EXPECT_EQ(8u, requests.size());
const RequestData* first_request = &requests[0];
EXPECT_TRUE(first_request->site_for_cookies.IsEquivalent(
net::SiteForCookies::FromUrl(top_url)));
EXPECT_FALSE(first_request->initiator.has_value());
for (size_t i = 1; i < requests.size(); i++) {
const RequestData* request = &requests[i];
EXPECT_TRUE(request->site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
ASSERT_TRUE(request->initiator.has_value());
EXPECT_EQ(top_origin, request->initiator);
}
}
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, LinkRelPrefetch) {
GURL top_url(embedded_test_server()->GetURL("/link_rel_prefetch.html"));
url::Origin top_origin = url::Origin::Create(top_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
WaitForRequests(2u);
auto requests = data();
EXPECT_EQ(2u, requests.size());
auto* request = &requests[1];
EXPECT_EQ(top_origin, request->initiator);
EXPECT_EQ(top_url, request->referrer);
EXPECT_TRUE(request->load_flags & net::LOAD_PREFETCH);
}
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, LinkRelPrefetchReferrerPolicy) {
GURL top_url(embedded_test_server()->GetURL(
"/link_rel_prefetch_referrer_policy.html"));
GURL img_url(embedded_test_server()->GetURL("/image.jpg"));
url::Origin top_origin = url::Origin::Create(top_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
WaitForRequests(2u);
auto requests = data();
EXPECT_EQ(2u, requests.size());
auto* main_frame_request = &requests[0];
auto* image_request = &requests[1];
EXPECT_EQ(top_url, main_frame_request->url);
EXPECT_FALSE(main_frame_request->initiator.has_value());
EXPECT_EQ(img_url, image_request->url);
EXPECT_TRUE(image_request->initiator.has_value());
EXPECT_EQ(top_origin, image_request->initiator);
EXPECT_EQ(top_url.DeprecatedGetOriginAsURL().spec(), image_request->referrer);
EXPECT_TRUE(image_request->load_flags & net::LOAD_PREFETCH);
}
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, DISABLED_BasicCrossSite) {
GURL top_url(embedded_test_server()->GetURL(
"a.com", "/nested_page_with_subresources.html"));
GURL nested_url(embedded_test_server()->GetURL(
"not-a.com", "/page_with_subresources.html"));
url::Origin top_origin = url::Origin::Create(top_url);
url::Origin nested_origin = url::Origin::Create(nested_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
auto requests = data();
EXPECT_EQ(9u, requests.size());
EXPECT_EQ(top_url, requests[0].url);
EXPECT_TRUE(requests[0].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_FALSE(requests[0].initiator.has_value());
EXPECT_EQ(nested_url, requests[1].url);
EXPECT_TRUE(requests[1].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_EQ(top_origin, requests[1].initiator);
for (size_t i = 2; i < requests.size(); i++) {
SCOPED_TRACE(requests[i].url);
EXPECT_TRUE(requests[i].site_for_cookies.IsNull());
EXPECT_EQ(nested_origin, requests[i].initiator);
}
}
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, SameOriginNested) {
GURL top_url(embedded_test_server()->GetURL("/page_with_iframe.html"));
GURL image_url(embedded_test_server()->GetURL("/image.jpg"));
GURL nested_url(embedded_test_server()->GetURL("/title1.html"));
url::Origin top_origin = url::Origin::Create(top_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
WaitForRequests(3u);
auto requests = data();
EXPECT_EQ(3u, requests.size());
EXPECT_EQ(top_url, requests[0].url);
EXPECT_TRUE(requests[0].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_FALSE(requests[0].initiator.has_value());
EXPECT_EQ(image_url, requests[1].url);
EXPECT_TRUE(requests[1].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_EQ(top_origin, requests[1].initiator);
EXPECT_EQ(nested_url, requests[2].url);
EXPECT_TRUE(requests[2].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_EQ(top_origin, requests[2].initiator);
}
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, SameOriginAuxiliary) {
GURL top_url(embedded_test_server()->GetURL("/simple_links.html"));
GURL auxiliary_url(embedded_test_server()->GetURL("/title2.html"));
url::Origin top_origin = url::Origin::Create(top_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
ShellAddedObserver new_shell_observer;
EXPECT_EQ(true, EvalJs(shell(), "clickSameSiteNewWindowLink();"));
Shell* new_shell = new_shell_observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(new_shell->web_contents()));
WaitForRequests(2u);
auto requests = data();
EXPECT_EQ(2u, requests.size());
EXPECT_EQ(top_url, requests[0].url);
EXPECT_TRUE(requests[0].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_FALSE(requests[0].initiator.has_value());
EXPECT_EQ(auxiliary_url, requests[1].url);
EXPECT_TRUE(requests[1].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromUrl(auxiliary_url)));
EXPECT_EQ(top_origin, requests[1].initiator);
}
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, CrossOriginAuxiliary) {
GURL top_url(embedded_test_server()->GetURL("/simple_links.html"));
GURL auxiliary_url(embedded_test_server()->GetURL("foo.com", "/title2.html"));
url::Origin top_origin = url::Origin::Create(top_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
const char kReplacePortNumber[] = "setPortNumber(%d);";
uint16_t port_number = embedded_test_server()->port();
EXPECT_TRUE(
ExecJs(shell(), base::StringPrintf(kReplacePortNumber, port_number)));
ShellAddedObserver new_shell_observer;
EXPECT_EQ(true, EvalJs(shell(), "clickCrossSiteNewWindowLink();"));
Shell* new_shell = new_shell_observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(new_shell->web_contents()));
auto requests = data();
EXPECT_EQ(2u, requests.size());
EXPECT_EQ(top_url, requests[0].url);
EXPECT_TRUE(requests[0].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_FALSE(requests[0].initiator.has_value());
EXPECT_EQ(auxiliary_url, requests[1].url);
EXPECT_TRUE(requests[1].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromUrl(auxiliary_url)));
EXPECT_EQ(top_origin, requests[1].initiator);
}
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, FailedNavigation) {
GURL top_url(embedded_test_server()->GetURL("a.com", "/simple_page.html"));
url::Origin top_origin = url::Origin::Create(top_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
auto requests = data();
EXPECT_EQ(1u, requests.size());
EXPECT_EQ(top_url, requests[0].url);
EXPECT_TRUE(requests[0].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_FALSE(requests[0].initiator.has_value());
}
IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, CrossOriginNested) {
GURL top_url(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
GURL top_js_url(
embedded_test_server()->GetURL("a.com", "/tree_parser_util.js"));
GURL nested_url(embedded_test_server()->GetURL(
"b.com", "/cross_site_iframe_factory.html?b()"));
GURL nested_js_url(
embedded_test_server()->GetURL("b.com", "/tree_parser_util.js"));
url::Origin top_origin = url::Origin::Create(top_url);
url::Origin nested_origin = url::Origin::Create(nested_url);
NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1);
WaitForRequests(4u);
auto requests = data();
EXPECT_EQ(4u, requests.size());
EXPECT_EQ(top_url, requests[0].url);
EXPECT_TRUE(requests[0].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_FALSE(requests[0].initiator.has_value());
EXPECT_EQ(top_js_url, requests[1].url);
EXPECT_TRUE(requests[1].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_EQ(top_origin, requests[1].initiator);
EXPECT_EQ(nested_url, requests[2].url);
EXPECT_TRUE(requests[2].site_for_cookies.IsEquivalent(
net::SiteForCookies::FromOrigin(top_origin)));
EXPECT_EQ(top_origin, requests[2].initiator);
EXPECT_EQ(nested_js_url, requests[3].url);
EXPECT_TRUE(requests[3].site_for_cookies.IsNull());
EXPECT_EQ(nested_origin, requests[3].initiator);
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest,
CookieSameSiteStrictOpenNewNamedWindowTwice) {
ASSERT_TRUE(embedded_test_server()->Start());
BrowserContext* context = shell()->web_contents()->GetBrowserContext();
GURL a_url("http://a.com");
EXPECT_TRUE(SetCookie(context, a_url, "cookie_A=A; SameSite=Strict;"));
EXPECT_TRUE(SetCookie(context, a_url, "cookie_B=B"));
EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
"malicious.com", "/title1.html")));
GURL echoall_url = embedded_test_server()->GetURL("a.com", "/echoall");
std::string script = base::StringPrintf("window.open('%s', 'named_frame');",
echoall_url.spec().c_str());
{
TestNavigationObserver new_tab_observer(shell()->web_contents(), 1);
new_tab_observer.StartWatchingNewWebContents();
EXPECT_TRUE(ExecJs(shell(), script));
new_tab_observer.Wait();
ASSERT_EQ(2u, Shell::windows().size());
Shell* new_shell = Shell::windows()[1];
EXPECT_TRUE(WaitForLoadStop(new_shell->web_contents()));
std::string html_content =
EvalJs(new_shell, "document.body.textContent").ExtractString();
EXPECT_THAT(html_content.c_str(), Not(HasSubstr("cookie_A=A")));
EXPECT_THAT(html_content.c_str(), HasSubstr("cookie_B=B"));
}
{
Shell* new_shell = Shell::windows()[1];
TestNavigationObserver new_tab_observer(new_shell->web_contents(), 1);
EXPECT_TRUE(ExecJs(shell(), script));
new_tab_observer.Wait();
ASSERT_EQ(2u, Shell::windows().size());
EXPECT_TRUE(WaitForLoadStop(new_shell->web_contents()));
std::string html_content =
EvalJs(new_shell, "document.body.textContent").ExtractString();
EXPECT_THAT(html_content.c_str(), Not(HasSubstr("cookie_A=A")));
EXPECT_THAT(html_content.c_str(), HasSubstr("cookie_B=B"));
}
}
class URLModifyingThrottle : public blink::URLLoaderThrottle {
public:
URLModifyingThrottle(bool modify_start, bool modify_redirect)
: modify_start_(modify_start), modify_redirect_(modify_redirect) {}
URLModifyingThrottle(const URLModifyingThrottle&) = delete;
URLModifyingThrottle& operator=(const URLModifyingThrottle&) = delete;
~URLModifyingThrottle() override = default;
void WillStartRequest(network::ResourceRequest* request,
bool* defer) override {
if (!modify_start_) {
return;
}
GURL::Replacements replacements;
replacements.SetQueryStr("foo=bar");
request->url = request->url.ReplaceComponents(replacements);
request->headers.SetHeader("Foo", "BarRequest");
request->cors_exempt_headers.SetHeader("ExemptFoo", "ExemptBarRequest");
}
void WillRedirectRequest(
net::RedirectInfo* redirect_info,
const network::mojom::URLResponseHead& response_head,
bool* defer,
std::vector<std::string>* to_be_removed_request_headers,
net::HttpRequestHeaders* modified_request_headers,
net::HttpRequestHeaders* modified_cors_exempt_request_headers) override {
if (!modify_redirect_) {
return;
}
modified_request_headers->SetHeader("Foo", "BarRedirect");
modified_cors_exempt_request_headers->SetHeader("ExemptFoo",
"ExemptBarRedirect");
if (modified_redirect_url_) {
return;
}
modified_redirect_url_ = true;
GURL::Replacements replacements;
replacements.SetQueryStr("foo=bar");
redirect_info->new_url =
redirect_info->new_url.ReplaceComponents(replacements);
}
private:
bool modify_start_;
bool modify_redirect_;
bool modified_redirect_url_ = false;
};
class ThrottleContentBrowserClient
: public ContentBrowserTestContentBrowserClient {
public:
ThrottleContentBrowserClient(bool modify_start, bool modify_redirect)
: modify_start_(modify_start), modify_redirect_(modify_redirect) {}
ThrottleContentBrowserClient(const ThrottleContentBrowserClient&) = delete;
ThrottleContentBrowserClient& operator=(const ThrottleContentBrowserClient&) =
delete;
~ThrottleContentBrowserClient() override {}
std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
CreateURLLoaderThrottles(
const network::ResourceRequest& request,
BrowserContext* browser_context,
const base::RepeatingCallback<WebContents*()>& wc_getter,
NavigationUIData* navigation_ui_data,
FrameTreeNodeId frame_tree_node_id,
std::optional<int64_t> navigation_id
#if BUILDFLAG(ARKWEB_NETWORK_LOAD)
, bool is_prerendering
#endif
) override {
std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
auto throttle =
std::make_unique<URLModifyingThrottle>(modify_start_, modify_redirect_);
throttles.push_back(std::move(throttle));
return throttles;
}
private:
bool modify_start_;
bool modify_redirect_;
};
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, URLLoaderThrottleStartModify) {
base::Lock lock;
ThrottleContentBrowserClient content_browser_client(true, false);
std::set<GURL> urls_requested;
std::map<GURL, net::test_server::HttpRequest::HeaderMap> header_map;
embedded_test_server()->RegisterRequestMonitor(base::BindLambdaForTesting(
[&](const net::test_server::HttpRequest& request) {
base::AutoLock auto_lock(lock);
urls_requested.insert(request.GetURL());
header_map[request.GetURL()] = request.headers;
}));
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/simple_page.html");
GURL expected_url(url.spec() + "?foo=bar");
EXPECT_TRUE(
NavigateToURL(shell(), url, expected_url ));
{
base::AutoLock auto_lock(lock);
ASSERT_TRUE(urls_requested.find(expected_url) != urls_requested.end());
ASSERT_TRUE(header_map[expected_url]["Foo"] == "BarRequest");
ASSERT_TRUE(header_map[expected_url]["ExemptFoo"] == "ExemptBarRequest");
}
}
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, URLLoaderThrottleRedirectModify) {
base::Lock lock;
ThrottleContentBrowserClient content_browser_client(false, true);
std::set<GURL> urls_requested;
std::map<GURL, net::test_server::HttpRequest::HeaderMap> header_map;
embedded_test_server()->RegisterRequestMonitor(base::BindLambdaForTesting(
[&](const net::test_server::HttpRequest& request) {
base::AutoLock auto_lock(lock);
urls_requested.insert(request.GetURL());
header_map[request.GetURL()] = request.headers;
}));
ASSERT_TRUE(embedded_test_server()->Start());
GURL url =
embedded_test_server()->GetURL("/server-redirect?simple_page.html");
GURL expected_url =
embedded_test_server()->GetURL("/simple_page.html?foo=bar");
EXPECT_TRUE(
NavigateToURL(shell(), url, expected_url ));
{
base::AutoLock auto_lock(lock);
ASSERT_EQ(header_map[expected_url]["Foo"], "BarRedirect");
ASSERT_EQ(header_map[expected_url]["ExemptFoo"], "ExemptBarRedirect");
ASSERT_NE(urls_requested.find(expected_url), urls_requested.end());
}
}
class LoaderNoScriptStreamingBrowserTest : public ContentBrowserTest {
public:
LoaderNoScriptStreamingBrowserTest() = default;
LoaderNoScriptStreamingBrowserTest(
const LoaderNoScriptStreamingBrowserTest&) = delete;
LoaderNoScriptStreamingBrowserTest& operator=(
const LoaderNoScriptStreamingBrowserTest&) = delete;
~LoaderNoScriptStreamingBrowserTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII("js-flags", "--no-script-streaming");
}
};
IN_PROC_BROWSER_TEST_F(LoaderNoScriptStreamingBrowserTest, LoadScript) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/loader/blank.html")));
std::string expected_title("DONE");
std::u16string expected_title16(ASCIIToUTF16(expected_title));
TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
ASSERT_TRUE(ExecJs(shell(), R"(
(() => {
const script = document.createElement('script');
script.src = './change_title.js';
document.body.appendChild(script);
})();
)"));
EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
}
#if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)
#define MAYBE_Subresource304Response DISABLED_Subresource304Response
#else
#define MAYBE_Subresource304Response Subresource304Response
#endif
IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, MAYBE_Subresource304Response) {
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
[](const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
if (request.relative_url == "/test.html") {
auto response =
std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content_type("text/html");
const size_t kScriptCount = 100;
std::vector<std::string> html_strings;
html_strings.emplace_back("<head><title></title><head><script>");
html_strings.emplace_back("const kScriptCount = ");
html_strings.emplace_back(base::NumberToString(kScriptCount));
html_strings.emplace_back(";\n");
html_strings.emplace_back(R"(
let count = 0;
function done() {
if (++count == kScriptCount) {
document.title='Scripts Loaded';
}
}
)");
html_strings.emplace_back("</script>");
for (size_t i = 0; i < kScriptCount; ++i) {
html_strings.emplace_back("<script src=\"./test.js?");
html_strings.emplace_back(base::NumberToString(i));
html_strings.emplace_back("\"></script>");
}
response->set_content(base::StrCat(html_strings));
return response;
} else if (request.relative_url.starts_with("/test.js?")) {
auto response =
std::make_unique<net::test_server::BasicHttpResponse>();
if (request.headers.contains("if-modified-since")) {
response->set_code(net::HTTP_NOT_MODIFIED);
return response;
}
response->set_content_type("application/javascript");
response->set_content("done();");
response->AddCustomHeader("Cache-Control", "max-age=0, no-cache");
response->AddCustomHeader("pragma", "no-cache");
response->AddCustomHeader("Last-Modified",
"Wed, 20 Dec 2023 01:00:00 GMT");
return response;
}
return nullptr;
}));
ASSERT_TRUE(embedded_test_server()->Start());
{
std::u16string expected_title(u"Scripts Loaded");
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/test.html")));
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
{
{
std::u16string expected_title(u"Title Cleared");
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
EXPECT_EQ("Title Cleared",
EvalJs(shell(), "document.title = 'Title Cleared';"));
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
{
std::u16string expected_title(u"Scripts Loaded");
TitleWatcher title_watcher(shell()->web_contents(), expected_title);
shell()->Reload();
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
}
}
}