diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc index fa3458247476b1..572cfaa68c4887 100644 --- a/content/browser/devtools/devtools_url_loader_interceptor.cc +++ b/content/browser/devtools/devtools_url_loader_interceptor.cc @@ -1071,10 +1071,10 @@ void InterceptionJob::ProcessRedirectByClient(const GURL& redirect_url) { response_metadata_->redirect_info = std::make_unique( net::RedirectInfo::ComputeRedirectInfo( request.method, request.url, request.site_for_cookies, - first_party_url_policy, request.referrer_policy, - request.referrer.spec(), &headers, headers.response_code(), - redirect_url, false /* insecure_scheme_was_upgraded */, - true /* copy_fragment */)); + request.top_frame_origin, first_party_url_policy, + request.referrer_policy, request.referrer.spec(), &headers, + headers.response_code(), redirect_url, + false /* insecure_scheme_was_upgraded */, true /* copy_fragment */)); client_->OnReceiveRedirect(*response_metadata_->redirect_info, response_metadata_->head); diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc index 5e3e6d6d15e4d3..5129a1e1d526b6 100644 --- a/content/browser/frame_host/navigation_request.cc +++ b/content/browser/frame_host/navigation_request.cc @@ -1489,12 +1489,21 @@ void NavigationRequest::OnStartChecksComplete( frame_tree_node_, begin_params_.get(), &report_raw_headers); devtools_instrumentation::OnNavigationRequestWillBeSent(*this); + // If this is a top-frame navigation, then use the origin of the url (and + // update it as redirects happen). If this is a sub-frame navigation, get the + // URL from the top frame. + GURL top_frame_url = + frame_tree_node_->IsMainFrame() + ? common_params_.url + : frame_tree_node_->frame_tree()->root()->current_url(); + url::Origin top_frame_origin = url::Origin::Create(top_frame_url); + loader_ = NavigationURLLoader::Create( browser_context->GetResourceContext(), partition, std::make_unique( common_params_, begin_params_.Clone(), site_for_cookies, - frame_tree_node_->IsMainFrame(), parent_is_main_frame, - IsSecureFrame(frame_tree_node_->parent()), + top_frame_origin, frame_tree_node_->IsMainFrame(), + parent_is_main_frame, IsSecureFrame(frame_tree_node_->parent()), frame_tree_node_->frame_tree_node_id(), is_for_guests_only, report_raw_headers, navigating_frame_host->GetVisibilityState() == diff --git a/content/browser/frame_host/navigation_request_info.cc b/content/browser/frame_host/navigation_request_info.cc index f02f9a18f6a3ad..b0ab6cdf70d237 100644 --- a/content/browser/frame_host/navigation_request_info.cc +++ b/content/browser/frame_host/navigation_request_info.cc @@ -11,6 +11,7 @@ NavigationRequestInfo::NavigationRequestInfo( const CommonNavigationParams& common_params, mojom::BeginNavigationParamsPtr begin_params, const GURL& site_for_cookies, + const base::Optional& top_frame_origin, bool is_main_frame, bool parent_is_main_frame, bool are_ancestors_secure, @@ -26,6 +27,7 @@ NavigationRequestInfo::NavigationRequestInfo( : common_params(common_params), begin_params(std::move(begin_params)), site_for_cookies(site_for_cookies), + top_frame_origin(top_frame_origin), is_main_frame(is_main_frame), parent_is_main_frame(parent_is_main_frame), are_ancestors_secure(are_ancestors_secure), @@ -42,6 +44,7 @@ NavigationRequestInfo::NavigationRequestInfo(const NavigationRequestInfo& other) : common_params(other.common_params), begin_params(other.begin_params.Clone()), site_for_cookies(other.site_for_cookies), + top_frame_origin(other.top_frame_origin), is_main_frame(other.is_main_frame), parent_is_main_frame(other.parent_is_main_frame), are_ancestors_secure(other.are_ancestors_secure), diff --git a/content/browser/frame_host/navigation_request_info.h b/content/browser/frame_host/navigation_request_info.h index 8b6a81c92d9d12..e0d59153bf2648 100644 --- a/content/browser/frame_host/navigation_request_info.h +++ b/content/browser/frame_host/navigation_request_info.h @@ -26,6 +26,7 @@ struct CONTENT_EXPORT NavigationRequestInfo { NavigationRequestInfo(const CommonNavigationParams& common_params, mojom::BeginNavigationParamsPtr begin_params, const GURL& site_for_cookies, + const base::Optional& top_frame_origin, bool is_main_frame, bool parent_is_main_frame, bool are_ancestors_secure, @@ -48,6 +49,12 @@ struct CONTENT_EXPORT NavigationRequestInfo { // checked by the third-party cookie blocking policy. const GURL site_for_cookies; + // The origin of the navigation if top frame, else the origin of the top + // frame. + // TODO(crbug.com/910716) Make this required. I believe we just need to add + // support for signed exchange redirects. + const base::Optional top_frame_origin; + const bool is_main_frame; const bool parent_is_main_frame; diff --git a/content/browser/frame_host/navigator_impl_unittest.cc b/content/browser/frame_host/navigator_impl_unittest.cc index abda7b1645119a..e69a9ff38bf723 100644 --- a/content/browser/frame_host/navigator_impl_unittest.cc +++ b/content/browser/frame_host/navigator_impl_unittest.cc @@ -312,6 +312,8 @@ TEST_F(NavigatorTestWithBrowserSideNavigation, BeginNavigation) { EXPECT_EQ(kUrl2, subframe_loader->request_info()->common_params.url); // First party for cookies url should be that of the main frame. EXPECT_EQ(kUrl1, subframe_loader->request_info()->site_for_cookies); + EXPECT_EQ(url::Origin::Create(kUrl1), + subframe_loader->request_info()->top_frame_origin); EXPECT_FALSE(subframe_loader->request_info()->is_main_frame); EXPECT_TRUE(subframe_loader->request_info()->parent_is_main_frame); EXPECT_TRUE(subframe_request->browser_initiated()); diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index 2905e7418f5fb5..6104aafdac2213 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc @@ -209,6 +209,7 @@ std::unique_ptr CreateResourceRequest( new_request->method = request_info->common_params.method; new_request->url = request_info->common_params.url; new_request->site_for_cookies = request_info->site_for_cookies; + new_request->top_frame_origin = request_info->top_frame_origin; net::RequestPriority net_priority = net::HIGHEST; if (!request_info->is_main_frame && @@ -299,6 +300,7 @@ std::unique_ptr CreateNavigationRequestInfoForRedirect( return std::make_unique( std::move(new_common_params), std::move(new_begin_params), updated_resource_request.site_for_cookies, + updated_resource_request.top_frame_origin, previous_request_info.is_main_frame, previous_request_info.parent_is_main_frame, previous_request_info.are_ancestors_secure, @@ -1028,7 +1030,6 @@ class NavigationURLLoaderImpl::URLLoaderRequestController const base::Optional& modified_request_headers) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!redirect_info_.new_url.is_empty()); - if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { auto* common_params = const_cast(&request_info_->common_params); @@ -1065,6 +1066,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController resource_request_->url = redirect_info_.new_url; resource_request_->method = redirect_info_.new_method; resource_request_->site_for_cookies = redirect_info_.new_site_for_cookies; + resource_request_->top_frame_origin = redirect_info_.new_top_frame_origin; resource_request_->referrer = GURL(redirect_info_.new_referrer); resource_request_->referrer_policy = redirect_info_.new_referrer_policy; url_chain_.push_back(redirect_info_.new_url); diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc index 6e03991981f9f3..3dd219a5e76f51 100644 --- a/content/browser/loader/navigation_url_loader_impl_unittest.cc +++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc @@ -181,7 +181,8 @@ class NavigationURLLoaderImplTest : public testing::Test { std::unique_ptr request_info( new NavigationRequestInfo( - common_params, std::move(begin_params), url, is_main_frame, + common_params, std::move(begin_params), url, + url::Origin::Create(url), is_main_frame, false /* parent_is_main_frame */, false /* are_ancestors_secure */, -1 /* frame_tree_node_id */, false /* is_for_guests_only */, false /* report_raw_headers */, false /* is_prerenering */, @@ -306,6 +307,38 @@ TEST_F(NavigationURLLoaderImplTest, RequestPriority) { NavigateAndReturnRequestPriority(url, false /* is_main_frame */)); } +TEST_F(NavigationURLLoaderImplTest, TopFrameOriginOfMainFrameNavigation) { + ASSERT_TRUE(http_test_server_.Start()); + + const GURL url = http_test_server_.GetURL("/foo"); + + TestNavigationURLLoaderDelegate delegate; + std::unique_ptr loader = CreateTestLoader( + url, + base::StringPrintf("%s: %s", net::HttpRequestHeaders::kOrigin, + url.GetOrigin().spec().c_str()), + "GET", &delegate, NavigationDownloadPolicy::kAllow, + true /*is_main_frame*/, false /*upgrade_if_insecure*/); + delegate.WaitForRequestStarted(); + + ASSERT_TRUE(most_recent_resource_request_); + EXPECT_EQ(url::Origin::Create(url), + *most_recent_resource_request_->top_frame_origin); +} + +TEST_F(NavigationURLLoaderImplTest, + TopFrameOriginOfRedirectedMainFrameNavigation) { + ASSERT_TRUE(http_test_server_.Start()); + + const GURL url = http_test_server_.GetURL("/redirect301-to-echo"); + const GURL final_url = http_test_server_.GetURL("/echo"); + + HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.GetOrigin().spec()); + + EXPECT_EQ(url::Origin::Create(final_url), + *most_recent_resource_request_->top_frame_origin); +} + TEST_F(NavigationURLLoaderImplTest, Redirect301Tests) { ASSERT_TRUE(http_test_server_.Start()); diff --git a/content/browser/loader/navigation_url_loader_unittest.cc b/content/browser/loader/navigation_url_loader_unittest.cc index f3cbb9950093c2..a90bfba4583e0f 100644 --- a/content/browser/loader/navigation_url_loader_unittest.cc +++ b/content/browser/loader/navigation_url_loader_unittest.cc @@ -94,8 +94,8 @@ class NavigationURLLoaderTest : public testing::Test { std::unique_ptr request_info( new NavigationRequestInfo(common_params, std::move(begin_params), url, - true, false, false, -1, false, false, false, - false, nullptr, + url::Origin::Create(url), true, false, false, + -1, false, false, false, false, nullptr, base::UnguessableToken::Create(), base::UnguessableToken::Create())); return NavigationURLLoader::Create( diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 83b81f3e28f5f4..2831e906d3cd91 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc @@ -850,6 +850,7 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( new_request->set_method(request_data.method); new_request->set_site_for_cookies(request_data.site_for_cookies); + new_request->set_top_frame_origin(request_data.top_frame_origin); new_request->set_attach_same_site_cookies( request_data.attach_same_site_cookies); new_request->set_upgrade_if_insecure(request_data.upgrade_if_insecure); @@ -1474,6 +1475,7 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( new_request->set_method(info.common_params.method); new_request->set_site_for_cookies(info.site_for_cookies); + new_request->set_top_frame_origin(info.top_frame_origin); new_request->set_initiator(info.begin_params->initiator_origin); new_request->set_upgrade_if_insecure(info.upgrade_if_insecure); if (info.is_main_frame) { diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc index 73b5961922b916..43919fe66e6042 100644 --- a/content/browser/loader/resource_dispatcher_host_unittest.cc +++ b/content/browser/loader/resource_dispatcher_host_unittest.cc @@ -843,8 +843,8 @@ class ResourceDispatcherHostTest : public testing::TestWithParam { common_params.url = url; std::unique_ptr request_info( new NavigationRequestInfo(common_params, std::move(begin_params), url, - true, false, false, -1, false, false, false, - false, nullptr, + url::Origin::Create(url), true, false, false, + -1, false, false, false, false, nullptr, base::UnguessableToken::Create(), base::UnguessableToken::Create())); std::unique_ptr test_loader = diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc index ba1db40b2aa4d7..e194b1d1fdb22d 100644 --- a/content/browser/navigation_browsertest.cc +++ b/content/browser/navigation_browsertest.cc @@ -43,6 +43,7 @@ #include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_navigation_throttle.h" #include "content/public/test/test_navigation_throttle_inserter.h" +#include "content/public/test/url_loader_interceptor.h" #include "content/shell/browser/shell.h" #include "content/shell/browser/shell_content_browser_client.h" #include "content/shell/browser/shell_download_manager_delegate.h" @@ -191,6 +192,40 @@ class NavigationBrowserTest : public NavigationBaseBrowserTest { NavigationBaseBrowserTest::SetUpOnMainThread(); ASSERT_TRUE(embedded_test_server()->Start()); } + + // Navigate to |url| and for each ResourceRequest record its + // top_frame_origin. Stop listening after |final_resource| has been + // detected. The output is recorded in |top_frame_origins|. + void NavigateAndRecordTopFrameOrigins( + const GURL& url, + const GURL& final_resource, + bool from_renderer, + std::map* top_frame_origins) { + if (from_renderer) + EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank"))); + + base::RunLoop run_loop; + base::OnceClosure quit_closure = run_loop.QuitClosure(); + + // Intercept network requests and record them. + URLLoaderInterceptor interceptor(base::BindLambdaForTesting( + [&](URLLoaderInterceptor::RequestParams* params) -> bool { + (*top_frame_origins)[params->url_request.url] = + *params->url_request.top_frame_origin; + + if (params->url_request.url == final_resource) + std::move(quit_closure).Run(); + return false; + })); + + if (from_renderer) + EXPECT_TRUE(NavigateToURLFromRenderer(shell(), url)); + else + EXPECT_TRUE(NavigateToURL(shell(), url)); + + // Wait until the last resource we care about has been requested. + run_loop.Run(); + } }; // Ensure that browser initiated basic navigations work. @@ -715,6 +750,48 @@ IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest, EXPECT_EQ("\"done\"", done); } +IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, BrowserNavigationTopFrameOrigin) { + std::map top_frame_origins; + GURL url(embedded_test_server()->GetURL("/title1.html")); + + NavigateAndRecordTopFrameOrigins(url, url /*final_resource*/, + false /*from_renderer*/, &top_frame_origins); + EXPECT_EQ(url::Origin::Create(url), top_frame_origins[url]); +} + +IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, RenderNavigationTopFrameOrigin) { + std::map top_frame_origins; + GURL url(embedded_test_server()->GetURL("/title2.html")); + + NavigateAndRecordTopFrameOrigins(url, url /*final_resource*/, + true /*from_renderer*/, &top_frame_origins); + EXPECT_EQ(url::Origin::Create(url), top_frame_origins[url]); +} + +IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SubframeTopFrameOrigin) { + std::map top_frame_origins; + GURL url(embedded_test_server()->GetURL("/page_with_iframe.html")); + GURL iframe_document = embedded_test_server()->GetURL("/title1.html"); + + NavigateAndRecordTopFrameOrigins(url, iframe_document /*final_resource*/, + false /*from_renderer*/, &top_frame_origins); + EXPECT_EQ(url::Origin::Create(url), top_frame_origins[url]); + EXPECT_EQ(url::Origin::Create(url), top_frame_origins[iframe_document]); +} + +IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SubresourceTopFrameOrigin) { + std::map top_frame_origins; + GURL url(embedded_test_server()->GetURL("/page_with_iframe_and_image.html")); + GURL blank_image = embedded_test_server()->GetURL("/blank.jpg"); + + NavigateAndRecordTopFrameOrigins(url, blank_image /*final_resource*/, + false /*from_renderer*/, &top_frame_origins); + EXPECT_EQ(url::Origin::Create(url), top_frame_origins[url]); + EXPECT_EQ(url::Origin::Create(url), + top_frame_origins[embedded_test_server()->GetURL("/image.jpg")]); + EXPECT_EQ(url::Origin::Create(url), top_frame_origins[blank_image]); +} + // Navigation are started in the browser process. After the headers are // received, the URLLoaderClient is transferred from the browser process to the // renderer process. This test ensures that when the the URLLoader is deleted diff --git a/content/common/service_worker/service_worker_loader_helpers.cc b/content/common/service_worker/service_worker_loader_helpers.cc index 1b26bc94e458d7..736dcbe9471b35 100644 --- a/content/common/service_worker/service_worker_loader_helpers.cc +++ b/content/common/service_worker/service_worker_loader_helpers.cc @@ -124,8 +124,8 @@ ServiceWorkerLoaderHelpers::ComputeRedirectInfo( : net::URLRequest::NEVER_CHANGE_FIRST_PARTY_URL; return net::RedirectInfo::ComputeRedirectInfo( original_request.method, original_request.url, - original_request.site_for_cookies, first_party_url_policy, - original_request.referrer_policy, + original_request.site_for_cookies, original_request.top_frame_origin, + first_party_url_policy, original_request.referrer_policy, network::ComputeReferrer(original_request.referrer), response_head.headers.get(), response_head.headers->response_code(), original_request.url.Resolve(new_location), false); diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc index 044a92e3066b48..18292c5a1ab239 100644 --- a/content/common/throttling_url_loader.cc +++ b/content/common/throttling_url_loader.cc @@ -350,6 +350,8 @@ void ThrottlingURLLoader::StartNow() { redirect_info.new_method = start_info_->url_request.method; redirect_info.new_url = throttle_will_start_redirect_url_; redirect_info.new_site_for_cookies = throttle_will_start_redirect_url_; + redirect_info.new_top_frame_origin = + url::Origin::Create(throttle_will_start_redirect_url_); network::ResourceResponseHead response_head; std::string header_string = base::StringPrintf( @@ -557,6 +559,7 @@ void ThrottlingURLLoader::OnReceiveRedirect( request.url = redirect_info.new_url; request.method = redirect_info.new_method; request.site_for_cookies = redirect_info.new_site_for_cookies; + request.top_frame_origin = redirect_info.new_top_frame_origin; request.referrer = GURL(redirect_info.new_referrer); request.referrer_policy = redirect_info.new_referrer_policy; diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc index ed0bcc9d96e5fe..846f4e310c44cf 100644 --- a/content/renderer/loader/web_url_loader_impl.cc +++ b/content/renderer/loader/web_url_loader_impl.cc @@ -641,6 +641,7 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request, resource_request->method = method; resource_request->url = url_; resource_request->site_for_cookies = request.SiteForCookies(); + resource_request->top_frame_origin = request.TopFrameOrigin(); resource_request->upgrade_if_insecure = request.UpgradeIfInsecure(); resource_request->is_revalidating = request.IsRevalidating(); if (!request.RequestorOrigin().IsNull()) { @@ -818,6 +819,7 @@ bool WebURLLoaderImpl::Context::OnReceivedRedirect( url_ = WebURL(redirect_info.new_url); return client_->WillFollowRedirect( url_, redirect_info.new_site_for_cookies, + redirect_info.new_top_frame_origin, WebString::FromUTF8(redirect_info.new_referrer), Referrer::NetReferrerPolicyToBlinkReferrerPolicy( redirect_info.new_referrer_policy), diff --git a/content/renderer/loader/web_url_loader_impl_unittest.cc b/content/renderer/loader/web_url_loader_impl_unittest.cc index 2035060409ee55..0af9e4144587a9 100644 --- a/content/renderer/loader/web_url_loader_impl_unittest.cc +++ b/content/renderer/loader/web_url_loader_impl_unittest.cc @@ -185,13 +185,15 @@ class TestWebURLLoaderClient : public blink::WebURLLoaderClient { ~TestWebURLLoaderClient() override {} // blink::WebURLLoaderClient implementation: - bool WillFollowRedirect(const blink::WebURL& new_url, - const blink::WebURL& new_site_for_cookies, - const blink::WebString& new_referrer, - network::mojom::ReferrerPolicy new_referrer_policy, - const blink::WebString& new_method, - const blink::WebURLResponse& passed_redirect_response, - bool& report_raw_headers) override { + bool WillFollowRedirect( + const blink::WebURL& new_url, + const blink::WebURL& new_site_for_cookies, + const base::Optional& new_top_frame_origin, + const blink::WebString& new_referrer, + network::mojom::ReferrerPolicy new_referrer_policy, + const blink::WebString& new_method, + const blink::WebURLResponse& passed_redirect_response, + bool& report_raw_headers) override { EXPECT_TRUE(loader_); // No test currently simulates mutiple redirects. diff --git a/content/test/data/page_with_iframe_and_image.html b/content/test/data/page_with_iframe_and_image.html new file mode 100644 index 00000000000000..0217f090ebe5e5 --- /dev/null +++ b/content/test/data/page_with_iframe_and_image.html @@ -0,0 +1,9 @@ + + + + +

This page has an iframe. Yay for iframes! +

+ + + diff --git a/headless/test/test_network_interceptor.cc b/headless/test/test_network_interceptor.cc index 573f6b4b38b8bd..23799573a5a65b 100644 --- a/headless/test/test_network_interceptor.cc +++ b/headless/test/test_network_interceptor.cc @@ -51,6 +51,7 @@ class RedirectLoader : public network::mojom::URLLoader { void NotifyRedirect(const std::string& location) { auto redirect_info = net::RedirectInfo::ComputeRedirectInfo( url_request_.method, url_request_.url, url_request_.site_for_cookies, + url_request_.top_frame_origin, net::URLRequest::FirstPartyURLPolicy:: UPDATE_FIRST_PARTY_URL_ON_REDIRECT, url_request_.referrer_policy, url_request_.referrer.spec(), diff --git a/net/url_request/redirect_info.cc b/net/url_request/redirect_info.cc index f7075073fbeb90..258f64287ccfdf 100644 --- a/net/url_request/redirect_info.cc +++ b/net/url_request/redirect_info.cc @@ -117,6 +117,7 @@ RedirectInfo RedirectInfo::ComputeRedirectInfo( const std::string& original_method, const GURL& original_url, const GURL& original_site_for_cookies, + const base::Optional& original_top_frame_origin, URLRequest::FirstPartyURLPolicy original_first_party_url_policy, URLRequest::ReferrerPolicy original_referrer_policy, const std::string& original_referrer, @@ -156,8 +157,13 @@ RedirectInfo RedirectInfo::ComputeRedirectInfo( if (original_first_party_url_policy == URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT) { redirect_info.new_site_for_cookies = redirect_info.new_url; + if (original_top_frame_origin) { + redirect_info.new_top_frame_origin = + url::Origin::Create(redirect_info.new_url); + } } else { redirect_info.new_site_for_cookies = original_site_for_cookies; + redirect_info.new_top_frame_origin = original_top_frame_origin; } redirect_info.new_referrer_policy = ProcessReferrerPolicyHeaderOnRedirect( diff --git a/net/url_request/redirect_info.h b/net/url_request/redirect_info.h index d461115c298ba1..c8ce8e5c80d19d 100644 --- a/net/url_request/redirect_info.h +++ b/net/url_request/redirect_info.h @@ -28,6 +28,7 @@ struct NET_EXPORT RedirectInfo { const std::string& original_method, const GURL& original_url, const GURL& original_site_for_cookies, + const base::Optional& original_top_frame_origin, URLRequest::FirstPartyURLPolicy original_first_party_url_policy, URLRequest::ReferrerPolicy original_referrer_policy, const std::string& original_referrer, @@ -63,6 +64,8 @@ struct NET_EXPORT RedirectInfo { // The new first-party URL for cookies. GURL new_site_for_cookies; + base::Optional new_top_frame_origin; + // The new HTTP referrer header. std::string new_referrer; diff --git a/net/url_request/redirect_info_unittest.cc b/net/url_request/redirect_info_unittest.cc index 01a307e658279e..cefc3a7b8fe98d 100644 --- a/net/url_request/redirect_info_unittest.cc +++ b/net/url_request/redirect_info_unittest.cc @@ -30,6 +30,7 @@ TEST(RedirectInfoTest, MethodForRedirect) { const GURL kOriginalUrl = GURL("https://foo.test/original"); const GURL kOriginalSiteForCookies = GURL("https://foo.test/"); + const url::Origin kOriginalTopFrameOrigin = url::Origin::Create(kOriginalUrl); const URLRequest::FirstPartyURLPolicy kOriginalFirstPartyUrlPolicy = net::URLRequest::NEVER_CHANGE_FIRST_PARTY_URL; const URLRequest::ReferrerPolicy kOriginalReferrerPolicy = @@ -46,10 +47,10 @@ TEST(RedirectInfoTest, MethodForRedirect) { RedirectInfo redirect_info = RedirectInfo::ComputeRedirectInfo( test.original_method, kOriginalUrl, kOriginalSiteForCookies, - kOriginalFirstPartyUrlPolicy, kOriginalReferrerPolicy, - kOriginalReferrer, nullptr /* response_headers */, - test.http_status_code, kNewLocation, kInsecureSchemeWasUpgraded, - kCopyFragment); + kOriginalTopFrameOrigin, kOriginalFirstPartyUrlPolicy, + kOriginalReferrerPolicy, kOriginalReferrer, + nullptr /* response_headers */, test.http_status_code, kNewLocation, + kInsecureSchemeWasUpgraded, kCopyFragment); EXPECT_EQ(test.expected_new_method, redirect_info.new_method); EXPECT_EQ(test.http_status_code, redirect_info.status_code); @@ -97,12 +98,15 @@ TEST(RedirectInfoTest, CopyFragment) { RedirectInfo redirect_info = RedirectInfo::ComputeRedirectInfo( KOriginalMethod, GURL(test.original_url), kOriginalSiteForCookies, + url::Origin::Create(GURL(test.original_url)), kOriginalFirstPartyUrlPolicy, kOriginalReferrerPolicy, kOriginalReferrer, nullptr /* response_headers */, kHttpStatusCode, GURL(test.new_location), kInsecureSchemeWasUpgraded, test.copy_fragment); EXPECT_EQ(GURL(test.expected_new_url), redirect_info.new_url); + EXPECT_EQ(url::Origin::Create(GURL(test.original_url)), + redirect_info.new_top_frame_origin); } } @@ -135,12 +139,19 @@ TEST(RedirectInfoTest, FirstPartyURLPolicy) { RedirectInfo redirect_info = RedirectInfo::ComputeRedirectInfo( KOriginalMethod, kOriginalUrl, kOriginalSiteForCookies, - test.original_first_party_url_policy, kOriginalReferrerPolicy, - kOriginalReferrer, nullptr /* response_headers */, kHttpStatusCode, - kNewLocation, kInsecureSchemeWasUpgraded, kCopyFragment); + url::Origin::Create(kOriginalUrl), test.original_first_party_url_policy, + kOriginalReferrerPolicy, kOriginalReferrer, + nullptr /* response_headers */, kHttpStatusCode, kNewLocation, + kInsecureSchemeWasUpgraded, kCopyFragment); EXPECT_EQ(GURL(test.expected_new_site_for_cookies), redirect_info.new_site_for_cookies); + url::Origin expected_top_frame_origin = + test.original_first_party_url_policy == + URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT + ? url::Origin::Create(GURL(test.expected_new_site_for_cookies)) + : url::Origin::Create(kOriginalUrl); + EXPECT_EQ(expected_top_frame_origin, redirect_info.new_top_frame_origin); } } @@ -448,9 +459,9 @@ TEST(RedirectInfoTest, ReferrerPolicy) { RedirectInfo redirect_info = RedirectInfo::ComputeRedirectInfo( KOriginalMethod, original_url, kOriginalSiteForCookies, - kOriginalFirstPartyUrlPolicy, test.original_referrer_policy, - test.original_referrer, response_headers.get(), - response_headers->response_code(), new_location, + url::Origin::Create(original_url), kOriginalFirstPartyUrlPolicy, + test.original_referrer_policy, test.original_referrer, + response_headers.get(), response_headers->response_code(), new_location, kInsecureSchemeWasUpgraded, kCopyFragment); EXPECT_EQ(test.expected_new_referrer_policy, diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index c37e8c29cc1605..75b075eead1c25 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc @@ -971,6 +971,7 @@ void URLRequest::Redirect( referrer_ = redirect_info.new_referrer; referrer_policy_ = redirect_info.new_referrer_policy; site_for_cookies_ = redirect_info.new_site_for_cookies; + top_frame_origin_ = redirect_info.new_top_frame_origin; url_chain_.push_back(redirect_info.new_url); --redirect_limit_; diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index 12ce9b653092de..64fcb0cad19741 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -287,6 +287,15 @@ class NET_EXPORT URLRequest : public base::SupportsUserData { // This method may only be called before Start(). void set_site_for_cookies(const GURL& site_for_cookies); + // The origin of the top frame of the page making the request (where + // applicable). Note that this is experimental and may not always be set. + const base::Optional& top_frame_origin() const { + return top_frame_origin_; + } + void set_top_frame_origin(const base::Optional& origin) { + top_frame_origin_ = origin; + } + // Indicate whether SameSite cookies should be attached even though the // request is cross-site. bool attach_same_site_cookies() const { return attach_same_site_cookies_; } @@ -855,6 +864,8 @@ class NET_EXPORT URLRequest : public base::SupportsUserData { std::vector url_chain_; GURL site_for_cookies_; + base::Optional top_frame_origin_; + bool attach_same_site_cookies_; base::Optional initiator_; GURL delegate_redirect_url_; diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc index fddd0be90686d1..2422a0b9ef55f3 100644 --- a/net/url_request/url_request_job.cc +++ b/net/url_request/url_request_job.cc @@ -427,10 +427,10 @@ void URLRequestJob::NotifyHeadersComplete() { RedirectInfo redirect_info = RedirectInfo::ComputeRedirectInfo( request_->method(), request_->url(), request_->site_for_cookies(), - request_->first_party_url_policy(), request_->referrer_policy(), - request_->referrer(), request_->response_headers(), http_status_code, - new_location, insecure_scheme_was_upgraded, - CopyFragmentOnRedirect(new_location)); + request_->top_frame_origin(), request_->first_party_url_policy(), + request_->referrer_policy(), request_->referrer(), + request_->response_headers(), http_status_code, new_location, + insecure_scheme_was_upgraded, CopyFragmentOnRedirect(new_location)); bool defer_redirect = false; request_->NotifyReceivedRedirect(redirect_info, &defer_redirect); diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 5f635242972666..817ea5063e108e 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -8003,18 +8003,16 @@ TEST_F(URLRequestTestHTTP, Redirect302PreserveReferenceFragment) { GURL expected_url(http_test_server()->GetURL("/echo#fragment")); TestDelegate d; - { - std::unique_ptr r(default_context().CreateRequest( - original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + std::unique_ptr r(default_context().CreateRequest( + original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); - r->Start(); - d.RunUntilComplete(); + r->Start(); + d.RunUntilComplete(); - EXPECT_EQ(2U, r->url_chain().size()); - EXPECT_EQ(OK, d.request_status()); - EXPECT_EQ(original_url, r->original_url()); - EXPECT_EQ(expected_url, r->url()); - } + EXPECT_EQ(2U, r->url_chain().size()); + EXPECT_EQ(OK, d.request_status()); + EXPECT_EQ(original_url, r->original_url()); + EXPECT_EQ(expected_url, r->url()); } TEST_F(URLRequestTestHTTP, RedirectPreserveFirstPartyURL) { @@ -8024,18 +8022,54 @@ TEST_F(URLRequestTestHTTP, RedirectPreserveFirstPartyURL) { GURL first_party_url("http://example.com"); TestDelegate d; - { - std::unique_ptr r(default_context().CreateRequest( - url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); - r->set_site_for_cookies(first_party_url); + std::unique_ptr r(default_context().CreateRequest( + url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + r->set_site_for_cookies(first_party_url); - r->Start(); - d.RunUntilComplete(); + r->Start(); + d.RunUntilComplete(); - EXPECT_EQ(2U, r->url_chain().size()); - EXPECT_EQ(OK, d.request_status()); - EXPECT_EQ(first_party_url, r->site_for_cookies()); - } + EXPECT_EQ(2U, r->url_chain().size()); + EXPECT_EQ(OK, d.request_status()); + EXPECT_EQ(first_party_url, r->site_for_cookies()); +} + +TEST_F(URLRequestTestHTTP, RedirectPreserveTopFrameOrigin) { + ASSERT_TRUE(http_test_server()->Start()); + + GURL url(http_test_server()->GetURL("/redirect302-to-echo")); + url::Origin top_frame_origin = + url::Origin::Create(GURL("http://example.com")); + TestDelegate d; + + std::unique_ptr r(default_context().CreateRequest( + url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + r->set_top_frame_origin(top_frame_origin); + + r->Start(); + d.RunUntilComplete(); + + EXPECT_EQ(2U, r->url_chain().size()); + EXPECT_EQ(OK, d.request_status()); + EXPECT_EQ(top_frame_origin, *r->top_frame_origin()); +} + +TEST_F(URLRequestTestHTTP, RedirectPreserveUnsetTopFrameOrigin) { + ASSERT_TRUE(http_test_server()->Start()); + + GURL url(http_test_server()->GetURL("/redirect302-to-echo")); + TestDelegate d; + + std::unique_ptr r(default_context().CreateRequest( + url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + // Since we're not setting a top frame origin, we wouldn't expect one after + // the redirect. + r->Start(); + d.RunUntilComplete(); + + EXPECT_EQ(2U, r->url_chain().size()); + EXPECT_EQ(OK, d.request_status()); + EXPECT_FALSE(r->top_frame_origin()); } TEST_F(URLRequestTestHTTP, RedirectUpdateFirstPartyURL) { @@ -8046,7 +8080,7 @@ TEST_F(URLRequestTestHTTP, RedirectUpdateFirstPartyURL) { GURL expected_first_party_url(http_test_server()->GetURL("/echo")); TestDelegate d; - { + std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_site_for_cookies(original_first_party_url); @@ -8059,7 +8093,50 @@ TEST_F(URLRequestTestHTTP, RedirectUpdateFirstPartyURL) { EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(expected_first_party_url, r->site_for_cookies()); - } +} + +TEST_F(URLRequestTestHTTP, RedirectUpdateTopFrameOrigin) { + ASSERT_TRUE(http_test_server()->Start()); + + GURL url(http_test_server()->GetURL("/redirect302-to-echo")); + url::Origin original_top_frame_origin = + url::Origin::Create(GURL("http://example.com")); + url::Origin expected_top_frame_origin = + url::Origin::Create(GURL(http_test_server()->GetURL("/echo"))); + + TestDelegate d; + + std::unique_ptr r(default_context().CreateRequest( + url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + r->set_top_frame_origin(original_top_frame_origin); + r->set_first_party_url_policy(URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); + + r->Start(); + d.RunUntilComplete(); + + EXPECT_EQ(2U, r->url_chain().size()); + EXPECT_EQ(OK, d.request_status()); + EXPECT_EQ(expected_top_frame_origin, *r->top_frame_origin()); +} + +TEST_F(URLRequestTestHTTP, RedirectIgnoreUnsetTopFrameOrigin) { + ASSERT_TRUE(http_test_server()->Start()); + + GURL url(http_test_server()->GetURL("/redirect302-to-echo")); + TestDelegate d; + + std::unique_ptr r(default_context().CreateRequest( + url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); + // Since we're not setting a top frame origin, we wouldn't expect one after + // the redirect. + r->set_first_party_url_policy(URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); + + r->Start(); + d.RunUntilComplete(); + + EXPECT_EQ(2U, r->url_chain().size()); + EXPECT_EQ(OK, d.request_status()); + EXPECT_FALSE(r->top_frame_origin()); } TEST_F(URLRequestTestHTTP, InterceptPost302RedirectGet) { diff --git a/services/network/public/cpp/net_ipc_param_traits.h b/services/network/public/cpp/net_ipc_param_traits.h index 9ae1082fd76685..9bfa614500c978 100644 --- a/services/network/public/cpp/net_ipc_param_traits.h +++ b/services/network/public/cpp/net_ipc_param_traits.h @@ -253,6 +253,7 @@ IPC_STRUCT_TRAITS_BEGIN(net::RedirectInfo) IPC_STRUCT_TRAITS_MEMBER(new_method) IPC_STRUCT_TRAITS_MEMBER(new_url) IPC_STRUCT_TRAITS_MEMBER(new_site_for_cookies) + IPC_STRUCT_TRAITS_MEMBER(new_top_frame_origin) IPC_STRUCT_TRAITS_MEMBER(new_referrer) IPC_STRUCT_TRAITS_MEMBER(insecure_scheme_was_upgraded) IPC_STRUCT_TRAITS_MEMBER(new_referrer_policy) diff --git a/services/network/public/cpp/network_ipc_param_traits.h b/services/network/public/cpp/network_ipc_param_traits.h index 053601c39bef37..8e672fbcd60cbd 100644 --- a/services/network/public/cpp/network_ipc_param_traits.h +++ b/services/network/public/cpp/network_ipc_param_traits.h @@ -139,6 +139,7 @@ IPC_STRUCT_TRAITS_BEGIN(network::ResourceRequest) IPC_STRUCT_TRAITS_MEMBER(method) IPC_STRUCT_TRAITS_MEMBER(url) IPC_STRUCT_TRAITS_MEMBER(site_for_cookies) + IPC_STRUCT_TRAITS_MEMBER(top_frame_origin) IPC_STRUCT_TRAITS_MEMBER(attach_same_site_cookies) IPC_STRUCT_TRAITS_MEMBER(update_first_party_url_on_redirect) IPC_STRUCT_TRAITS_MEMBER(request_initiator) diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h index fbadf62a939487..cd8ba727c0d0cf 100644 --- a/services/network/public/cpp/resource_request.h +++ b/services/network/public/cpp/resource_request.h @@ -43,6 +43,12 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { // done if there really is no way to determine the correct value. GURL site_for_cookies; + // If the optional is valid, contains the origin of the top frame of the page + // making the request. Note that this is experimental and is only set for + // navigation and document subresource requests but not other cases such as + // workers. + base::Optional top_frame_origin; + // Boolean indicating whether SameSite cookies are allowed to be attached // to the request. It should be used as additional input to network side // checks. diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index 7e9be16d02c9d4..938d46a3e810b2 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc @@ -369,6 +369,7 @@ URLLoader::URLLoader( GURL(request.url), request.priority, this, traffic_annotation); url_request_->set_method(request.method); url_request_->set_site_for_cookies(request.site_for_cookies); + url_request_->set_top_frame_origin(request.top_frame_origin); url_request_->set_attach_same_site_cookies(request.attach_same_site_cookies); url_request_->SetReferrer(ComputeReferrer(request.referrer)); url_request_->set_referrer_policy(request.referrer_policy); diff --git a/third_party/blink/public/platform/web_url_loader_client.h b/third_party/blink/public/platform/web_url_loader_client.h index 96991bd5446c3a..61c9a1789726bd 100644 --- a/third_party/blink/public/platform/web_url_loader_client.h +++ b/third_party/blink/public/platform/web_url_loader_client.h @@ -58,6 +58,7 @@ class BLINK_PLATFORM_EXPORT WebURLLoaderClient { virtual bool WillFollowRedirect( const WebURL& new_url, const WebURL& new_site_for_cookies, + const base::Optional& new_top_frame_origin, const WebString& new_referrer, network::mojom::ReferrerPolicy new_referrer_policy, const WebString& new_method, diff --git a/third_party/blink/public/platform/web_url_request.h b/third_party/blink/public/platform/web_url_request.h index 9d9542d85db187..5e6145252b7faa 100644 --- a/third_party/blink/public/platform/web_url_request.h +++ b/third_party/blink/public/platform/web_url_request.h @@ -130,6 +130,10 @@ class WebURLRequest { BLINK_PLATFORM_EXPORT WebURL SiteForCookies() const; BLINK_PLATFORM_EXPORT void SetSiteForCookies(const WebURL&); + BLINK_PLATFORM_EXPORT base::Optional TopFrameOrigin() + const; + BLINK_PLATFORM_EXPORT void SetTopFrameOrigin(const WebSecurityOrigin&); + // https://fetch.spec.whatwg.org/#concept-request-origin BLINK_PLATFORM_EXPORT WebSecurityOrigin RequestorOrigin() const; BLINK_PLATFORM_EXPORT void SetRequestorOrigin(const WebSecurityOrigin&); diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index 6fc8bef23cccdf..d417b7d8a3cc8f 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc @@ -5551,6 +5551,13 @@ String Document::lastModified() const { date.Minute(), date.Second()); } +scoped_refptr Document::TopFrameOrigin() const { + if (!GetFrame()) + return scoped_refptr(); + + return GetFrame()->Tree().Top().GetSecurityContext()->GetSecurityOrigin(); +} + const KURL Document::SiteForCookies() const { // TODO(mkwst): This doesn't properly handle HTML Import documents. diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h index 7d5ec6f0e2730d..8a589603674d81 100644 --- a/third_party/blink/renderer/core/dom/document.h +++ b/third_party/blink/renderer/core/dom/document.h @@ -958,6 +958,8 @@ class CORE_EXPORT Document : public ContainerNode, const KURL& CookieURL() const { return cookie_url_; } void SetCookieURL(const KURL& url) { cookie_url_ = url; } + scoped_refptr TopFrameOrigin() const; + const KURL SiteForCookies() const; // The following implements the rule from HTML 4 for what valid names are. diff --git a/third_party/blink/renderer/core/exported/web_document_test.cc b/third_party/blink/renderer/core/exported/web_document_test.cc index 0f691ce334c46a..9d982f033fe57e 100644 --- a/third_party/blink/renderer/core/exported/web_document_test.cc +++ b/third_party/blink/renderer/core/exported/web_document_test.cc @@ -338,10 +338,17 @@ Document* WebDocumentFirstPartyTest::NestedNestedDocument() const { ->GetDocument(); } +bool OriginsEqual(const char* path, + scoped_refptr origin) { + return SecurityOrigin::Create(ToOriginA(path)) + ->IsSameSchemeHostPort(origin.get()); +} + TEST_F(WebDocumentFirstPartyTest, Empty) { Load(g_empty_file); ASSERT_EQ(ToOriginA(g_empty_file), TopDocument()->SiteForCookies()); + ASSERT_TRUE(OriginsEqual(g_empty_file, TopDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedOriginA) { @@ -349,6 +356,10 @@ TEST_F(WebDocumentFirstPartyTest, NestedOriginA) { ASSERT_EQ(ToOriginA(g_nested_origin_a), TopDocument()->SiteForCookies()); ASSERT_EQ(ToOriginA(g_nested_origin_a), NestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_origin_a, TopDocument()->TopFrameOrigin())); + ASSERT_TRUE( + OriginsEqual(g_nested_origin_a, NestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedOriginSubA) { @@ -357,6 +368,11 @@ TEST_F(WebDocumentFirstPartyTest, NestedOriginSubA) { ASSERT_EQ(ToOriginA(g_nested_origin_sub_a), TopDocument()->SiteForCookies()); ASSERT_EQ(ToOriginA(g_nested_origin_sub_a), NestedDocument()->SiteForCookies()); + + ASSERT_TRUE( + OriginsEqual(g_nested_origin_sub_a, TopDocument()->TopFrameOrigin())); + ASSERT_TRUE( + OriginsEqual(g_nested_origin_sub_a, NestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedOriginSecureA) { @@ -366,6 +382,11 @@ TEST_F(WebDocumentFirstPartyTest, NestedOriginSecureA) { TopDocument()->SiteForCookies()); ASSERT_EQ(ToOriginA(g_nested_origin_secure_a), NestedDocument()->SiteForCookies()); + + ASSERT_TRUE( + OriginsEqual(g_nested_origin_secure_a, TopDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_secure_a, + NestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedOriginAInOriginA) { @@ -377,6 +398,11 @@ TEST_F(WebDocumentFirstPartyTest, NestedOriginAInOriginA) { NestedDocument()->SiteForCookies()); ASSERT_EQ(ToOriginA(g_nested_origin_a_in_origin_a), NestedNestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_origin_a_in_origin_a, + TopDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_a_in_origin_a, + NestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedOriginAInOriginB) { @@ -386,6 +412,13 @@ TEST_F(WebDocumentFirstPartyTest, NestedOriginAInOriginB) { TopDocument()->SiteForCookies()); ASSERT_EQ(NullURL(), NestedDocument()->SiteForCookies()); ASSERT_EQ(NullURL(), NestedNestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_origin_a_in_origin_b, + TopDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_a_in_origin_b, + NestedDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_a_in_origin_b, + NestedNestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedOriginB) { @@ -393,6 +426,10 @@ TEST_F(WebDocumentFirstPartyTest, NestedOriginB) { ASSERT_EQ(ToOriginA(g_nested_origin_b), TopDocument()->SiteForCookies()); ASSERT_EQ(NullURL(), NestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_origin_b, TopDocument()->TopFrameOrigin())); + ASSERT_TRUE( + OriginsEqual(g_nested_origin_b, NestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedOriginBInOriginA) { @@ -403,6 +440,13 @@ TEST_F(WebDocumentFirstPartyTest, NestedOriginBInOriginA) { ASSERT_EQ(ToOriginA(g_nested_origin_b_in_origin_a), NestedDocument()->SiteForCookies()); ASSERT_EQ(NullURL(), NestedNestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_origin_b_in_origin_a, + TopDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_b_in_origin_a, + NestedDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_b_in_origin_a, + NestedNestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedOriginBInOriginB) { @@ -412,6 +456,13 @@ TEST_F(WebDocumentFirstPartyTest, NestedOriginBInOriginB) { TopDocument()->SiteForCookies()); ASSERT_EQ(NullURL(), NestedDocument()->SiteForCookies()); ASSERT_EQ(NullURL(), NestedNestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_origin_b_in_origin_b, + TopDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_b_in_origin_b, + NestedDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_b_in_origin_b, + NestedNestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedSrcdoc) { @@ -419,6 +470,10 @@ TEST_F(WebDocumentFirstPartyTest, NestedSrcdoc) { ASSERT_EQ(ToOriginA(g_nested_src_doc), TopDocument()->SiteForCookies()); ASSERT_EQ(ToOriginA(g_nested_src_doc), NestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_src_doc, TopDocument()->TopFrameOrigin())); + ASSERT_TRUE( + OriginsEqual(g_nested_src_doc, NestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, NestedData) { @@ -426,6 +481,9 @@ TEST_F(WebDocumentFirstPartyTest, NestedData) { ASSERT_EQ(ToOriginA(g_nested_data), TopDocument()->SiteForCookies()); ASSERT_EQ(NullURL(), NestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_data, TopDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_data, NestedDocument()->TopFrameOrigin())); } TEST_F(WebDocumentFirstPartyTest, @@ -440,6 +498,13 @@ TEST_F(WebDocumentFirstPartyTest, NestedDocument()->SiteForCookies()); ASSERT_EQ(ToOriginA(g_nested_origin_a_in_origin_b), NestedNestedDocument()->SiteForCookies()); + + ASSERT_TRUE(OriginsEqual(g_nested_origin_a_in_origin_b, + TopDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_a_in_origin_b, + NestedDocument()->TopFrameOrigin())); + ASSERT_TRUE(OriginsEqual(g_nested_origin_a_in_origin_b, + NestedNestedDocument()->TopFrameOrigin())); } } // namespace blink diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc index 67dacb3285fcd2..ca3287c160e19e 100644 --- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc +++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc @@ -232,6 +232,7 @@ struct FrameFetchContext::FrozenState final const base::Optional& address_space, const ContentSecurityPolicy* content_security_policy, KURL site_for_cookies, + scoped_refptr top_frame_origin, const ClientHintsPreferences& client_hints_preferences, float device_pixel_ratio, const String& user_agent, @@ -242,6 +243,7 @@ struct FrameFetchContext::FrozenState final address_space(address_space), content_security_policy(content_security_policy), site_for_cookies(site_for_cookies), + top_frame_origin(std::move(top_frame_origin)), client_hints_preferences(client_hints_preferences), device_pixel_ratio(device_pixel_ratio), user_agent(user_agent), @@ -253,6 +255,7 @@ struct FrameFetchContext::FrozenState final const base::Optional address_space; const Member content_security_policy; const KURL site_for_cookies; + const scoped_refptr top_frame_origin; const ClientHintsPreferences client_hints_preferences; const float device_pixel_ratio; const String user_agent; @@ -520,6 +523,7 @@ void FrameFetchContext::DispatchDidChangeResourcePriority( void FrameFetchContext::PrepareRequest(ResourceRequest& request, RedirectType redirect_type) { SetFirstPartyCookie(request); + request.SetTopFrameOrigin(GetTopFrameOrigin()); String user_agent = GetUserAgent(); request.SetHTTPUserAgent(AtomicString(user_agent)); @@ -1123,6 +1127,15 @@ bool FrameFetchContext::IsFirstPartyOrigin(const KURL& url) const { ->IsSameSchemeHostPort(SecurityOrigin::Create(url).get()); } +scoped_refptr FrameFetchContext::GetTopFrameOrigin() + const { + if (IsDetached()) + return frozen_state_->top_frame_origin; + + Document* document = document_ ? document_.Get() : GetFrame()->GetDocument(); + return document->TopFrameOrigin(); +} + bool FrameFetchContext::ShouldBlockRequestByInspector(const KURL& url) const { if (IsDetached()) return false; @@ -1434,7 +1447,7 @@ FetchContext* FrameFetchContext::Detach() { if (document_) { frozen_state_ = MakeGarbageCollected( Url(), GetParentSecurityOrigin(), GetAddressSpace(), - GetContentSecurityPolicy(), GetSiteForCookies(), + GetContentSecurityPolicy(), GetSiteForCookies(), GetTopFrameOrigin(), GetClientHintsPreferences(), GetDevicePixelRatio(), GetUserAgent(), IsMainFrame(), IsSVGImageChromeClient()); SetFetchClientSettingsObject( @@ -1444,7 +1457,7 @@ FetchContext* FrameFetchContext::Detach() { // Some getters are unavailable in this case. frozen_state_ = MakeGarbageCollected( NullURL(), GetParentSecurityOrigin(), GetAddressSpace(), - GetContentSecurityPolicy(), GetSiteForCookies(), + GetContentSecurityPolicy(), GetSiteForCookies(), GetTopFrameOrigin(), GetClientHintsPreferences(), GetDevicePixelRatio(), GetUserAgent(), IsMainFrame(), IsSVGImageChromeClient()); SetFetchClientSettingsObject( diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.h b/third_party/blink/renderer/core/loader/frame_fetch_context.h index 71fb9fe7e0825d..973b4a0dc59d66 100644 --- a/third_party/blink/renderer/core/loader/frame_fetch_context.h +++ b/third_party/blink/renderer/core/loader/frame_fetch_context.h @@ -263,6 +263,9 @@ class CORE_EXPORT FrameFetchContext final : public BaseFetchContext { // frame's main resource. bool IsFirstPartyOrigin(const KURL& url) const; + // Returns the origin of the top frame in the document. + scoped_refptr GetTopFrameOrigin() const; + Member document_loader_; Member document_; diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc index c387c5d94929bc..51f57324b7df5d 100644 --- a/third_party/blink/renderer/platform/exported/web_url_request.cc +++ b/third_party/blink/renderer/platform/exported/web_url_request.cc @@ -101,6 +101,16 @@ void WebURLRequest::SetSiteForCookies(const WebURL& site_for_cookies) { resource_request_->SetSiteForCookies(site_for_cookies); } +base::Optional WebURLRequest::TopFrameOrigin() const { + const SecurityOrigin* origin = resource_request_->TopFrameOrigin(); + return origin ? base::Optional(origin) + : base::Optional(); +} + +void WebURLRequest::SetTopFrameOrigin(const WebSecurityOrigin& origin) { + resource_request_->SetTopFrameOrigin(origin); +} + WebSecurityOrigin WebURLRequest::RequestorOrigin() const { return resource_request_->RequestorOrigin(); } diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc index e777fa5f57ca2a..14324af39ecaff 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc @@ -547,6 +547,7 @@ static bool IsManualRedirectFetchRequest(const ResourceRequest& request) { bool ResourceLoader::WillFollowRedirect( const WebURL& new_url, const WebURL& new_site_for_cookies, + const base::Optional& new_top_frame_origin, const WebString& new_referrer, network::mojom::ReferrerPolicy new_referrer_policy, const WebString& new_method, @@ -561,10 +562,15 @@ bool ResourceLoader::WillFollowRedirect( return false; } + scoped_refptr top_frame_origin = + new_top_frame_origin + ? base::WrapRefCounted(new_top_frame_origin.value().Get()) + : scoped_refptr(); std::unique_ptr new_request = resource_->LastResourceRequest().CreateRedirectRequest( - new_url, new_method, new_site_for_cookies, new_referrer, - new_referrer_policy, + new_url, new_method, new_site_for_cookies, top_frame_origin, + new_referrer, new_referrer_policy, + !passed_redirect_response.WasFetchedViaServiceWorker()); ResourceType resource_type = resource_->GetType(); diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h index 2cc7f9cae9dea9..c70f9497838592 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h +++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h @@ -109,13 +109,15 @@ class PLATFORM_EXPORT ResourceLoader final // A failed load is indicated by 1 DidFail(), which can occur at any time // before DidFinishLoading(), including synchronous inside one of the other // callbacks via ResourceLoader::cancel() - bool WillFollowRedirect(const WebURL& new_url, - const WebURL& new_site_for_cookies, - const WebString& new_referrer, - network::mojom::ReferrerPolicy new_referrer_policy, - const WebString& new_method, - const WebURLResponse& passed_redirect_response, - bool& report_raw_headers) override; + bool WillFollowRedirect( + const WebURL& new_url, + const WebURL& new_site_for_cookies, + const base::Optional& new_top_frame_origin, + const WebString& new_referrer, + network::mojom::ReferrerPolicy new_referrer_policy, + const WebString& new_method, + const WebURLResponse& passed_redirect_response, + bool& report_raw_headers) override; void DidSendData(unsigned long long bytes_sent, unsigned long long total_bytes_to_be_sent) override; void DidReceiveResponse(const WebURLResponse&) override; diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc index 4e0c30afdc0a77..0fa7840bb80f8b 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc @@ -93,6 +93,7 @@ std::unique_ptr ResourceRequest::CreateRedirectRequest( const KURL& new_url, const AtomicString& new_method, const KURL& new_site_for_cookies, + scoped_refptr new_top_frame_origin, const String& new_referrer, network::mojom::ReferrerPolicy new_referrer_policy, bool skip_service_worker) const { @@ -101,6 +102,7 @@ std::unique_ptr ResourceRequest::CreateRedirectRequest( request->SetRequestorOrigin(RequestorOrigin()); request->SetHTTPMethod(new_method); request->SetSiteForCookies(new_site_for_cookies); + request->SetTopFrameOrigin(std::move(new_top_frame_origin)); String referrer = new_referrer.IsEmpty() ? Referrer::NoReferrer() : String(new_referrer); // TODO(domfarolino): Stop storing ResourceRequest's generated referrer as a @@ -181,6 +183,15 @@ void ResourceRequest::SetSiteForCookies(const KURL& site_for_cookies) { site_for_cookies_ = site_for_cookies; } +const SecurityOrigin* ResourceRequest::TopFrameOrigin() const { + return top_frame_origin_.get(); +} + +void ResourceRequest::SetTopFrameOrigin( + scoped_refptr origin) { + top_frame_origin_ = std::move(origin); +} + const AtomicString& ResourceRequest::HttpMethod() const { return http_method_; } diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h index a50a101eb7c969..518ddf9638ab54 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h +++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h @@ -84,6 +84,7 @@ class PLATFORM_EXPORT ResourceRequest final { const KURL& new_url, const AtomicString& new_method, const KURL& new_site_for_cookies, + scoped_refptr new_top_frame_origin, const String& new_referrer, network::mojom::ReferrerPolicy new_referrer_policy, bool skip_service_worker) const; @@ -104,6 +105,9 @@ class PLATFORM_EXPORT ResourceRequest final { const KURL& SiteForCookies() const; void SetSiteForCookies(const KURL&); + const SecurityOrigin* TopFrameOrigin() const; + void SetTopFrameOrigin(scoped_refptr); + // The origin of the request, specified at // https://fetch.spec.whatwg.org/#concept-request-origin. This origin can be // null upon request, corresponding to the "client" value in the spec. In that @@ -438,6 +442,7 @@ class PLATFORM_EXPORT ResourceRequest final { // TimeDelta::Max() represents the default timeout on platforms that have one. base::TimeDelta timeout_interval_; KURL site_for_cookies_; + scoped_refptr top_frame_origin_; scoped_refptr requestor_origin_; diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc index ffb5fbcea4d7df..188cbd4ecc1447 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc @@ -33,11 +33,28 @@ TEST(ResourceRequestTest, SetIsAdResource) { std::unique_ptr redirect_request = original.CreateRedirectRequest( KURL("https://example.test/redirect"), original.HttpMethod(), - original.SiteForCookies(), original.HttpReferrer(), - original.GetReferrerPolicy(), original.GetSkipServiceWorker()); + original.SiteForCookies(), original.TopFrameOrigin(), + original.HttpReferrer(), original.GetReferrerPolicy(), + original.GetSkipServiceWorker()); EXPECT_TRUE(redirect_request->IsAdResource()); } +TEST(ResourceRequestTest, SetTopFrameURL) { + KURL url("http://example.com"); + scoped_refptr origin = SecurityOrigin::Create(url); + ResourceRequest original; + original.SetTopFrameOrigin(origin); + + // Should persist across redirects. + std::unique_ptr redirect_request = + original.CreateRedirectRequest( + KURL("https://example.test/redirect"), original.HttpMethod(), + original.SiteForCookies(), original.TopFrameOrigin(), + original.HttpReferrer(), original.GetReferrerPolicy(), + original.GetSkipServiceWorker()); + EXPECT_EQ(origin, redirect_request->TopFrameOrigin()); +} + TEST(ResourceRequestTest, UpgradeIfInsecureAcrossRedirects) { ResourceRequest original; EXPECT_FALSE(original.UpgradeIfInsecure()); @@ -48,8 +65,9 @@ TEST(ResourceRequestTest, UpgradeIfInsecureAcrossRedirects) { std::unique_ptr redirect_request = original.CreateRedirectRequest( KURL("https://example.test/redirect"), original.HttpMethod(), - original.SiteForCookies(), original.HttpReferrer(), - original.GetReferrerPolicy(), original.GetSkipServiceWorker()); + original.SiteForCookies(), original.TopFrameOrigin(), + original.HttpReferrer(), original.GetReferrerPolicy(), + original.GetSkipServiceWorker()); EXPECT_TRUE(redirect_request->UpgradeIfInsecure()); } diff --git a/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc b/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc index 93d1e97824bc58..3a42fa36d90f09 100644 --- a/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc +++ b/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc @@ -8,6 +8,7 @@ #include "third_party/blink/public/platform/url_conversion.h" #include "third_party/blink/public/platform/web_data.h" +#include "third_party/blink/public/platform/web_security_origin.h" #include "third_party/blink/public/platform/web_url_error.h" #include "third_party/blink/public/platform/web_url_loader_client.h" #include "third_party/blink/renderer/platform/shared_buffer.h" @@ -93,7 +94,8 @@ WebURL WebURLLoaderMock::ServeRedirect( bool report_raw_headers = false; bool follow = client_->WillFollowRedirect( - redirect_url, redirect_url, WebString(), + redirect_url, redirect_url, + WebSecurityOrigin::Create(WebURL(redirect_url)), WebString(), network::mojom::ReferrerPolicy::kDefault, request.HttpMethod(), redirect_response, report_raw_headers); // |this| might be deleted in willFollowRedirect().