diff options
Diffstat (limited to 'chromium/content/browser/loader')
15 files changed, 1050 insertions, 1096 deletions
diff --git a/chromium/content/browser/loader/cached_navigation_url_loader.cc b/chromium/content/browser/loader/cached_navigation_url_loader.cc index 54c58fdb6ba..4f387dd0f7e 100644 --- a/chromium/content/browser/loader/cached_navigation_url_loader.cc +++ b/chromium/content/browser/loader/cached_navigation_url_loader.cc @@ -4,7 +4,6 @@ #include "content/browser/loader/cached_navigation_url_loader.h" -#include "base/task/post_task.h" #include "content/browser/frame_host/navigation_request_info.h" #include "content/browser/loader/navigation_url_loader_delegate.h" #include "content/browser/loader/navigation_url_loader_impl.h" @@ -28,8 +27,8 @@ CachedNavigationURLLoader::CachedNavigationURLLoader( // Normal navigations never call OnResponseStarted on the same message loop // iteration that the NavigationURLLoader is created, because they have to // make a network request. - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&CachedNavigationURLLoader::OnResponseStarted, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&CachedNavigationURLLoader::OnResponseStarted, weak_factory_.GetWeakPtr())); } diff --git a/chromium/content/browser/loader/cross_origin_read_blocking_checker.cc b/chromium/content/browser/loader/cross_origin_read_blocking_checker.cc index 3b4298fff67..003f89024a5 100644 --- a/chromium/content/browser/loader/cross_origin_read_blocking_checker.cc +++ b/chromium/content/browser/loader/cross_origin_read_blocking_checker.cc @@ -5,8 +5,8 @@ #include "content/browser/loader/cross_origin_read_blocking_checker.h" #include "base/callback.h" -#include "base/task/post_task.h" #include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" #include "net/base/mime_sniffer.h" #include "services/network/cross_origin_read_blocking.h" @@ -71,14 +71,14 @@ class CrossOriginReadBlockingChecker::BlobIOState { } void OnNetError() { - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&CrossOriginReadBlockingChecker::OnNetError, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&CrossOriginReadBlockingChecker::OnNetError, checker_, blob_reader_->net_error())); } void OnReadComplete(int bytes_read) { - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&CrossOriginReadBlockingChecker::OnReadComplete, checker_, bytes_read, buffer_, blob_reader_->net_error())); @@ -127,8 +127,8 @@ CrossOriginReadBlockingChecker::CrossOriginReadBlockingChecker( std::make_unique<storage::BlobDataHandle>(blob_data_handle)); // base::Unretained is safe because |blob_io_state_| will be deleted on // the IO thread. - base::PostTask(FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&BlobIOState::StartSniffing, + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BlobIOState::StartSniffing, base::Unretained(blob_io_state_.get()))); return; } @@ -137,7 +137,7 @@ CrossOriginReadBlockingChecker::CrossOriginReadBlockingChecker( } CrossOriginReadBlockingChecker::~CrossOriginReadBlockingChecker() { - base::DeleteSoon(FROM_HERE, {BrowserThread::IO}, std::move(blob_io_state_)); + GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE, std::move(blob_io_state_)); } int CrossOriginReadBlockingChecker::GetNetError() { diff --git a/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc b/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc index 7e630c8000f..bc26b111691 100644 --- a/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc +++ b/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc @@ -17,10 +17,10 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/task/post_task.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" +#include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "content/browser/site_instance_impl.h" #include "content/public/browser/browser_task_traits.h" @@ -310,9 +310,10 @@ class RequestInterceptor { } if (!got_all_data) { - base::PostTask(FROM_HERE, base::BindOnce(&RequestInterceptor::ReadBody, - base::Unretained(this), - std::move(completion_callback))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&RequestInterceptor::ReadBody, base::Unretained(this), + std::move(completion_callback))); } else { std::move(completion_callback).Run(); } diff --git a/chromium/content/browser/loader/loader_browsertest.cc b/chromium/content/browser/loader/loader_browsertest.cc index f82d19ea842..a229d260140 100644 --- a/chromium/content/browser/loader/loader_browsertest.cc +++ b/chromium/content/browser/loader/loader_browsertest.cc @@ -15,7 +15,6 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/task/post_task.h" #include "base/test/bind_test_util.h" #include "build/build_config.h" #include "content/browser/download/download_manager_impl.h" @@ -68,11 +67,11 @@ class LoaderBrowserTest : public ContentBrowserTest, protected: void SetUpOnMainThread() override { base::FilePath path = GetTestFilePath("", ""); - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&net::URLRequestMockHTTPJob::AddUrlHandlers, path)); - base::PostTask(FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler)); + GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler)); host_resolver()->AddRule("*", "127.0.0.1"); } @@ -281,8 +280,8 @@ std::unique_ptr<net::test_server::HttpResponse> CancelOnRequest( if (request.relative_url != relative_url) return nullptr; - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - crash_network_service_callback); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, + crash_network_service_callback); return std::make_unique<net::test_server::HungResponse>(); } diff --git a/chromium/content/browser/loader/navigation_url_loader_impl.cc b/chromium/content/browser/loader/navigation_url_loader_impl.cc index 9ecb16d776d..6eb68513704 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl.cc +++ b/chromium/content/browser/loader/navigation_url_loader_impl.cc @@ -16,7 +16,6 @@ #include "base/metrics/histogram_macros.h" #include "base/optional.h" #include "base/stl_util.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" @@ -35,13 +34,11 @@ #include "content/browser/loader/navigation_loader_interceptor.h" #include "content/browser/loader/navigation_url_loader_delegate.h" #include "content/browser/loader/prefetch_url_loader_service.h" -#include "content/browser/loader/single_request_url_loader_factory.h" -#include "content/browser/loader/url_loader_throttles.h" #include "content/browser/navigation_subresource_loader_params.h" #include "content/browser/service_worker/service_worker_container_host.h" #include "content/browser/service_worker/service_worker_main_resource_handle.h" #include "content/browser/service_worker/service_worker_main_resource_handle_core.h" -#include "content/browser/service_worker/service_worker_navigation_loader_interceptor.h" +#include "content/browser/service_worker/service_worker_main_resource_loader_interceptor.h" #include "content/browser/storage_partition_impl.h" #include "content/browser/url_loader_factory_getter.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -62,6 +59,7 @@ #include "content/public/browser/shared_cors_origin_access_list.h" #include "content/public/browser/ssl_status.h" #include "content/public/browser/url_loader_request_interceptor.h" +#include "content/public/browser/url_loader_throttles.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" @@ -142,7 +140,6 @@ class NavigationLoaderInterceptorBrowserContainer std::unique_ptr<URLLoaderRequestInterceptor> browser_interceptor_; }; -// Only used on the IO thread. base::LazyInstance<NavigationURLLoaderImpl::URLLoaderFactoryInterceptor>::Leaky g_loader_factory_interceptor = LAZY_INSTANCE_INITIALIZER; @@ -191,11 +188,11 @@ const net::NetworkTrafficAnnotationTag kNavigationUrlLoaderTrafficAnnotation = "combination of both) limits the scope of these requests." )"); +// TODO(kinuko): |request_info| can likely be given as a const ref. std::unique_ptr<network::ResourceRequest> CreateResourceRequest( NavigationRequestInfo* request_info, int frame_tree_node_id, mojo::PendingRemote<network::mojom::CookieAccessObserver> cookie_observer) { - // TODO(scottmg): Port over stuff from RDHI::BeginNavigationRequest() here. auto new_request = std::make_unique<network::ResourceRequest>(); new_request->method = request_info->common_params->method; @@ -250,41 +247,7 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest( new_request->has_user_gesture = request_info->common_params->has_user_gesture; new_request->enable_load_timing = true; new_request->mode = network::mojom::RequestMode::kNavigate; - - FrameTreeNode* frame_tree_node = - FrameTreeNode::GloballyFindByID(frame_tree_node_id); - if (request_info->is_main_frame) { - new_request->destination = - frame_tree_node && - WebContentsImpl::FromFrameTreeNode(frame_tree_node)->IsPortal() - ? network::mojom::RequestDestination::kIframe - : network::mojom::RequestDestination::kDocument; - } else { - if (frame_tree_node) { - switch (frame_tree_node->frame_owner_element_type()) { - case blink::mojom::FrameOwnerElementType::kObject: - new_request->destination = - network::mojom::RequestDestination::kObject; - break; - case blink::mojom::FrameOwnerElementType::kEmbed: - new_request->destination = network::mojom::RequestDestination::kEmbed; - break; - case blink::mojom::FrameOwnerElementType::kIframe: - new_request->destination = - network::mojom::RequestDestination::kIframe; - break; - case blink::mojom::FrameOwnerElementType::kFrame: - new_request->destination = network::mojom::RequestDestination::kFrame; - break; - case blink::mojom::FrameOwnerElementType::kPortal: - case blink::mojom::FrameOwnerElementType::kNone: - NOTREACHED(); - break; - } - } else { - new_request->destination = network::mojom::RequestDestination::kDocument; - } - } + new_request->destination = request_info->begin_params->request_destination; if (ui::PageTransitionIsWebTriggerable( request_info->common_params->transition)) { @@ -338,578 +301,506 @@ const char* FrameAcceptHeaderValue() { } // namespace -// Kept around during the lifetime of the navigation request, and is -// responsible for dispatching a ResourceRequest to the appropriate -// URLLoader. In order to get the right URLLoader it builds a vector -// of NavigationLoaderInterceptors and successively calls MaybeCreateLoader -// on each until the request is successfully handled. The same sequence -// may be performed multiple times when redirects happen. -// TODO(michaeln): Expose this class and add more unittests. -class NavigationURLLoaderImpl::URLLoaderRequestController - : public network::mojom::URLLoaderClient { - public: - URLLoaderRequestController( - std::vector<std::unique_ptr<NavigationLoaderInterceptor>> - initial_interceptors, - std::unique_ptr<network::ResourceRequest> resource_request, - BrowserContext* browser_context, - StoragePartitionImpl* storage_partition, - const GURL& url, - mojo::PendingReceiver<network::mojom::URLLoaderFactory> - proxied_factory_receiver, - mojo::PendingRemote<network::mojom::URLLoaderFactory> - proxied_factory_remote, - std::set<std::string> known_schemes, - bool bypass_redirect_checks, - const base::WeakPtr<NavigationURLLoaderImpl>& owner) - : interceptors_(std::move(initial_interceptors)), - resource_request_(std::move(resource_request)), - url_(url), - owner_(owner), - proxied_factory_receiver_(std::move(proxied_factory_receiver)), - proxied_factory_remote_(std::move(proxied_factory_remote)), - known_schemes_(std::move(known_schemes)), - bypass_redirect_checks_(bypass_redirect_checks), - browser_context_(browser_context), - storage_partition_(storage_partition), - head_(network::mojom::URLResponseHead::New()) {} - - ~URLLoaderRequestController() override { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - // If neither OnCompleted nor OnReceivedResponse has been invoked, the - // request was canceled before receiving a response, so log a cancellation. - // Results after receiving a non-error response are logged in the renderer, - // if the request is passed to one. If it's a download, or not passed to a - // renderer for some other reason, results will not be logged for the - // request. The net::OK check may not be necessary - the case where OK is - // received without receiving any headers looks broken, anyways. - if (!received_response_ && (!status_ || status_->error_code != net::OK)) { - RecordLoadHistograms(url::Origin::Create(url_), - resource_request_->destination, - status_ ? status_->error_code : net::ERR_ABORTED); - } +// TODO(kinuko): Fix the method ordering and move these methods after the ctor. +NavigationURLLoaderImpl::~NavigationURLLoaderImpl() { + // If neither OnCompleted nor OnReceivedResponse has been invoked, the + // request was canceled before receiving a response, so log a cancellation. + // Results after receiving a non-error response are logged in the renderer, + // if the request is passed to one. If it's a download, or not passed to a + // renderer for some other reason, results will not be logged for the + // request. The net::OK check may not be necessary - the case where OK is + // received without receiving any headers looks broken, anyways. + if (!received_response_ && (!status_ || status_->error_code != net::OK)) { + RecordLoadHistograms(url::Origin::Create(url_), + resource_request_->destination, + status_ ? status_->error_code : net::ERR_ABORTED); } +} - static uint32_t GetURLLoaderOptions(bool is_main_frame) { - uint32_t options = network::mojom::kURLLoadOptionNone; - - // Ensure that Mime sniffing works. - options |= network::mojom::kURLLoadOptionSniffMimeType; +uint32_t NavigationURLLoaderImpl::GetURLLoaderOptions(bool is_main_frame) { + uint32_t options = network::mojom::kURLLoadOptionNone; - if (is_main_frame) { - // SSLInfo is not needed on subframe responses because users can inspect - // only the certificate for the main frame when using the info bubble. - options |= network::mojom::kURLLoadOptionSendSSLInfoWithResponse; - options |= network::mojom::kURLLoadOptionSendSSLInfoForCertificateError; - } + // Ensure that Mime sniffing works. + options |= network::mojom::kURLLoadOptionSniffMimeType; - return options; + if (is_main_frame) { + // SSLInfo is not needed on subframe responses because users can inspect + // only the certificate for the main frame when using the info bubble. + options |= network::mojom::kURLLoadOptionSendSSLInfoWithResponse; + options |= network::mojom::kURLLoadOptionSendSSLInfoForCertificateError; } - void Start( - std::unique_ptr<network::PendingSharedURLLoaderFactory> - pending_network_loader_factory, - ServiceWorkerMainResourceHandle* - service_worker_handle /* for UI thread only */, - ServiceWorkerMainResourceHandleCore* - service_worker_handle_core /* for IO thread only */, - AppCacheNavigationHandle* appcache_handle, - scoped_refptr<PrefetchedSignedExchangeCache> - prefetched_signed_exchange_cache, - scoped_refptr<SignedExchangePrefetchMetricRecorder> - signed_exchange_prefetch_metric_recorder, - std::unique_ptr<NavigationRequestInfo> request_info, - std::unique_ptr<NavigationUIData> navigation_ui_data, - mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_for_webui, - bool needs_loader_factory_interceptor, - base::Time ui_post_time, - std::string accept_langs) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(!started_); - ui_to_io_time_ += (base::Time::Now() - ui_post_time); - global_request_id_ = GlobalRequestID::MakeBrowserInitiated(); - frame_tree_node_id_ = request_info->frame_tree_node_id; - started_ = true; - web_contents_getter_ = base::BindRepeating( - &WebContents::FromFrameTreeNodeId, frame_tree_node_id_); - navigation_ui_data_ = std::move(navigation_ui_data); - service_worker_handle_ = service_worker_handle; - - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&NavigationURLLoaderImpl::OnRequestStarted, - owner_, base::TimeTicks::Now())); - - DCHECK(pending_network_loader_factory); - network_loader_factory_ = network::SharedURLLoaderFactory::Create( - std::move(pending_network_loader_factory)); - if (needs_loader_factory_interceptor && - g_loader_factory_interceptor.Get()) { - mojo::PendingRemote<network::mojom::URLLoaderFactory> factory; - mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver = - factory.InitWithNewPipeAndPassReceiver(); - g_loader_factory_interceptor.Get().Run(&receiver); - network_loader_factory_->Clone(std::move(receiver)); - network_loader_factory_ = - base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( - std::move(factory)); - } + return options; +} - std::string accept_value = FrameAcceptHeaderValue(); - if (signed_exchange_utils::IsSignedExchangeHandlingEnabled( - browser_context_)) { - accept_value.append(kAcceptHeaderSignedExchangeSuffix); - } - resource_request_->headers.SetHeader(net::HttpRequestHeaders::kAccept, - accept_value); +void NavigationURLLoaderImpl::Start( + std::unique_ptr<network::PendingSharedURLLoaderFactory> + pending_network_loader_factory, + AppCacheNavigationHandle* appcache_handle, + scoped_refptr<PrefetchedSignedExchangeCache> + prefetched_signed_exchange_cache, + scoped_refptr<SignedExchangePrefetchMetricRecorder> + signed_exchange_prefetch_metric_recorder, + mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_for_webui, + std::string accept_langs, + bool needs_loader_factory_interceptor) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(!started_); + DCHECK(!head_); + head_ = network::mojom::URLResponseHead::New(); + started_ = true; + + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&NavigationURLLoaderImpl::NotifyRequestStarted, + weak_factory_.GetWeakPtr(), base::TimeTicks::Now())); + + // TODO(kinuko): This can likely be initialized in the ctor. + network_loader_factory_ = network::SharedURLLoaderFactory::Create( + std::move(pending_network_loader_factory)); + if (needs_loader_factory_interceptor && g_loader_factory_interceptor.Get()) { + mojo::PendingRemote<network::mojom::URLLoaderFactory> factory; + mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver = + factory.InitWithNewPipeAndPassReceiver(); + g_loader_factory_interceptor.Get().Run(&receiver); + network_loader_factory_->Clone(std::move(receiver)); + network_loader_factory_ = + base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( + std::move(factory)); + } - // NetworkService cases only. - // Requests to WebUI scheme won't get redirected to/from other schemes - // or be intercepted, so we just let it go here. - if (factory_for_webui.is_valid()) { - url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( - base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( - std::move(factory_for_webui)), - CreateURLLoaderThrottles(), 0 /* routing_id */, - global_request_id_.request_id, network::mojom::kURLLoadOptionNone, - resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation, - base::ThreadTaskRunnerHandle::Get()); - return; - } + std::string accept_header_value = FrameAcceptHeaderValue(); + if (signed_exchange_utils::IsSignedExchangeHandlingEnabled( + browser_context_)) { + accept_header_value.append(kAcceptHeaderSignedExchangeSuffix); + } + resource_request_->headers.SetHeader(net::HttpRequestHeaders::kAccept, + accept_header_value); - // Requests to Blob scheme won't get redirected to/from other schemes - // or be intercepted, so we just let it go here. - if (request_info->common_params->url.SchemeIsBlob() && - request_info->blob_url_loader_factory) { - url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( - network::SharedURLLoaderFactory::Create( - std::move(request_info->blob_url_loader_factory)), - CreateURLLoaderThrottles(), 0 /* routing_id */, - global_request_id_.request_id, network::mojom::kURLLoadOptionNone, - resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation, - base::ThreadTaskRunnerHandle::Get()); - return; - } + // Requests to WebUI scheme won't get redirected to/from other schemes + // or be intercepted, so we just let it go here. + if (factory_for_webui.is_valid()) { + url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( + base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( + std::move(factory_for_webui)), + CreateURLLoaderThrottles(), 0 /* routing_id */, + global_request_id_.request_id, network::mojom::kURLLoadOptionNone, + resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation, + base::ThreadTaskRunnerHandle::Get()); + return; + } - CreateInterceptors(request_info.get(), appcache_handle, - prefetched_signed_exchange_cache, - signed_exchange_prefetch_metric_recorder, accept_langs); - Restart(); + // Requests to Blob scheme won't get redirected to/from other schemes + // or be intercepted, so we just let it go here. + if (request_info_->common_params->url.SchemeIsBlob() && + request_info_->blob_url_loader_factory) { + url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( + network::SharedURLLoaderFactory::Create( + std::move(request_info_->blob_url_loader_factory)), + CreateURLLoaderThrottles(), 0 /* routing_id */, + global_request_id_.request_id, network::mojom::kURLLoadOptionNone, + resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation, + base::ThreadTaskRunnerHandle::Get()); + return; } - void CreateInterceptors(NavigationRequestInfo* request_info, - AppCacheNavigationHandle* appcache_handle, - scoped_refptr<PrefetchedSignedExchangeCache> - prefetched_signed_exchange_cache, - scoped_refptr<SignedExchangePrefetchMetricRecorder> - signed_exchange_prefetch_metric_recorder, - const std::string& accept_langs) { - if (prefetched_signed_exchange_cache) { - std::unique_ptr<NavigationLoaderInterceptor> - prefetched_signed_exchange_interceptor = - prefetched_signed_exchange_cache->MaybeCreateInterceptor( - request_info->common_params->url); - if (prefetched_signed_exchange_interceptor) { - interceptors_.push_back( - std::move(prefetched_signed_exchange_interceptor)); - } - } + CreateInterceptors(appcache_handle, prefetched_signed_exchange_cache, + signed_exchange_prefetch_metric_recorder, accept_langs); + Restart(); +} - // Set up an interceptor for service workers. - if (service_worker_handle_) { - auto service_worker_interceptor = - ServiceWorkerNavigationLoaderInterceptor::CreateForNavigation( - resource_request_->url, service_worker_handle_->AsWeakPtr(), - *request_info); - // The interceptor may not be created in certain cases (e.g., the origin - // is not secure). - if (service_worker_interceptor) - interceptors_.push_back(std::move(service_worker_interceptor)); +void NavigationURLLoaderImpl::CreateInterceptors( + AppCacheNavigationHandle* appcache_handle, + scoped_refptr<PrefetchedSignedExchangeCache> + prefetched_signed_exchange_cache, + scoped_refptr<SignedExchangePrefetchMetricRecorder> + signed_exchange_prefetch_metric_recorder, + const std::string& accept_langs) { + if (prefetched_signed_exchange_cache) { + std::unique_ptr<NavigationLoaderInterceptor> + prefetched_signed_exchange_interceptor = + prefetched_signed_exchange_cache->MaybeCreateInterceptor(url_); + if (prefetched_signed_exchange_interceptor) { + interceptors_.push_back( + std::move(prefetched_signed_exchange_interceptor)); } + } - // Set-up an interceptor for AppCache if non-null |appcache_handle| is - // given. - if (appcache_handle) { - CHECK(appcache_handle->host()); - std::unique_ptr<NavigationLoaderInterceptor> appcache_interceptor = - AppCacheRequestHandler::InitializeForMainResourceNetworkService( - *resource_request_, appcache_handle->host()->GetWeakPtr()); - if (appcache_interceptor) - interceptors_.push_back(std::move(appcache_interceptor)); - } + // Set up an interceptor for service workers. + if (service_worker_handle_) { + auto service_worker_interceptor = + ServiceWorkerMainResourceLoaderInterceptor::CreateForNavigation( + resource_request_->url, service_worker_handle_->AsWeakPtr(), + *request_info_); + // The interceptor may not be created in certain cases (e.g., the origin + // is not secure). + if (service_worker_interceptor) + interceptors_.push_back(std::move(service_worker_interceptor)); + } - // Set-up an interceptor for SignedExchange handling if it is enabled. - if (signed_exchange_utils::IsSignedExchangeHandlingEnabled( - browser_context_)) { - interceptors_.push_back(CreateSignedExchangeRequestHandler( - *request_info, network_loader_factory_, - std::move(signed_exchange_prefetch_metric_recorder), - std::move(accept_langs))); - } + // Set-up an interceptor for AppCache if non-null |appcache_handle| is + // given. + if (appcache_handle) { + CHECK(appcache_handle->host()); + std::unique_ptr<NavigationLoaderInterceptor> appcache_interceptor = + AppCacheRequestHandler::InitializeForMainResourceNetworkService( + *resource_request_, appcache_handle->host()->GetWeakPtr()); + if (appcache_interceptor) + interceptors_.push_back(std::move(appcache_interceptor)); + } - // See if embedders want to add interceptors. - std::vector<std::unique_ptr<URLLoaderRequestInterceptor>> - browser_interceptors = - GetContentClient() - ->browser() - ->WillCreateURLLoaderRequestInterceptors( - navigation_ui_data_.get(), request_info->frame_tree_node_id, - network_loader_factory_); - if (!browser_interceptors.empty()) { - for (auto& browser_interceptor : browser_interceptors) { - interceptors_.push_back( - std::make_unique<NavigationLoaderInterceptorBrowserContainer>( - std::move(browser_interceptor))); - } - } + // Set-up an interceptor for SignedExchange handling if it is enabled. + if (signed_exchange_utils::IsSignedExchangeHandlingEnabled( + browser_context_)) { + interceptors_.push_back(CreateSignedExchangeRequestHandler( + *request_info_, network_loader_factory_, + std::move(signed_exchange_prefetch_metric_recorder), + std::move(accept_langs))); } - // This could be called multiple times to follow a chain of redirects. - void Restart() { - // Clear |url_loader_| if it's not the default one (network). This allows - // the restarted request to use a new loader, instead of, e.g., reusing the - // AppCache or service worker loader. For an optimization, we keep and reuse - // the default url loader if the all |interceptors_| doesn't handle the - // redirected request. If the network service is enabled, reset the loader - // if the redirected URL's scheme and the previous URL scheme don't match in - // their use or disuse of the network service loader. - if (!default_loader_used_ || - (url_chain_.size() > 1 && - IsURLHandledByNetworkService(url_chain_[url_chain_.size() - 1]) != - IsURLHandledByNetworkService(url_chain_[url_chain_.size() - 2]))) { - if (url_loader_) - url_loader_->ResetForFollowRedirect(); - url_loader_.reset(); + // See if embedders want to add interceptors. + std::vector<std::unique_ptr<URLLoaderRequestInterceptor>> + browser_interceptors = + GetContentClient()->browser()->WillCreateURLLoaderRequestInterceptors( + navigation_ui_data_.get(), frame_tree_node_id_, + network_loader_factory_); + if (!browser_interceptors.empty()) { + for (auto& browser_interceptor : browser_interceptors) { + interceptors_.push_back( + std::make_unique<NavigationLoaderInterceptorBrowserContainer>( + std::move(browser_interceptor))); } - interceptor_index_ = 0; - received_response_ = false; - head_ = network::mojom::URLResponseHead::New(); - MaybeStartLoader(nullptr /* interceptor */, - {} /* single_request_factory */); } +} - // |interceptor| is non-null if this is called by one of the interceptors - // (via a LoaderCallback). - // |single_request_handler| is the RequestHandler given by the |interceptor|, - // non-null if the interceptor wants to handle the request. - void MaybeStartLoader( - NavigationLoaderInterceptor* interceptor, - scoped_refptr<network::SharedURLLoaderFactory> single_request_factory) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(started_); - - if (single_request_factory) { - // |interceptor| wants to handle the request with - // |single_request_handler|. - DCHECK(interceptor); - - std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles = - CreateURLLoaderThrottles(); - // Intercepted requests need MimeSniffingThrottle to do mime sniffing. - // Non-intercepted requests usually go through the regular network - // URLLoader, which does mime sniffing. - throttles.push_back(std::make_unique<blink::MimeSniffingThrottle>( - base::ThreadTaskRunnerHandle::Get())); +void NavigationURLLoaderImpl::Restart() { + // Clear |url_loader_| if it's not the default one (network). This allows + // the restarted request to use a new loader, instead of, e.g., reusing the + // AppCache or service worker loader. For an optimization, we keep and reuse + // the default url loader if the all |interceptors_| doesn't handle the + // redirected request. If the network service is enabled, reset the loader + // if the redirected URL's scheme and the previous URL scheme don't match in + // their use or disuse of the network service loader. + if (!default_loader_used_ || + (url_chain_.size() > 1 && + IsURLHandledByNetworkService(url_chain_[url_chain_.size() - 1]) != + IsURLHandledByNetworkService(url_chain_[url_chain_.size() - 2]))) { + if (url_loader_) + url_loader_->ResetForFollowRedirect(); + url_loader_.reset(); + } + interceptor_index_ = 0; + received_response_ = false; + head_ = network::mojom::URLResponseHead::New(); + MaybeStartLoader(nullptr /* interceptor */, {} /* single_request_factory */); +} - default_loader_used_ = false; - // If |url_loader_| already exists, this means we are following a redirect - // using an interceptor. In this case we should make sure to reset the - // loader, similar to what is done in Restart(). - if (url_loader_) - url_loader_->ResetForFollowRedirect(); - url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( - std::move(single_request_factory), std::move(throttles), - frame_tree_node_id_, global_request_id_.request_id, - network::mojom::kURLLoadOptionNone, resource_request_.get(), this, - kNavigationUrlLoaderTrafficAnnotation, - base::ThreadTaskRunnerHandle::Get()); - - subresource_loader_params_ = - interceptor->MaybeCreateSubresourceLoaderParams(); - if (interceptor->ShouldBypassRedirectChecks()) - bypass_redirect_checks_ = true; - return; - } +void NavigationURLLoaderImpl::MaybeStartLoader( + NavigationLoaderInterceptor* interceptor, + scoped_refptr<network::SharedURLLoaderFactory> single_request_factory) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(started_); + + if (single_request_factory) { + // |interceptor| wants to handle the request with + // |single_request_handler|. + DCHECK(interceptor); + + std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles = + CreateURLLoaderThrottles(); + // Intercepted requests need MimeSniffingThrottle to do mime sniffing. + // Non-intercepted requests usually go through the regular network + // URLLoader, which does mime sniffing. + throttles.push_back(std::make_unique<blink::MimeSniffingThrottle>( + base::ThreadTaskRunnerHandle::Get())); + + default_loader_used_ = false; + // If |url_loader_| already exists, this means we are following a redirect + // using an interceptor. In this case we should make sure to reset the + // loader, similar to what is done in Restart(). + if (url_loader_) + url_loader_->ResetForFollowRedirect(); + url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( + std::move(single_request_factory), std::move(throttles), + frame_tree_node_id_, global_request_id_.request_id, + network::mojom::kURLLoadOptionNone, resource_request_.get(), this, + kNavigationUrlLoaderTrafficAnnotation, + base::ThreadTaskRunnerHandle::Get()); - // Before falling back to the next interceptor, see if |interceptor| still - // wants to give additional info to the frame for subresource loading. In - // that case we will just fall back to the default loader (i.e. won't go on - // to the next interceptors) but send the subresource_loader_params to the - // child process. This is necessary for correctness in the cases where, e.g. - // there's a controlling service worker that doesn't have a fetch event - // handler so it doesn't intercept requests. In that case we still want to - // skip AppCache. - if (interceptor) { - subresource_loader_params_ = - interceptor->MaybeCreateSubresourceLoaderParams(); - - // If non-null |subresource_loader_params_| is returned, make sure - // we skip the next interceptors. - if (subresource_loader_params_) - interceptor_index_ = interceptors_.size(); - } + subresource_loader_params_ = + interceptor->MaybeCreateSubresourceLoaderParams(); + if (interceptor->ShouldBypassRedirectChecks()) + bypass_redirect_checks_ = true; + return; + } - // See if the next interceptor wants to handle the request. - if (interceptor_index_ < interceptors_.size()) { - auto* next_interceptor = interceptors_[interceptor_index_++].get(); - next_interceptor->MaybeCreateLoader( - *resource_request_, browser_context_, - base::BindOnce(&URLLoaderRequestController::MaybeStartLoader, - base::Unretained(this), next_interceptor), - base::BindOnce( - &URLLoaderRequestController::FallbackToNonInterceptedRequest, - base::Unretained(this))); - return; - } + // Before falling back to the next interceptor, see if |interceptor| still + // wants to give additional info to the frame for subresource loading. In + // that case we will just fall back to the default loader (i.e. won't go on + // to the next interceptors) but send the subresource_loader_params to the + // child process. This is necessary for correctness in the cases where, e.g. + // there's a controlling service worker that doesn't have a fetch event + // handler so it doesn't intercept requests. In that case we still want to + // skip AppCache. + if (interceptor) { + subresource_loader_params_ = + interceptor->MaybeCreateSubresourceLoaderParams(); + + // If non-null |subresource_loader_params_| is returned, make sure + // we skip the next interceptors. + if (subresource_loader_params_) + interceptor_index_ = interceptors_.size(); + } - // If we already have the default |url_loader_| we must come here after a - // redirect. No interceptors wanted to intercept the redirected request, so - // let the loader just follow the redirect. - if (url_loader_) { - DCHECK(!redirect_info_.new_url.is_empty()); - url_loader_->FollowRedirect( - std::move(url_loader_removed_headers_), - std::move(url_loader_modified_headers_), - std::move(url_loader_modified_cors_exempt_headers_)); - return; - } + // See if the next interceptor wants to handle the request. + if (interceptor_index_ < interceptors_.size()) { + auto* next_interceptor = interceptors_[interceptor_index_++].get(); + next_interceptor->MaybeCreateLoader( + *resource_request_, browser_context_, + base::BindOnce(&NavigationURLLoaderImpl::MaybeStartLoader, + base::Unretained(this), next_interceptor), + base::BindOnce( + &NavigationURLLoaderImpl::FallbackToNonInterceptedRequest, + base::Unretained(this))); + return; + } + + // If we already have the default |url_loader_| we must come here after a + // redirect. No interceptors wanted to intercept the redirected request, so + // let the loader just follow the redirect. + if (url_loader_) { + DCHECK(!redirect_info_.new_url.is_empty()); + url_loader_->FollowRedirect( + std::move(url_loader_removed_headers_), + std::move(url_loader_modified_headers_), + std::move(url_loader_modified_cors_exempt_headers_)); + return; + } + + // No interceptors wanted to handle this request. + uint32_t options = network::mojom::kURLLoadOptionNone; + scoped_refptr<network::SharedURLLoaderFactory> factory = + PrepareForNonInterceptedRequest(&options); + url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( + std::move(factory), CreateURLLoaderThrottles(), frame_tree_node_id_, + global_request_id_.request_id, options, resource_request_.get(), + this /* client */, kNavigationUrlLoaderTrafficAnnotation, + base::ThreadTaskRunnerHandle::Get()); +} - // No interceptors wanted to handle this request. - uint32_t options = network::mojom::kURLLoadOptionNone; - scoped_refptr<network::SharedURLLoaderFactory> factory = - PrepareForNonInterceptedRequest(&options); +void NavigationURLLoaderImpl::FallbackToNonInterceptedRequest( + bool reset_subresource_loader_params) { + if (reset_subresource_loader_params) + subresource_loader_params_.reset(); + + uint32_t options = network::mojom::kURLLoadOptionNone; + scoped_refptr<network::SharedURLLoaderFactory> factory = + PrepareForNonInterceptedRequest(&options); + if (url_loader_) { + // |url_loader_| is using the factory for the interceptor that decided to + // fallback, so restart it with the non-interceptor factory. + url_loader_->RestartWithFactory(std::move(factory), options); + } else { + // In SXG cases we don't have |url_loader_| because it was reset when the + // SXG interceptor intercepted the response in + // MaybeCreateLoaderForResponse. + DCHECK(response_loader_receiver_.is_bound()); + response_loader_receiver_.reset(); url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( std::move(factory), CreateURLLoaderThrottles(), frame_tree_node_id_, global_request_id_.request_id, options, resource_request_.get(), this /* client */, kNavigationUrlLoaderTrafficAnnotation, base::ThreadTaskRunnerHandle::Get()); } +} - // This is the |fallback_callback| passed to - // NavigationLoaderInterceptor::MaybeCreateLoader. It allows an interceptor - // to initially elect to handle a request, and later decide to fallback to - // the default behavior. This is needed for service worker network fallback - // and signed exchange (SXG) fallback redirect. - void FallbackToNonInterceptedRequest(bool reset_subresource_loader_params) { - if (reset_subresource_loader_params) - subresource_loader_params_.reset(); - - uint32_t options = network::mojom::kURLLoadOptionNone; - scoped_refptr<network::SharedURLLoaderFactory> factory = - PrepareForNonInterceptedRequest(&options); - if (url_loader_) { - // |url_loader_| is using the factory for the interceptor that decided to - // fallback, so restart it with the non-interceptor factory. - url_loader_->RestartWithFactory(std::move(factory), options); - } else { - // In SXG cases we don't have |url_loader_| because it was reset when the - // SXG interceptor intercepted the response in - // MaybeCreateLoaderForResponse. - DCHECK(response_loader_receiver_.is_bound()); - response_loader_receiver_.reset(); - url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( - std::move(factory), CreateURLLoaderThrottles(), frame_tree_node_id_, - global_request_id_.request_id, options, resource_request_.get(), - this /* client */, kNavigationUrlLoaderTrafficAnnotation, - base::ThreadTaskRunnerHandle::Get()); - } - } - - scoped_refptr<network::SharedURLLoaderFactory> - PrepareForNonInterceptedRequest(uint32_t* out_options) { - // TODO(https://crbug.com/796425): We temporarily wrap raw - // mojom::URLLoaderFactory pointers into SharedURLLoaderFactory. Need to - // further refactor the factory getters to avoid this. - scoped_refptr<network::SharedURLLoaderFactory> factory; - - if (!IsURLHandledByNetworkService(resource_request_->url)) { - if (known_schemes_.find(resource_request_->url.scheme()) == - known_schemes_.end()) { - mojo::PendingRemote<network::mojom::URLLoaderFactory> loader_factory; - bool handled = GetContentClient()->browser()->HandleExternalProtocol( - resource_request_->url, web_contents_getter_, - ChildProcessHost::kInvalidUniqueID, navigation_ui_data_.get(), - resource_request_->resource_type == - static_cast<int>(blink::mojom::ResourceType::kMainFrame), - static_cast<ui::PageTransition>(resource_request_->transition_type), - resource_request_->has_user_gesture, - resource_request_->request_initiator, &loader_factory); - - if (loader_factory) { - factory = - base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( - std::move(loader_factory)); - } else { - factory = base::MakeRefCounted<SingleRequestURLLoaderFactory>( - base::BindOnce(UnknownSchemeCallback, handled)); - } - } else { - mojo::Remote<network::mojom::URLLoaderFactory>& non_network_factory = - non_network_url_loader_factories_[resource_request_->url.scheme()]; - if (!non_network_factory.is_bound()) { - owner_->BindNonNetworkURLLoaderFactoryReceiver( - frame_tree_node_id_, resource_request_->url, - non_network_factory.BindNewPipeAndPassReceiver()); - } - factory = - base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( - non_network_factory.get()); - } - - if (g_loader_factory_interceptor.Get()) { - mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote; - mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver = - factory_remote.InitWithNewPipeAndPassReceiver(); - g_loader_factory_interceptor.Get().Run(&receiver); - factory->Clone(std::move(receiver)); +scoped_refptr<network::SharedURLLoaderFactory> +NavigationURLLoaderImpl::PrepareForNonInterceptedRequest( + uint32_t* out_options) { + // TODO(https://crbug.com/796425): We temporarily wrap raw + // mojom::URLLoaderFactory pointers into SharedURLLoaderFactory. Need to + // further refactor the factory getters to avoid this. + scoped_refptr<network::SharedURLLoaderFactory> factory; + + if (!IsURLHandledByNetworkService(resource_request_->url)) { + if (known_schemes_.find(resource_request_->url.scheme()) == + known_schemes_.end()) { + mojo::PendingRemote<network::mojom::URLLoaderFactory> loader_factory; + bool handled = GetContentClient()->browser()->HandleExternalProtocol( + resource_request_->url, web_contents_getter_, + ChildProcessHost::kInvalidUniqueID, navigation_ui_data_.get(), + resource_request_->resource_type == + static_cast<int>(blink::mojom::ResourceType::kMainFrame), + static_cast<ui::PageTransition>(resource_request_->transition_type), + resource_request_->has_user_gesture, + resource_request_->request_initiator, &loader_factory); + + if (loader_factory) { factory = base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( - std::move(factory_remote)); + std::move(loader_factory)); + } else { + factory = base::MakeRefCounted<SingleRequestURLLoaderFactory>( + base::BindOnce(UnknownSchemeCallback, handled)); } } else { - default_loader_used_ = true; - - // NOTE: We only support embedders proxying network-service-bound requests - // not handled by NavigationLoaderInterceptors above (e.g. Service Worker - // or AppCache). Hence this code is only reachable when one of the above - // interceptors isn't used and the URL is either a data URL or has a - // scheme which is handled by the network service. - if (proxied_factory_receiver_.is_valid()) { - DCHECK(proxied_factory_remote_.is_valid()); - // We don't worry about reconnection since it's a single navigation. - network_loader_factory_->Clone(std::move(proxied_factory_receiver_)); - // Replace the network factory with the proxied version since this may - // need to be used in redirects, and we've already consumed - // |proxied_factory_receiver_|. - network_loader_factory_ = - base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( - std::move(proxied_factory_remote_)); + mojo::Remote<network::mojom::URLLoaderFactory>& non_network_factory = + non_network_url_loader_factory_remotes_[resource_request_->url + .scheme()]; + if (!non_network_factory.is_bound()) { + BindNonNetworkURLLoaderFactoryReceiver( + resource_request_->url, + non_network_factory.BindNewPipeAndPassReceiver()); } - factory = network_loader_factory_; + factory = + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + non_network_factory.get()); } - url_chain_.push_back(resource_request_->url); - *out_options = GetURLLoaderOptions( - resource_request_->resource_type == - static_cast<int>(blink::mojom::ResourceType::kMainFrame)); - return factory; - } - void FollowRedirect( - const std::vector<std::string>& removed_headers, - const net::HttpRequestHeaders& modified_headers, - const net::HttpRequestHeaders& modified_cors_exempt_headers, - PreviewsState new_previews_state, - base::Time ui_post_time) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(!redirect_info_.new_url.is_empty()); - ui_to_io_time_ += (base::Time::Now() - ui_post_time); - - // Update |resource_request_| and call Restart to give our |interceptors_| a - // chance at handling the new location. If no interceptor wants to take - // over, we'll use the existing url_loader to follow the redirect, see - // MaybeStartLoader. - // TODO(michaeln): This is still WIP and is based on URLRequest::Redirect, - // there likely remains more to be done. - // a. For subframe navigations, the Origin header may need to be modified - // differently? - - bool should_clear_upload = false; - net::RedirectUtil::UpdateHttpRequest( - resource_request_->url, resource_request_->method, redirect_info_, - removed_headers, modified_headers, &resource_request_->headers, - &should_clear_upload); - if (should_clear_upload) { - // The request body is no longer applicable. - resource_request_->request_body.reset(); + if (g_loader_factory_interceptor.Get()) { + mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote; + mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver = + factory_remote.InitWithNewPipeAndPassReceiver(); + g_loader_factory_interceptor.Get().Run(&receiver); + factory->Clone(std::move(receiver)); + factory = base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( + std::move(factory_remote)); } - - 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; - - // See if navigation network isolation key needs to be updated. - resource_request_->trusted_params->isolation_info = - resource_request_->trusted_params->isolation_info.CreateForRedirect( - url::Origin::Create(resource_request_->url)); - - resource_request_->referrer = GURL(redirect_info_.new_referrer); - resource_request_->referrer_policy = redirect_info_.new_referrer_policy; - resource_request_->previews_state = new_previews_state; - url_chain_.push_back(redirect_info_.new_url); - - // Need to cache modified headers for |url_loader_| since it doesn't use - // |resource_request_| during redirect. - url_loader_removed_headers_ = removed_headers; - url_loader_modified_headers_ = modified_headers; - url_loader_modified_cors_exempt_headers_ = modified_cors_exempt_headers; - - // Don't send Accept: application/signed-exchange for fallback redirects. - if (redirect_info_.is_signed_exchange_fallback_redirect) { - url_loader_modified_headers_.SetHeader(net::HttpRequestHeaders::kAccept, - FrameAcceptHeaderValue()); - resource_request_->headers.SetHeader(net::HttpRequestHeaders::kAccept, - FrameAcceptHeaderValue()); + } else { + default_loader_used_ = true; + + // NOTE: We only support embedders proxying network-service-bound requests + // not handled by NavigationLoaderInterceptors above (e.g. Service Worker + // or AppCache). Hence this code is only reachable when one of the above + // interceptors isn't used and the URL is either a data URL or has a + // scheme which is handled by the network service. + if (proxied_factory_receiver_.is_valid()) { + DCHECK(proxied_factory_remote_.is_valid()); + // We don't worry about reconnection since it's a single navigation. + network_loader_factory_->Clone(std::move(proxied_factory_receiver_)); + // Replace the network factory with the proxied version since this may + // need to be used in redirects, and we've already consumed + // |proxied_factory_receiver_|. + network_loader_factory_ = + base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( + std::move(proxied_factory_remote_)); } - - Restart(); + factory = network_loader_factory_; } + url_chain_.push_back(resource_request_->url); + *out_options = GetURLLoaderOptions( + resource_request_->resource_type == + static_cast<int>(blink::mojom::ResourceType::kMainFrame)); + return factory; +} - base::Optional<SubresourceLoaderParams> TakeSubresourceLoaderParams() { - return std::move(subresource_loader_params_); +void NavigationURLLoaderImpl::FollowRedirectInternal( + const std::vector<std::string>& removed_headers, + const net::HttpRequestHeaders& modified_headers, + const net::HttpRequestHeaders& modified_cors_exempt_headers, + PreviewsState new_previews_state, + base::Time ui_post_time) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(!redirect_info_.new_url.is_empty()); + + // Update |resource_request_| and call Restart to give our |interceptors_| a + // chance at handling the new location. If no interceptor wants to take + // over, we'll use the existing url_loader to follow the redirect, see + // MaybeStartLoader. + // TODO(michaeln): This is still WIP and is based on URLRequest::Redirect, + // there likely remains more to be done. + // a. For subframe navigations, the Origin header may need to be modified + // differently? + + bool should_clear_upload = false; + net::RedirectUtil::UpdateHttpRequest( + resource_request_->url, resource_request_->method, redirect_info_, + removed_headers, modified_headers, &resource_request_->headers, + &should_clear_upload); + if (should_clear_upload) { + // The request body is no longer applicable. + resource_request_->request_body.reset(); } - private: - // network::mojom::URLLoaderClient implementation: - void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override { - head_ = std::move(head); - on_receive_response_time_ = base::TimeTicks::Now(); + 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; + + // See if navigation network isolation key needs to be updated. + resource_request_->trusted_params->isolation_info = + resource_request_->trusted_params->isolation_info.CreateForRedirect( + url::Origin::Create(resource_request_->url)); + + resource_request_->referrer = GURL(redirect_info_.new_referrer); + resource_request_->referrer_policy = redirect_info_.new_referrer_policy; + resource_request_->previews_state = new_previews_state; + url_chain_.push_back(redirect_info_.new_url); + + // Need to cache modified headers for |url_loader_| since it doesn't use + // |resource_request_| during redirect. + url_loader_removed_headers_ = removed_headers; + url_loader_modified_headers_ = modified_headers; + url_loader_modified_cors_exempt_headers_ = modified_cors_exempt_headers; + + // Don't send Accept: application/signed-exchange for fallback redirects. + if (redirect_info_.is_signed_exchange_fallback_redirect) { + url_loader_modified_headers_.SetHeader(net::HttpRequestHeaders::kAccept, + FrameAcceptHeaderValue()); + resource_request_->headers.SetHeader(net::HttpRequestHeaders::kAccept, + FrameAcceptHeaderValue()); } - // network::mojom::URLLoaderClient implementation: - void OnStartLoadingResponseBody( - mojo::ScopedDataPipeConsumerHandle response_body) override { - if (!on_receive_response_time_.is_null()) { - UMA_HISTOGRAM_TIMES( - "Navigation.OnReceiveResponseToOnStartLoadingResponseBody", - base::TimeTicks::Now() - on_receive_response_time_); - } + Restart(); +} + +void NavigationURLLoaderImpl::OnReceiveResponse( + network::mojom::URLResponseHeadPtr head) { + head_ = std::move(head); + on_receive_response_time_ = base::TimeTicks::Now(); +} - response_body_ = std::move(response_body); - received_response_ = true; +void NavigationURLLoaderImpl::OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle response_body) { + if (!on_receive_response_time_.is_null()) { + UMA_HISTOGRAM_TIMES( + "Navigation.OnReceiveResponseToOnStartLoadingResponseBody", + base::TimeTicks::Now() - on_receive_response_time_); + } - // If the default loader (network) was used to handle the URL load request - // we need to see if the interceptors want to potentially create a new - // loader for the response. e.g. AppCache. - if (MaybeCreateLoaderForResponse(&head_)) - return; + response_body_ = std::move(response_body); + received_response_ = true; - network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints; + // If the default loader (network) was used to handle the URL load request + // we need to see if the interceptors want to potentially create a new + // loader for the response. e.g. AppCache. + if (MaybeCreateLoaderForResponse(&head_)) + return; - if (url_loader_) { - url_loader_client_endpoints = url_loader_->Unbind(); - } else { - url_loader_client_endpoints = - network::mojom::URLLoaderClientEndpoints::New( - std::move(response_url_loader_), - response_loader_receiver_.Unbind()); - } + network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints; - // 304 responses should abort the navigation, rather than display the page. - // This needs to be after the URLLoader has been moved to - // |url_loader_client_endpoints| in order to abort the request, to avoid - // receiving unexpected call. - if (head_->headers && - head_->headers->response_code() == net::HTTP_NOT_MODIFIED) { - // Call CancelWithError instead of OnComplete so that if there is an - // intercepting URLLoaderFactory it gets notified. - url_loader_->CancelWithError( - net::ERR_ABORTED, - base::StringPiece(base::NumberToString(net::ERR_ABORTED))); - return; - } + if (url_loader_) { + url_loader_client_endpoints = url_loader_->Unbind(); + } else { + url_loader_client_endpoints = network::mojom::URLLoaderClientEndpoints::New( + std::move(response_url_loader_), response_loader_receiver_.Unbind()); + } + + // 304 responses should abort the navigation, rather than display the page. + // This needs to be after the URLLoader has been moved to + // |url_loader_client_endpoints| in order to abort the request, to avoid + // receiving unexpected call. + if (head_->headers && + head_->headers->response_code() == net::HTTP_NOT_MODIFIED) { + // Call CancelWithError instead of OnComplete so that if there is an + // intercepting URLLoaderFactory it gets notified. + url_loader_->CancelWithError( + net::ERR_ABORTED, + base::StringPiece(base::NumberToString(net::ERR_ABORTED))); + return; + } - bool must_download = download_utils::MustDownload( - url_, head_->headers.get(), head_->mime_type); - bool known_mime_type = blink::IsSupportedMimeType(head_->mime_type); + bool must_download = download_utils::MustDownload(url_, head_->headers.get(), + head_->mime_type); + bool known_mime_type = blink::IsSupportedMimeType(head_->mime_type); #if BUILDFLAG(ENABLE_PLUGINS) if (!head_->intercepted_by_plugin && !must_download && !known_mime_type) { @@ -929,389 +820,273 @@ class NavigationURLLoaderImpl::URLLoaderRequestController CallOnReceivedResponse(std::move(head_), std::move(url_loader_client_endpoints), is_download); - } +} #if BUILDFLAG(ENABLE_PLUGINS) - void CheckPluginAndContinueOnReceiveResponse( - network::mojom::URLResponseHeadPtr head, - network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, - bool is_download_if_not_handled_by_plugin, - const std::vector<WebPluginInfo>& plugins) { - bool stale; - WebPluginInfo plugin; - FrameTreeNode* frame_tree_node = - FrameTreeNode::GloballyFindByID(frame_tree_node_id_); - int render_process_id = - frame_tree_node->current_frame_host()->GetProcess()->GetID(); - int routing_id = frame_tree_node->current_frame_host()->GetRoutingID(); - bool has_plugin = PluginService::GetInstance()->GetPluginInfo( - render_process_id, routing_id, resource_request_->url, url::Origin(), - head->mime_type, false /* allow_wildcard */, &stale, &plugin, nullptr); - - if (stale) { - // Refresh the plugins asynchronously. - PluginService::GetInstance()->GetPlugins(base::BindOnce( - &URLLoaderRequestController::CheckPluginAndContinueOnReceiveResponse, - weak_factory_.GetWeakPtr(), std::move(head), - std::move(url_loader_client_endpoints), - is_download_if_not_handled_by_plugin)); - return; - } - - bool is_download = !has_plugin && is_download_if_not_handled_by_plugin; - - CallOnReceivedResponse(std::move(head), - std::move(url_loader_client_endpoints), is_download); +void NavigationURLLoaderImpl::CheckPluginAndContinueOnReceiveResponse( + network::mojom::URLResponseHeadPtr head, + network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, + bool is_download_if_not_handled_by_plugin, + const std::vector<WebPluginInfo>& plugins) { + bool stale; + WebPluginInfo plugin; + FrameTreeNode* frame_tree_node = + FrameTreeNode::GloballyFindByID(frame_tree_node_id_); + int render_process_id = + frame_tree_node->current_frame_host()->GetProcess()->GetID(); + int routing_id = frame_tree_node->current_frame_host()->GetRoutingID(); + bool has_plugin = PluginService::GetInstance()->GetPluginInfo( + render_process_id, routing_id, resource_request_->url, url::Origin(), + head->mime_type, false /* allow_wildcard */, &stale, &plugin, nullptr); + + if (stale) { + // Refresh the plugins asynchronously. + PluginService::GetInstance()->GetPlugins(base::BindOnce( + &NavigationURLLoaderImpl::CheckPluginAndContinueOnReceiveResponse, + weak_factory_.GetWeakPtr(), std::move(head), + std::move(url_loader_client_endpoints), + is_download_if_not_handled_by_plugin)); + return; } + + bool is_download = !has_plugin && is_download_if_not_handled_by_plugin; + CallOnReceivedResponse(std::move(head), + std::move(url_loader_client_endpoints), is_download); +} #endif - void CallOnReceivedResponse( - network::mojom::URLResponseHeadPtr head, - network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, - bool is_download) { - network::mojom::URLResponseHead* head_ptr = head.get(); - auto on_receive_response = base::BindOnce( - &NavigationURLLoaderImpl::OnReceiveResponse, owner_, std::move(head), - std::move(url_loader_client_endpoints), std::move(response_body_), - global_request_id_, is_download, ui_to_io_time_, base::Time::Now()); - - ParseHeaders(url_, head_ptr, std::move(on_receive_response)); - } +void NavigationURLLoaderImpl::CallOnReceivedResponse( + network::mojom::URLResponseHeadPtr head, + network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, + bool is_download) { + network::mojom::URLResponseHead* head_ptr = head.get(); + auto on_receive_response = base::BindOnce( + &NavigationURLLoaderImpl::NotifyResponseStarted, + weak_factory_.GetWeakPtr(), std::move(head), + std::move(url_loader_client_endpoints), std::move(response_body_), + global_request_id_, is_download); + + ParseHeaders(url_, head_ptr, std::move(on_receive_response)); +} - void OnReceiveRedirect(const net::RedirectInfo& redirect_info, - network::mojom::URLResponseHeadPtr head) override { - net::Error error = net::OK; - if (!bypass_redirect_checks_ && - !IsSafeRedirectTarget(url_, redirect_info.new_url)) { - error = net::ERR_UNSAFE_REDIRECT; - } else if (--redirect_limit_ == 0) { - error = net::ERR_TOO_MANY_REDIRECTS; - if (redirect_info.is_signed_exchange_fallback_redirect) - UMA_HISTOGRAM_BOOLEAN("SignedExchange.FallbackRedirectLoop", true); - } - if (error != net::OK) { - if (url_loader_) { - // Call CancelWithError instead of OnComplete so that if there is an - // intercepting URLLoaderFactory (created through the embedder's - // ContentBrowserClient::WillCreateURLLoaderFactory) it gets notified. - url_loader_->CancelWithError( - error, base::StringPiece(base::NumberToString(error))); - } else { - // TODO(crbug.com/1052242): Make sure ResetWithReason() is called on the - // original url_loader_. - OnComplete(network::URLLoaderCompletionStatus(error)); - } - return; +void NavigationURLLoaderImpl::OnReceiveRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head) { + net::Error error = net::OK; + if (!bypass_redirect_checks_ && + !IsSafeRedirectTarget(url_, redirect_info.new_url)) { + error = net::ERR_UNSAFE_REDIRECT; + } else if (--redirect_limit_ == 0) { + error = net::ERR_TOO_MANY_REDIRECTS; + if (redirect_info.is_signed_exchange_fallback_redirect) + UMA_HISTOGRAM_BOOLEAN("SignedExchange.FallbackRedirectLoop", true); + } + if (error != net::OK) { + if (url_loader_) { + // Call CancelWithError instead of OnComplete so that if there is an + // intercepting URLLoaderFactory (created through the embedder's + // ContentBrowserClient::WillCreateURLLoaderFactory) it gets notified. + url_loader_->CancelWithError( + error, base::StringPiece(base::NumberToString(error))); + } else { + // TODO(crbug.com/1052242): Make sure ResetWithReason() is called on the + // original url_loader_. + OnComplete(network::URLLoaderCompletionStatus(error)); } - - // Store the redirect_info for later use in FollowRedirect where we give - // our interceptors_ a chance to intercept the request for the new location. - redirect_info_ = redirect_info; - - url_ = redirect_info.new_url; - - network::mojom::URLResponseHead* head_ptr = head.get(); - auto on_receive_redirect = - base::BindOnce(&NavigationURLLoaderImpl::OnReceiveRedirect, owner_, - redirect_info, std::move(head), base::Time::Now()); - ParseHeaders(url_, head_ptr, std::move(on_receive_redirect)); + return; } - void OnUploadProgress(int64_t current_position, - int64_t total_size, - OnUploadProgressCallback callback) override { - NOTREACHED(); - } + // Store the redirect_info for later use in FollowRedirect where we give + // our interceptors_ a chance to intercept the request for the new location. + redirect_info_ = redirect_info; - void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override { - NOTREACHED(); - } + url_ = redirect_info.new_url; - void OnTransferSizeUpdated(int32_t transfer_size_diff) override {} + network::mojom::URLResponseHead* head_ptr = head.get(); + auto on_receive_redirect = base::BindOnce( + &NavigationURLLoaderImpl::NotifyRequestRedirected, + weak_factory_.GetWeakPtr(), redirect_info, std::move(head)); + ParseHeaders(url_, head_ptr, std::move(on_receive_redirect)); +} - void OnComplete(const network::URLLoaderCompletionStatus& status) override { - UMA_HISTOGRAM_BOOLEAN( - "Navigation.URLLoaderNetworkService.OnCompleteHasSSLInfo", - status.ssl_info.has_value()); +void NavigationURLLoaderImpl::OnUploadProgress( + int64_t current_position, + int64_t total_size, + OnUploadProgressCallback callback) { + NOTREACHED(); +} - // Successful load must have used OnResponseStarted first. In this case, the - // URLLoaderClient has already been transferred to the renderer process and - // OnComplete is not expected to be called here. - if (status.error_code == net::OK) { - base::debug::DumpWithoutCrashing(); - return; - } +void NavigationURLLoaderImpl::OnReceiveCachedMetadata( + mojo_base::BigBuffer data) { + NOTREACHED(); +} - if (status.ssl_info.has_value()) { - UMA_HISTOGRAM_MEMORY_KB( - "Navigation.URLLoaderNetworkService.OnCompleteCertificateChainsSize", - GetCertificateChainsSizeInKB(status.ssl_info.value())); - } +void NavigationURLLoaderImpl::OnComplete( + const network::URLLoaderCompletionStatus& status) { + UMA_HISTOGRAM_BOOLEAN( + "Navigation.URLLoaderNetworkService.OnCompleteHasSSLInfo", + status.ssl_info.has_value()); + + // Successful load must have used OnResponseStarted first. In this case, the + // URLLoaderClient has already been transferred to the renderer process and + // OnComplete is not expected to be called here. + if (status.error_code == net::OK) { + base::debug::DumpWithoutCrashing(); + return; + } - // If the default loader (network) was used to handle the URL load request - // we need to see if the interceptors want to potentially create a new - // loader for the response. e.g. AppCache. - // - // Note: Despite having received a response, the HTTP_NOT_MODIFIED(304) ones - // are ignored using OnComplete(net::ERR_ABORTED). No interceptor must - // be used in this case. - if (!received_response_) { - auto response = network::mojom::URLResponseHead::New(); - if (MaybeCreateLoaderForResponse(&response)) - return; - } + if (status.ssl_info.has_value()) { + UMA_HISTOGRAM_MEMORY_KB( + "Navigation.URLLoaderNetworkService.OnCompleteCertificateChainsSize", + GetCertificateChainsSizeInKB(status.ssl_info.value())); + } - status_ = status; - base::PostTask( - FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&NavigationURLLoaderImpl::OnComplete, owner_, status)); + // If the default loader (network) was used to handle the URL load request + // we need to see if the interceptors want to potentially create a new + // loader for the response. e.g. AppCache. + // + // Note: Despite having received a response, the HTTP_NOT_MODIFIED(304) ones + // are ignored using OnComplete(net::ERR_ABORTED). No interceptor must + // be used in this case. + if (!received_response_) { + auto response = network::mojom::URLResponseHead::New(); + if (MaybeCreateLoaderForResponse(&response)) + return; } + status_ = status; + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&NavigationURLLoaderImpl::NotifyRequestFailed, + weak_factory_.GetWeakPtr(), status)); +} + // Returns true if an interceptor wants to handle the response, i.e. return a // different response. For e.g. AppCache may have fallback content. - bool MaybeCreateLoaderForResponse( - network::mojom::URLResponseHeadPtr* response) { - if (!default_loader_used_ && - !web_bundle_utils::CanLoadAsWebBundle(url_, (*response)->mime_type)) { - return false; - } - for (size_t i = 0u; i < interceptors_.size(); ++i) { - NavigationLoaderInterceptor* interceptor = interceptors_[i].get(); - mojo::PendingReceiver<network::mojom::URLLoaderClient> - response_client_receiver; - bool skip_other_interceptors = false; - bool will_return_unsafe_redirect = false; - if (interceptor->MaybeCreateLoaderForResponse( - *resource_request_, response, &response_body_, - &response_url_loader_, &response_client_receiver, - url_loader_.get(), &skip_other_interceptors, - &will_return_unsafe_redirect)) { - if (will_return_unsafe_redirect) - bypass_redirect_checks_ = true; - response_loader_receiver_.reset(); - response_loader_receiver_.Bind(std::move(response_client_receiver)); - default_loader_used_ = false; - url_loader_.reset(); // Consumed above. - response_body_.reset(); // Consumed above. - if (skip_other_interceptors) { - std::vector<std::unique_ptr<NavigationLoaderInterceptor>> - new_interceptors; - new_interceptors.push_back(std::move(interceptors_[i])); - new_interceptors.swap(interceptors_); - // Reset the state of ServiceWorkerContainerHost. - // Currently we don't support Service Worker in Signed Exchange - // pages. The page will not be controlled by service workers. And - // Service Worker related APIs will fail with NoDocumentURL error. - // TODO(crbug/898733): Support SignedExchange loading and Service - // Worker integration. - if (service_worker_handle_) { - RunOrPostTaskOnThread( - FROM_HERE, ServiceWorkerContext::GetCoreThreadId(), - base::BindOnce( - [](ServiceWorkerMainResourceHandleCore* core) { - base::WeakPtr<ServiceWorkerContainerHost> container_host = - core->container_host(); - if (container_host) { - container_host->SetControllerRegistration( - nullptr, false /* notify_controllerchange */); - container_host->UpdateUrls( - GURL(), net::SiteForCookies(), base::nullopt); - } - }, - // Unretained() is safe because the handle owns the core, - // and core gets deleted on the core thread in a task that - // must occur after this task. - base::Unretained(service_worker_handle_->core()))); - } +bool NavigationURLLoaderImpl::MaybeCreateLoaderForResponse( + network::mojom::URLResponseHeadPtr* response) { + if (!default_loader_used_ && + !web_bundle_utils::CanLoadAsWebBundle(url_, (*response)->mime_type)) { + return false; + } + for (auto& interceptor : interceptors_) { + mojo::PendingReceiver<network::mojom::URLLoaderClient> + response_client_receiver; + bool skip_other_interceptors = false; + bool will_return_unsafe_redirect = false; + if (interceptor->MaybeCreateLoaderForResponse( + *resource_request_, response, &response_body_, + &response_url_loader_, &response_client_receiver, url_loader_.get(), + &skip_other_interceptors, &will_return_unsafe_redirect)) { + if (will_return_unsafe_redirect) + bypass_redirect_checks_ = true; + response_loader_receiver_.reset(); + response_loader_receiver_.Bind(std::move(response_client_receiver)); + default_loader_used_ = false; + url_loader_.reset(); // Consumed above. + response_body_.reset(); // Consumed above. + if (skip_other_interceptors) { + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> + new_interceptors; + new_interceptors.push_back(std::move(interceptor)); + new_interceptors.swap(interceptors_); + // Reset the state of ServiceWorkerContainerHost. + // Currently we don't support Service Worker in Signed Exchange + // pages. The page will not be controlled by service workers. And + // Service Worker related APIs will fail with NoDocumentURL error. + // TODO(crbug/898733): Support SignedExchange loading and Service + // Worker integration. + if (service_worker_handle_) { + RunOrPostTaskOnThread( + FROM_HERE, ServiceWorkerContext::GetCoreThreadId(), + base::BindOnce( + [](ServiceWorkerMainResourceHandleCore* core) { + base::WeakPtr<ServiceWorkerContainerHost> container_host = + core->container_host(); + if (container_host) { + container_host->SetControllerRegistration( + nullptr, false /* notify_controllerchange */); + container_host->UpdateUrls(GURL(), net::SiteForCookies(), + base::nullopt); + } + }, + // Unretained() is safe because the handle owns the core, + // and core gets deleted on the core thread in a task that + // must occur after this task. + base::Unretained(service_worker_handle_->core()))); } - return true; } + return true; } - return false; - } - - std::vector<std::unique_ptr<blink::URLLoaderThrottle>> - CreateURLLoaderThrottles() { - return CreateContentBrowserURLLoaderThrottles( - *resource_request_, browser_context_, web_contents_getter_, - navigation_ui_data_.get(), frame_tree_node_id_); - } - - std::unique_ptr<SignedExchangeRequestHandler> - CreateSignedExchangeRequestHandler( - const NavigationRequestInfo& request_info, - scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - scoped_refptr<SignedExchangePrefetchMetricRecorder> - signed_exchange_prefetch_metric_recorder, - std::string accept_langs) { - // It is safe to pass the callback of CreateURLLoaderThrottles with the - // unretained |this|, because the passed callback will be used by a - // SignedExchangeHandler which is indirectly owned by |this| until its - // header is verified and parsed, that's where the getter is used. - return std::make_unique<SignedExchangeRequestHandler>( - GetURLLoaderOptions(request_info.is_main_frame), - request_info.frame_tree_node_id, request_info.devtools_navigation_token, - std::move(url_loader_factory), - base::BindRepeating( - &URLLoaderRequestController::CreateURLLoaderThrottles, - base::Unretained(this)), - std::move(signed_exchange_prefetch_metric_recorder), - std::move(accept_langs)); } + return false; +} - void ParseHeaders(const GURL& url, - network::mojom::URLResponseHead* head, - base::OnceClosure continuation) { - // The main path: - // -------------- - // The ParsedHeaders are already provided. No more work needed. - // - // Currently used when the response is coming from: - // - Network - // - ServiceWorker - // - WebUI - if (head->parsed_headers) { - std::move(continuation).Run(); - return; - } - - // As an optimization, when we know the parsed headers will be empty, we can - // skip the network process roundtrip. - // TODO(arthursonzogni): If there are any performance issues, consider - // checking the |head->headers| contains at least one header to be parsed. - if (!head->headers) { - head->parsed_headers = network::mojom::ParsedHeaders::New(); - std::move(continuation).Run(); - return; - } - - auto assign = [](base::OnceClosure continuation, - network::mojom::URLResponseHead* head, - network::mojom::ParsedHeadersPtr parsed_headers) { - head->parsed_headers = std::move(parsed_headers); - std::move(continuation).Run(); - }; +std::vector<std::unique_ptr<blink::URLLoaderThrottle>> +NavigationURLLoaderImpl::CreateURLLoaderThrottles() { + return CreateContentBrowserURLLoaderThrottles( + *resource_request_, browser_context_, web_contents_getter_, + navigation_ui_data_.get(), frame_tree_node_id_); +} - storage_partition_->GetNetworkContext()->ParseHeaders( - url, head->headers, - base::BindOnce(assign, std::move(continuation), head)); - } +std::unique_ptr<SignedExchangeRequestHandler> +NavigationURLLoaderImpl::CreateSignedExchangeRequestHandler( + const NavigationRequestInfo& request_info, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + scoped_refptr<SignedExchangePrefetchMetricRecorder> + signed_exchange_prefetch_metric_recorder, + std::string accept_langs) { + // It is safe to pass the callback of CreateURLLoaderThrottles with the + // unretained |this|, because the passed callback will be used by a + // SignedExchangeHandler which is indirectly owned by |this| until its + // header is verified and parsed, that's where the getter is used. + return std::make_unique<SignedExchangeRequestHandler>( + GetURLLoaderOptions(request_info.is_main_frame), + request_info.frame_tree_node_id, request_info.devtools_navigation_token, + std::move(url_loader_factory), + base::BindRepeating(&NavigationURLLoaderImpl::CreateURLLoaderThrottles, + base::Unretained(this)), + std::move(signed_exchange_prefetch_metric_recorder), + std::move(accept_langs)); +} - std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors_; - size_t interceptor_index_ = 0; - - std::unique_ptr<network::ResourceRequest> resource_request_; - // Non-NetworkService: |request_info_| is updated along with - // |resource_request_| on redirects. - std::unique_ptr<NavigationRequestInfo> request_info_; - int frame_tree_node_id_ = 0; - GlobalRequestID global_request_id_; - net::RedirectInfo redirect_info_; - int redirect_limit_ = net::URLRequest::kMaxRedirects; - base::RepeatingCallback<WebContents*()> web_contents_getter_; - std::unique_ptr<NavigationUIData> navigation_ui_data_; - scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory_; - - std::unique_ptr<blink::ThrottlingURLLoader> url_loader_; - - // Caches the modified request headers provided by clients during redirect, - // will be consumed by next |url_loader_->FollowRedirect()|. - std::vector<std::string> url_loader_removed_headers_; - net::HttpRequestHeaders url_loader_modified_headers_; - net::HttpRequestHeaders url_loader_modified_cors_exempt_headers_; - - std::vector<GURL> url_chain_; - - // Current URL that is being navigated, updated after redirection. - GURL url_; - - // Currently used by the AppCache loader to pass its factory to the - // renderer which enables it to handle subresources. - base::Optional<SubresourceLoaderParams> subresource_loader_params_; - - // This is referenced only on the UI thread. - base::WeakPtr<NavigationURLLoaderImpl> owner_; - - // Set to true if the default URLLoader (network service) was used for the - // current navigation. - bool default_loader_used_ = false; - - // URLLoaderClient receiver for loaders created for responses received from - // the network loader. - mojo::Receiver<network::mojom::URLLoaderClient> response_loader_receiver_{ - this}; - - // URLLoader instance for response loaders, i.e loaders created for handling - // responses received from the network URLLoader. - mojo::PendingRemote<network::mojom::URLLoader> response_url_loader_; - - // Set to true if we receive a valid response from a URLLoader, i.e. - // URLLoaderClient::OnStartLoadingResponseBody() is called. - bool received_response_ = false; - - // When URLLoaderClient::OnReceiveResponse() is called. For UMA. - base::TimeTicks on_receive_response_time_; - - bool started_ = false; - - // Lazily initialized and used in the case of non-network resource - // navigations. Keyed by URL scheme. - std::map<std::string, mojo::Remote<network::mojom::URLLoaderFactory>> - non_network_url_loader_factories_; - - // Non-NetworkService: - // Generator of a request handler for sending request to the network. This - // captures all of parameters to create a - // SingleRequestURLLoaderFactory::RequestHandler. Used only when - // NetworkService is disabled. - // Set |was_request_intercepted| to true if the request was intercepted by an - // interceptor and the request is falling back to the network. In that case, - // any interceptors won't intercept the request. - base::RepeatingCallback<SingleRequestURLLoaderFactory::RequestHandler( - bool /* was_request_intercepted */)> - default_request_handler_factory_; - - // The completion status if it has been received. This is needed to handle - // the case that the response is intercepted by download, and OnComplete() is - // already called while we are transferring the |url_loader_| and response - // body to download code. - base::Optional<network::URLLoaderCompletionStatus> status_; - - // Before creating this URLLoaderRequestController on UI thread, the embedder - // may have elected to proxy the URLLoaderFactory receiver, in which case - // these fields will contain input (remote) and output (receiver) endpoints - // for the proxy. If this controller is handling a receiver for which proxying - // is supported, receivers will be plumbed through these endpoints. +void NavigationURLLoaderImpl::ParseHeaders( + const GURL& url, + network::mojom::URLResponseHead* head, + base::OnceClosure continuation) { + // The main path: + // -------------- + // The ParsedHeaders are already provided. No more work needed. // - // Note that these are only used for receivers that go to the Network Service. - mojo::PendingReceiver<network::mojom::URLLoaderFactory> - proxied_factory_receiver_; - mojo::PendingRemote<network::mojom::URLLoaderFactory> proxied_factory_remote_; - - // The schemes that this loader can use. For anything else we'll try external - // protocol handlers. - std::set<std::string> known_schemes_; - - // True when a proxy will handle the redirect checks, or when an interceptor - // intentionally returned unsafe redirect response - // (eg: NavigationLoaderInterceptor for loading a local Web Bundle file). - bool bypass_redirect_checks_; - - ServiceWorkerMainResourceHandle* service_worker_handle_ = nullptr; - - // Counts the time overhead of all the hops from the UI to the IO threads. - base::TimeDelta ui_to_io_time_; - - BrowserContext* browser_context_; - StoragePartitionImpl* storage_partition_; + // Currently used when the response is coming from: + // - Network + // - ServiceWorker + // - WebUI + if (head->parsed_headers) { + std::move(continuation).Run(); + return; + } - network::mojom::URLResponseHeadPtr head_; - mojo::ScopedDataPipeConsumerHandle response_body_; + // As an optimization, when we know the parsed headers will be empty, we can + // skip the network process roundtrip. + // TODO(arthursonzogni): If there are any performance issues, consider + // checking the |head->headers| contains at least one header to be parsed. + if (!head->headers) { + head->parsed_headers = network::mojom::ParsedHeaders::New(); + std::move(continuation).Run(); + return; + } - mutable base::WeakPtrFactory<URLLoaderRequestController> weak_factory_{this}; + auto assign = [](base::OnceClosure continuation, + network::mojom::URLResponseHead* head, + network::mojom::ParsedHeadersPtr parsed_headers) { + head->parsed_headers = std::move(parsed_headers); + std::move(continuation).Run(); + }; - DISALLOW_COPY_AND_ASSIGN(URLLoaderRequestController); -}; + storage_partition_->GetNetworkContext()->ParseHeaders( + url, head->headers, + base::BindOnce(assign, std::move(continuation), head)); +} // TODO(https://crbug.com/790734): pass |navigation_ui_data| along with the // request so that it could be modified. @@ -1329,42 +1104,49 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( std::vector<std::unique_ptr<NavigationLoaderInterceptor>> initial_interceptors) : delegate_(delegate), - download_policy_(request_info->common_params->download_policy) { + browser_context_(browser_context), + storage_partition_(static_cast<StoragePartitionImpl*>(storage_partition)), + service_worker_handle_(service_worker_handle), + request_info_(std::move(request_info)), + url_(request_info_->common_params->url), + frame_tree_node_id_(request_info_->frame_tree_node_id), + global_request_id_(GlobalRequestID::MakeBrowserInitiated()), + web_contents_getter_( + base::BindRepeating(&WebContents::FromFrameTreeNodeId, + frame_tree_node_id_)), + navigation_ui_data_(std::move(navigation_ui_data)), + interceptors_(std::move(initial_interceptors)), + download_policy_(request_info_->common_params->download_policy) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - int frame_tree_node_id = request_info->frame_tree_node_id; TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1( "navigation", "Navigation timeToResponseStarted", this, - request_info->common_params->navigation_start, "FrameTreeNode id", - frame_tree_node_id); + request_info_->common_params->navigation_start, "FrameTreeNode id", + frame_tree_node_id_); - ServiceWorkerMainResourceHandleCore* service_worker_handle_core = - service_worker_handle ? service_worker_handle->core() : nullptr; - - auto* partition = static_cast<StoragePartitionImpl*>(storage_partition); scoped_refptr<SignedExchangePrefetchMetricRecorder> signed_exchange_prefetch_metric_recorder = - partition->GetPrefetchURLLoaderService() + storage_partition_->GetPrefetchURLLoaderService() ->signed_exchange_prefetch_metric_recorder(); - std::unique_ptr<network::ResourceRequest> new_request = CreateResourceRequest( - request_info.get(), frame_tree_node_id, std::move(cookie_observer)); + resource_request_ = CreateResourceRequest( + request_info_.get(), frame_tree_node_id_, std::move(cookie_observer)); - std::string accept_langs = GetContentClient()->browser()->GetAcceptLangs( - partition->browser_context()); + std::string accept_langs = + GetContentClient()->browser()->GetAcceptLangs(browser_context_); // Check if a web UI scheme wants to handle this request. FrameTreeNode* frame_tree_node = - FrameTreeNode::GloballyFindByID(frame_tree_node_id); + FrameTreeNode::GloballyFindByID(frame_tree_node_id_); const auto& schemes = URLDataManagerBackend::GetWebUISchemes(); - std::string scheme = new_request->url.scheme(); + std::string scheme = resource_request_->url.scheme(); mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_for_webui; if (base::Contains(schemes, scheme)) { DCHECK(frame_tree_node); DCHECK(frame_tree_node->navigation_request()); auto factory_receiver = factory_for_webui.InitWithNewPipeAndPassReceiver(); GetContentClient()->browser()->WillCreateURLLoaderFactory( - partition->browser_context(), frame_tree_node->current_frame_host(), + browser_context_, frame_tree_node->current_frame_host(), frame_tree_node->current_frame_host()->GetProcess()->GetID(), ContentBrowserClient::URLLoaderFactoryType::kNavigation, url::Origin(), frame_tree_node->navigation_request()->GetNavigationId(), @@ -1375,35 +1157,32 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( std::move(factory_receiver)); } - mojo::PendingRemote<network::mojom::URLLoaderFactory> proxied_factory_remote; - mojo::PendingReceiver<network::mojom::URLLoaderFactory> - proxied_factory_receiver; mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient> header_client; - bool bypass_redirect_checks = false; if (frame_tree_node) { + // Initialize proxied factory remote/receiver if necessary. + // This also populates |bypass_redirect_checks_|. DCHECK(frame_tree_node->navigation_request()); // |frame_tree_node| may be null in some unit test environments. GetContentClient() ->browser() ->RegisterNonNetworkNavigationURLLoaderFactories( - frame_tree_node_id, &non_network_url_loader_factories_); + frame_tree_node_id_, &non_network_url_loader_factories_); // The embedder may want to proxy all network-bound URLLoaderFactory - // receivers that it can. If it elects to do so, we'll pass its proxy - // endpoints off to the URLLoaderRequestController where wthey will be - // connected if the request type supports proxying. + // receivers that it can. If it elects to do so, those proxies will be + // connected when loader is created if the request type supports proxying. mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_factory; auto factory_receiver = pending_factory.InitWithNewPipeAndPassReceiver(); // Here we give nullptr for |factory_override|, because CORS is no-op for // navigations. bool use_proxy = GetContentClient()->browser()->WillCreateURLLoaderFactory( - partition->browser_context(), frame_tree_node->current_frame_host(), + browser_context_, frame_tree_node->current_frame_host(), frame_tree_node->current_frame_host()->GetProcess()->GetID(), ContentBrowserClient::URLLoaderFactoryType::kNavigation, url::Origin(), frame_tree_node->navigation_request()->GetNavigationId(), - &factory_receiver, &header_client, &bypass_redirect_checks, + &factory_receiver, &header_client, &bypass_redirect_checks_, nullptr /* disable_secure_dns */, nullptr /* factory_override */); if (devtools_instrumentation::WillCreateURLLoaderFactory( frame_tree_node->current_frame_host(), true /* is_navigation */, @@ -1412,17 +1191,17 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( use_proxy = true; } if (use_proxy) { - proxied_factory_receiver = std::move(factory_receiver); - proxied_factory_remote = std::move(pending_factory); + proxied_factory_receiver_ = std::move(factory_receiver); + proxied_factory_remote_ = std::move(pending_factory); } const std::string storage_domain; non_network_url_loader_factories_.emplace( url::kFileSystemScheme, - CreateFileSystemURLLoaderFactory(ChildProcessHost::kInvalidUniqueID, - frame_tree_node->frame_tree_node_id(), - partition->GetFileSystemContext(), - storage_domain)); + CreateFileSystemURLLoaderFactory( + ChildProcessHost::kInvalidUniqueID, + frame_tree_node->frame_tree_node_id(), + storage_partition_->GetFileSystemContext(), storage_domain)); } non_network_url_loader_factories_.emplace( @@ -1433,8 +1212,8 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( std::unique_ptr<network::mojom::URLLoaderFactory> file_url_loader_factory = std::make_unique<FileURLLoaderFactory>( - partition->browser_context()->GetPath(), - partition->browser_context()->GetSharedCorsOriginAccessList(), + browser_context_->GetPath(), + browser_context_->GetSharedCorsOriginAccessList(), // USER_VISIBLE because loaded file resources may affect the UI. base::TaskPriority::USER_VISIBLE); @@ -1456,43 +1235,29 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}))); #endif - std::set<std::string> known_schemes; for (auto& iter : non_network_url_loader_factories_) - known_schemes.insert(iter.first); + known_schemes_.insert(iter.first); bool needs_loader_factory_interceptor = false; std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_network_factory = - partition->GetURLLoaderFactoryForBrowserProcess()->Clone(); + storage_partition_->GetURLLoaderFactoryForBrowserProcess()->Clone(); if (header_client) { needs_loader_factory_interceptor = true; mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote; CreateURLLoaderFactoryWithHeaderClient( std::move(header_client), - factory_remote.InitWithNewPipeAndPassReceiver(), partition); + factory_remote.InitWithNewPipeAndPassReceiver(), storage_partition_); pending_network_factory = std::make_unique<network::WrapperPendingSharedURLLoaderFactory>( std::move(factory_remote)); } - DCHECK(!request_controller_); - request_controller_ = std::make_unique<URLLoaderRequestController>( - std::move(initial_interceptors), std::move(new_request), browser_context, - partition, request_info->common_params->url, - std::move(proxied_factory_receiver), std::move(proxied_factory_remote), - std::move(known_schemes), bypass_redirect_checks, - weak_factory_.GetWeakPtr()); - request_controller_->Start( - std::move(pending_network_factory), service_worker_handle, - service_worker_handle_core, appcache_handle, - std::move(prefetched_signed_exchange_cache), - std::move(signed_exchange_prefetch_metric_recorder), - std::move(request_info), std::move(navigation_ui_data), - std::move(factory_for_webui), needs_loader_factory_interceptor, - base::Time::Now(), std::move(accept_langs)); -} - -NavigationURLLoaderImpl::~NavigationURLLoaderImpl() { + Start(std::move(pending_network_factory), appcache_handle, + std::move(prefetched_signed_exchange_cache), + std::move(signed_exchange_prefetch_metric_recorder), + std::move(factory_for_webui), std::move(accept_langs), + needs_loader_factory_interceptor); } void NavigationURLLoaderImpl::FollowRedirect( @@ -1500,26 +1265,24 @@ void NavigationURLLoaderImpl::FollowRedirect( const net::HttpRequestHeaders& modified_headers, const net::HttpRequestHeaders& modified_cors_exempt_headers, PreviewsState new_previews_state) { - request_controller_->FollowRedirect(removed_headers, modified_headers, - modified_cors_exempt_headers, - new_previews_state, base::Time::Now()); + FollowRedirectInternal(removed_headers, modified_headers, + modified_cors_exempt_headers, new_previews_state, + base::Time::Now()); } -void NavigationURLLoaderImpl::OnReceiveResponse( +void NavigationURLLoaderImpl::NotifyRequestStarted(base::TimeTicks timestamp) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + delegate_->OnRequestStarted(timestamp); +} + +void NavigationURLLoaderImpl::NotifyResponseStarted( network::mojom::URLResponseHeadPtr response_head, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, mojo::ScopedDataPipeConsumerHandle response_body, const GlobalRequestID& global_request_id, - bool is_download, - base::TimeDelta total_ui_to_io_time, - base::Time io_post_time) { - const base::TimeDelta kMinTime = base::TimeDelta::FromMicroseconds(1); - const base::TimeDelta kMaxTime = base::TimeDelta::FromMilliseconds(100); - const int kBuckets = 50; - io_to_ui_time_ += (base::Time::Now() - io_post_time); - UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( - "Navigation.NavigationURLLoaderImplIOPostTime", - io_to_ui_time_ + total_ui_to_io_time, kMinTime, kMaxTime, kBuckets); + bool is_download) { + // TODO(https://crbug.com/1068896): Remove + // "Navigation.NavigationURLLoaderImplIOPostTime" histogram as well. TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this, "&NavigationURLLoaderImpl", this, "success", true); @@ -1533,20 +1296,17 @@ void NavigationURLLoaderImpl::OnReceiveResponse( std::move(url_loader_client_endpoints), std::move(response_head), std::move(response_body), GlobalRequestID(global_request_id.child_id, global_request_id.request_id), - is_download, download_policy_, - request_controller_->TakeSubresourceLoaderParams()); + is_download, download_policy_, std::move(subresource_loader_params_)); } -void NavigationURLLoaderImpl::OnReceiveRedirect( +void NavigationURLLoaderImpl::NotifyRequestRedirected( net::RedirectInfo redirect_info, - network::mojom::URLResponseHeadPtr response_head, - base::Time io_post_time) { + network::mojom::URLResponseHeadPtr response_head) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - io_to_ui_time_ += (base::Time::Now() - io_post_time); delegate_->OnRequestRedirected(redirect_info, std::move(response_head)); } -void NavigationURLLoaderImpl::OnComplete( +void NavigationURLLoaderImpl::NotifyRequestFailed( const network::URLLoaderCompletionStatus& status) { TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this, "&NavigationURLLoaderImpl", this, "success", false); @@ -1582,13 +1342,7 @@ void NavigationURLLoaderImpl::CreateURLLoaderFactoryWithHeaderClient( std::move(factory_receiver), std::move(params)); } -void NavigationURLLoaderImpl::OnRequestStarted(base::TimeTicks timestamp) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - delegate_->OnRequestStarted(timestamp); -} - void NavigationURLLoaderImpl::BindNonNetworkURLLoaderFactoryReceiver( - int frame_tree_node_id, const GURL& url, mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver) { auto it = non_network_url_loader_factories_.find(url.scheme()); @@ -1598,7 +1352,7 @@ void NavigationURLLoaderImpl::BindNonNetworkURLLoaderFactoryReceiver( } FrameTreeNode* frame_tree_node = - FrameTreeNode::GloballyFindByID(frame_tree_node_id); + FrameTreeNode::GloballyFindByID(frame_tree_node_id_); DCHECK(frame_tree_node); DCHECK(frame_tree_node->navigation_request()); diff --git a/chromium/content/browser/loader/navigation_url_loader_impl.h b/chromium/content/browser/loader/navigation_url_loader_impl.h index 0d856e83a47..fea2619adc1 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl.h +++ b/chromium/content/browser/loader/navigation_url_loader_impl.h @@ -10,8 +10,11 @@ #include "base/optional.h" #include "base/time/time.h" #include "content/browser/loader/navigation_url_loader.h" +#include "content/browser/loader/single_request_url_loader_factory.h" +#include "content/browser/navigation_subresource_loader_params.h" #include "content/common/navigation_params.h" #include "content/public/browser/content_browser_client.h" +#include "content/public/browser/global_request_id.h" #include "content/public/browser/ssl_status.h" #include "content/public/common/previews_state.h" #include "mojo/public/cpp/bindings/pending_receiver.h" @@ -29,11 +32,15 @@ namespace content { class BrowserContext; class NavigationLoaderInterceptor; class PrefetchedSignedExchangeCache; +class SignedExchangePrefetchMetricRecorder; +class SignedExchangeRequestHandler; class StoragePartition; class StoragePartitionImpl; -struct GlobalRequestID; +struct WebPluginInfo; -class CONTENT_EXPORT NavigationURLLoaderImpl : public NavigationURLLoader { +class CONTENT_EXPORT NavigationURLLoaderImpl + : public NavigationURLLoader, + public network::mojom::URLLoaderClient { public: // The caller is responsible for ensuring that |delegate| outlives the loader. // Note |initial_interceptors| is there for test purposes only. @@ -52,6 +59,101 @@ class CONTENT_EXPORT NavigationURLLoaderImpl : public NavigationURLLoader { initial_interceptors); ~NavigationURLLoaderImpl() override; + // TODO(kinuko): Make most of these methods private. + // TODO(kinuko): Some method parameters can probably be just kept as + // member variables rather than being passed around. + + // Starts the loader by finalizing loader factories initialization and + // calling Restart(). + // This is called only once (while Restart can be called multiple times). + // Sets |started_| true. + void Start( + std::unique_ptr<network::PendingSharedURLLoaderFactory> + pending_network_loader_factory, + AppCacheNavigationHandle* appcache_handle, + scoped_refptr<PrefetchedSignedExchangeCache> + prefetched_signed_exchange_cache, + scoped_refptr<SignedExchangePrefetchMetricRecorder> + signed_exchange_prefetch_metric_recorder, + mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_for_webui, + std::string accept_langs, + bool needs_loader_factory_interceptor); + void CreateInterceptors(AppCacheNavigationHandle* appcache_handle, + scoped_refptr<PrefetchedSignedExchangeCache> + prefetched_signed_exchange_cache, + scoped_refptr<SignedExchangePrefetchMetricRecorder> + signed_exchange_prefetch_metric_recorder, + const std::string& accept_langs); + + // This could be called multiple times to follow a chain of redirects. + void Restart(); + + // |interceptor| is non-null if this is called by one of the interceptors + // (via a LoaderCallback). + // |single_request_handler| is the RequestHandler given by the + // |interceptor|, non-null if the interceptor wants to handle the request. + void MaybeStartLoader( + NavigationLoaderInterceptor* interceptor, + scoped_refptr<network::SharedURLLoaderFactory> single_request_factory); + + // This is the |fallback_callback| passed to + // NavigationLoaderInterceptor::MaybeCreateLoader. It allows an interceptor + // to initially elect to handle a request, and later decide to fallback to + // the default behavior. This is needed for service worker network fallback + // and signed exchange (SXG) fallback redirect. + void FallbackToNonInterceptedRequest(bool reset_subresource_loader_params); + + scoped_refptr<network::SharedURLLoaderFactory> + PrepareForNonInterceptedRequest(uint32_t* out_options); + + // TODO(kinuko): Merge this back to FollowRedirect(). + void FollowRedirectInternal( + const std::vector<std::string>& removed_headers, + const net::HttpRequestHeaders& modified_headers, + const net::HttpRequestHeaders& modified_cors_exempt_headers, + PreviewsState new_previews_state, + base::Time ui_post_time); + + // network::mojom::URLLoaderClient implementation: + void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override; + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle response_body) override; + void OnReceiveRedirect(const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head) override; + void OnUploadProgress(int64_t current_position, + int64_t total_size, + OnUploadProgressCallback callback) override; + void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override; + void OnTransferSizeUpdated(int32_t transfer_size_diff) override {} + void OnComplete(const network::URLLoaderCompletionStatus& status) override; + +#if BUILDFLAG(ENABLE_PLUGINS) + void CheckPluginAndContinueOnReceiveResponse( + network::mojom::URLResponseHeadPtr head, + network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, + bool is_download_if_not_handled_by_plugin, + const std::vector<WebPluginInfo>& plugins); +#endif + + void CallOnReceivedResponse( + network::mojom::URLResponseHeadPtr head, + network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, + bool is_download); + bool MaybeCreateLoaderForResponse( + network::mojom::URLResponseHeadPtr* response); + std::vector<std::unique_ptr<blink::URLLoaderThrottle>> + CreateURLLoaderThrottles(); + std::unique_ptr<SignedExchangeRequestHandler> + CreateSignedExchangeRequestHandler( + const NavigationRequestInfo& request_info, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + scoped_refptr<SignedExchangePrefetchMetricRecorder> + signed_exchange_prefetch_metric_recorder, + std::string accept_langs); + void ParseHeaders(const GURL& url, + network::mojom::URLResponseHead* head, + base::OnceClosure continuation); + // NavigationURLLoader implementation: void FollowRedirect( const std::vector<std::string>& removed_headers, @@ -59,31 +161,29 @@ class CONTENT_EXPORT NavigationURLLoaderImpl : public NavigationURLLoader { const net::HttpRequestHeaders& modified_cors_exempt_headers, PreviewsState new_previews_state) override; - void OnReceiveResponse( + void NotifyRequestStarted(base::TimeTicks timestamp); + void NotifyResponseStarted( network::mojom::URLResponseHeadPtr response_head, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, mojo::ScopedDataPipeConsumerHandle response_body, const GlobalRequestID& global_request_id, - bool is_download, - base::TimeDelta total_ui_to_io_time, - base::Time io_post_time); - void OnReceiveRedirect(net::RedirectInfo redirect_info, - network::mojom::URLResponseHeadPtr response, - base::Time io_post_time); - void OnComplete(const network::URLLoaderCompletionStatus& status); + bool is_download); + void NotifyRequestRedirected(net::RedirectInfo redirect_info, + network::mojom::URLResponseHeadPtr response); + void NotifyRequestFailed(const network::URLLoaderCompletionStatus& status); // Intercepts loading of frame requests when network service is enabled and - // either a network::mojom::TrustedURLLoaderHeaderClient is being used or for - // schemes not handled by network service (e.g. files). This must be called on - // the UI thread or before threads start. + // either a network::mojom::TrustedURLLoaderHeaderClient is being used or + // for schemes not handled by network service (e.g. files). This must be + // called on the UI thread or before threads start. using URLLoaderFactoryInterceptor = base::RepeatingCallback<void( mojo::PendingReceiver<network::mojom::URLLoaderFactory>* receiver)>; static void SetURLLoaderFactoryInterceptorForTesting( const URLLoaderFactoryInterceptor& interceptor); // Creates a URLLoaderFactory for a navigation. The factory uses - // |header_client|. This should have the same settings as the factory from the - // URLLoaderFactoryGetter. Called on the UI thread. + // |header_client|. This should have the same settings as the factory from + // the URLLoaderFactoryGetter. Called on the UI thread. static void CreateURLLoaderFactoryWithHeaderClient( mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient> header_client, @@ -91,18 +191,102 @@ class CONTENT_EXPORT NavigationURLLoaderImpl : public NavigationURLLoader { StoragePartitionImpl* partition); private: - class URLLoaderRequestController; - void OnRequestStarted(base::TimeTicks timestamp); + // TODO(kinuko): This can be a file-local private anonymous function. + static uint32_t GetURLLoaderOptions(bool is_main_frame); void BindNonNetworkURLLoaderFactoryReceiver( - int frame_tree_node_id, const GURL& url, mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver); NavigationURLLoaderDelegate* delegate_; + BrowserContext* browser_context_; + StoragePartitionImpl* storage_partition_; + ServiceWorkerMainResourceHandle* service_worker_handle_; + + std::unique_ptr<network::ResourceRequest> resource_request_; + std::unique_ptr<NavigationRequestInfo> request_info_; - // Lives on the IO thread. - std::unique_ptr<URLLoaderRequestController> request_controller_; + // Current URL that is being navigated, updated after redirection. + GURL url_; + + // Redirect URL chain. + std::vector<GURL> url_chain_; + + const int frame_tree_node_id_; + const GlobalRequestID global_request_id_; + net::RedirectInfo redirect_info_; + int redirect_limit_ = net::URLRequest::kMaxRedirects; + base::RepeatingCallback<WebContents*()> web_contents_getter_; + std::unique_ptr<NavigationUIData> navigation_ui_data_; + + scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory_; + std::unique_ptr<blink::ThrottlingURLLoader> url_loader_; + + // Caches the modified request headers provided by clients during redirect, + // will be consumed by next |url_loader_->FollowRedirect()|. + std::vector<std::string> url_loader_removed_headers_; + net::HttpRequestHeaders url_loader_modified_headers_; + net::HttpRequestHeaders url_loader_modified_cors_exempt_headers_; + + // Currently used by the AppCache loader to pass its factory to the + // renderer which enables it to handle subresources. + base::Optional<SubresourceLoaderParams> subresource_loader_params_; + + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors_; + size_t interceptor_index_ = 0; + + // Set to true if the default URLLoader (network service) was used for the + // current navigation. + bool default_loader_used_ = false; + + // URLLoaderClient receiver for loaders created for responses received from + // the network loader. + mojo::Receiver<network::mojom::URLLoaderClient> response_loader_receiver_{ + this}; + + // URLLoader instance for response loaders, i.e loaders created for handling + // responses received from the network URLLoader. + mojo::PendingRemote<network::mojom::URLLoader> response_url_loader_; + + // Set to true if we receive a valid response from a URLLoader, i.e. + // URLLoaderClient::OnStartLoadingResponseBody() is called. + bool received_response_ = false; + + // When URLLoaderClient::OnReceiveResponse() is called. For UMA. + base::TimeTicks on_receive_response_time_; + + bool started_ = false; + + // The completion status if it has been received. This is needed to handle + // the case that the response is intercepted by download, and OnComplete() + // is already called while we are transferring the |url_loader_| and + // response body to download code. + base::Optional<network::URLLoaderCompletionStatus> status_; + + // Before creating this URLLoaderRequestController on UI thread, the + // embedder may have elected to proxy the URLLoaderFactory receiver, in + // which case these fields will contain input (remote) and output (receiver) + // endpoints for the proxy. If this controller is handling a receiver for + // which proxying is supported, receivers will be plumbed through these + // endpoints. + // + // Note that these are only used for receivers that go to the Network + // Service. + mojo::PendingReceiver<network::mojom::URLLoaderFactory> + proxied_factory_receiver_; + mojo::PendingRemote<network::mojom::URLLoaderFactory> proxied_factory_remote_; + + // The schemes that this loader can use. For anything else we'll try + // external protocol handlers. + std::set<std::string> known_schemes_; + + // True when a proxy will handle the redirect checks, or when an interceptor + // intentionally returned unsafe redirect response + // (eg: NavigationLoaderInterceptor for loading a local Web Bundle file). + bool bypass_redirect_checks_ = false; + + network::mojom::URLResponseHeadPtr head_; + mojo::ScopedDataPipeConsumerHandle response_body_; NavigationDownloadPolicy download_policy_; @@ -110,6 +294,14 @@ class CONTENT_EXPORT NavigationURLLoaderImpl : public NavigationURLLoader { ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_url_loader_factories_; + // Lazily initialized and used in the case of non-network resource + // navigations. Keyed by URL scheme. + // (These are cloned by entries populated in + // non_network_url_loader_factories_ and are ready to use, i.e. preparation + // calls like WillCreateURLLoaderFactory are already called) + std::map<std::string, mojo::Remote<network::mojom::URLLoaderFactory>> + non_network_url_loader_factory_remotes_; + // Counts the time overhead of all the hops from the IO to the UI threads. base::TimeDelta io_to_ui_time_; diff --git a/chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc b/chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc index 82ebc55f63f..c474d0e8fab 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc +++ b/chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc @@ -97,7 +97,8 @@ class TestNavigationLoaderInterceptor : public NavigationLoaderInterceptor { base::BindOnce(&TestNavigationLoaderInterceptor::DeleteURLLoader, base::Unretained(this)), std::move(receiver), 0 /* options */, resource_request, - std::move(client), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + std::move(client), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0, /* request_id */ 0 /* keepalive_request_size */, resource_scheduler_client_, nullptr /* keepalive_statistics_recorder */, diff --git a/chromium/content/browser/loader/prefetch_browsertest.cc b/chromium/content/browser/loader/prefetch_browsertest.cc index 387af864050..3244122bd0f 100644 --- a/chromium/content/browser/loader/prefetch_browsertest.cc +++ b/chromium/content/browser/loader/prefetch_browsertest.cc @@ -249,6 +249,15 @@ IN_PROC_BROWSER_TEST_P(PrefetchBrowserTest, } IN_PROC_BROWSER_TEST_P(PrefetchBrowserTest, + CrossOriginDocumentFromOpaqueOrigin) { + // Prefetching as=document from a data: URL does not crash the renderer. + EXPECT_TRUE(NavigateToURL( + shell(), + GURL("data:text/html,<title>Data URL Prefetch Target</title><link " + "rel=prefetch as=document href=https://google.com>"))); +} + +IN_PROC_BROWSER_TEST_P(PrefetchBrowserTest, CrossOriginDocumentNotReusedAsNestedFrameNavigation) { // This test is relevant only with SplitCache. // TODO(crbug.com/910708): Remove this early-return when SplitCache is enabled diff --git a/chromium/content/browser/loader/prefetch_url_loader.cc b/chromium/content/browser/loader/prefetch_url_loader.cc index 4b652887ba2..9c4462083d2 100644 --- a/chromium/content/browser/loader/prefetch_url_loader.cc +++ b/chromium/content/browser/loader/prefetch_url_loader.cc @@ -12,6 +12,7 @@ #include "content/browser/web_package/signed_exchange_prefetch_handler.h" #include "content/browser/web_package/signed_exchange_prefetch_metric_recorder.h" #include "content/browser/web_package/signed_exchange_utils.h" +#include "content/public/browser/browser_context.h" #include "content/public/common/content_features.h" #include "net/base/load_flags.h" #include "net/http/http_request_headers.h" @@ -43,7 +44,6 @@ PrefetchURLLoader::PrefetchURLLoader( signed_exchange_prefetch_metric_recorder, scoped_refptr<PrefetchedSignedExchangeCache> prefetched_signed_exchange_cache, - base::WeakPtr<storage::BlobStorageContext> blob_storage_context, const std::string& accept_langs, RecursivePrefetchTokenGenerator recursive_prefetch_token_generator) : frame_tree_node_id_(frame_tree_node_id), diff --git a/chromium/content/browser/loader/prefetch_url_loader.h b/chromium/content/browser/loader/prefetch_url_loader.h index d2ecfa5cac5..342a8e5090a 100644 --- a/chromium/content/browser/loader/prefetch_url_loader.h +++ b/chromium/content/browser/loader/prefetch_url_loader.h @@ -24,10 +24,6 @@ #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "url/gurl.h" -namespace storage { -class BlobStorageContext; -} // namespace storage - namespace network { class SharedURLLoaderFactory; } @@ -43,7 +39,8 @@ class PrefetchedSignedExchangeCacheAdapter; class SignedExchangePrefetchHandler; class SignedExchangePrefetchMetricRecorder; -// PrefetchURLLoader which basically just keeps draining the data. +// A URLLoader for loading a prefetch request, including <link rel="prefetch">. +// It basically just keeps draining the data. class CONTENT_EXPORT PrefetchURLLoader : public network::mojom::URLLoader, public network::mojom::URLLoaderClient, public mojo::DataPipeDrainer::Client { @@ -72,7 +69,6 @@ class CONTENT_EXPORT PrefetchURLLoader : public network::mojom::URLLoader, signed_exchange_prefetch_metric_recorder, scoped_refptr<PrefetchedSignedExchangeCache> prefetched_signed_exchange_cache, - base::WeakPtr<storage::BlobStorageContext> blob_storage_context, const std::string& accept_langs, RecursivePrefetchTokenGenerator recursive_prefetch_token_generator); ~PrefetchURLLoader() override; diff --git a/chromium/content/browser/loader/prefetch_url_loader_service.cc b/chromium/content/browser/loader/prefetch_url_loader_service.cc index 8e8dfa97694..7f222002556 100644 --- a/chromium/content/browser/loader/prefetch_url_loader_service.cc +++ b/chromium/content/browser/loader/prefetch_url_loader_service.cc @@ -7,13 +7,12 @@ #include "base/bind.h" #include "base/feature_list.h" #include "base/time/default_tick_clock.h" -#include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/loader/prefetch_url_loader.h" -#include "content/browser/loader/url_loader_throttles.h" #include "content/browser/url_loader_factory_getter.h" #include "content/browser/web_package/prefetched_signed_exchange_cache.h" #include "content/public/browser/content_browser_client.h" +#include "content/public/browser/url_loader_throttles.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_client.h" #include "mojo/public/cpp/bindings/message.h" @@ -23,7 +22,6 @@ #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" -#include "storage/browser/blob/blob_storage_context.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/url_loader_throttle.h" #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h" @@ -218,8 +216,7 @@ void PrefetchURLLoaderService::CreateLoaderAndStart( &PrefetchURLLoaderService::CreateURLLoaderThrottles, this, resource_request, current_context.frame_tree_node_id), browser_context_, signed_exchange_prefetch_metric_recorder_, - std::move(prefetched_signed_exchange_cache), blob_storage_context_, - accept_langs_, + std::move(prefetched_signed_exchange_cache), accept_langs_, base::BindOnce( &PrefetchURLLoaderService::GenerateRecursivePrefetchToken, this, current_context.weak_ptr_factory.GetWeakPtr())), diff --git a/chromium/content/browser/loader/prefetch_url_loader_service.h b/chromium/content/browser/loader/prefetch_url_loader_service.h index 98e29f04230..f9305193991 100644 --- a/chromium/content/browser/loader/prefetch_url_loader_service.h +++ b/chromium/content/browser/loader/prefetch_url_loader_service.h @@ -20,10 +20,6 @@ #include "third_party/blink/public/common/loader/url_loader_factory_bundle.h" #include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h" -namespace storage { -class BlobStorageContext; -} - namespace blink { class URLLoaderThrottle; } @@ -35,6 +31,9 @@ class PrefetchedSignedExchangeCache; class RenderFrameHostImpl; class URLLoaderFactoryGetter; +// A URLLoaderFactory that can be passed to a renderer to use for performing +// prefetches. The renderer uses it for prefetch requests including <link +// rel="prefetch">. class CONTENT_EXPORT PrefetchURLLoaderService final : public base::RefCountedThreadSafe< PrefetchURLLoaderService, @@ -127,11 +126,6 @@ class CONTENT_EXPORT PrefetchURLLoaderService final std::string accept_langs_; - // Used to create a BlobDataHandle from a DataPipe of signed exchange's inner - // response body to store to |prefetched_signed_exchange_cache_| when - // SignedExchangeSubresourcePrefetch is enabled. - base::WeakPtr<storage::BlobStorageContext> blob_storage_context_; - DISALLOW_COPY_AND_ASSIGN(PrefetchURLLoaderService); }; diff --git a/chromium/content/browser/loader/quic_transport_browsertest.cc b/chromium/content/browser/loader/quic_transport_browsertest.cc index cc588e56f18..91a6797255b 100644 --- a/chromium/content/browser/loader/quic_transport_browsertest.cc +++ b/chromium/content/browser/loader/quic_transport_browsertest.cc @@ -335,5 +335,53 @@ IN_PROC_BROWSER_TEST_F(QuicTransportBrowserTest, BidirectionalStream) { ASSERT_TRUE(WaitForTitle(ASCIIToUTF16("PASS"), {ASCIIToUTF16("FAIL")})); } +IN_PROC_BROWSER_TEST_F(QuicTransportBrowserTest, CertificateFingerprint) { + ASSERT_TRUE(embedded_test_server()->Start()); + ASSERT_TRUE( + NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html"))); + + ASSERT_TRUE(WaitForTitle(ASCIIToUTF16("Title Of Awesomeness"))); + + ASSERT_TRUE(ExecuteScript( + shell(), base::StringPrintf(R"JS( + async function run() { + // The connection fails because the fingerprint does not match. + const transport = new QuicTransport( + 'quic-transport://localhost:%d/echo', { + serverCertificateFingerprints: [ + { + algorithm: "sha-256", + value: "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:" + + "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00", + }, + ], + }); + + let fulfilled = false; + try { + await transport.ready; + fulfilled = true + } catch {} + + if (fulfilled) { + throw Error('ready should be rejected'); + } + + try { + await transport.closed; + } catch (e) { + return; + } + throw Error('closed should be rejected'); + } + + run().then(() => { document.title = 'PASS'; }, + (e) => { console.log(e); document.title = 'FAIL'; }); +)JS", + server_.server_address().port()))); + + ASSERT_TRUE(WaitForTitle(ASCIIToUTF16("PASS"), {ASCIIToUTF16("FAIL")})); +} + } // namespace } // namespace content diff --git a/chromium/content/browser/loader/url_loader_throttles.cc b/chromium/content/browser/loader/url_loader_throttles.cc index 5861a44ba28..3ab0ea5b489 100644 --- a/chromium/content/browser/loader/url_loader_throttles.cc +++ b/chromium/content/browser/loader/url_loader_throttles.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/loader/url_loader_throttles.h" +#include "content/public/browser/url_loader_throttles.h" #include "components/variations/net/omnibox_url_loader_throttle.h" #include "components/variations/net/variations_url_loader_throttle.h" diff --git a/chromium/content/browser/loader/url_loader_throttles.h b/chromium/content/browser/loader/url_loader_throttles.h deleted file mode 100644 index dadf9544f09..00000000000 --- a/chromium/content/browser/loader/url_loader_throttles.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_LOADER_URL_LOADER_THROTTLES_H_ -#define CONTENT_BROWSER_LOADER_URL_LOADER_THROTTLES_H_ - -#include "base/callback.h" - -namespace blink { -class URLLoaderThrottle; -} // namespace blink - -namespace network { -struct ResourceRequest; -} // namespace network - -namespace content { - -class BrowserContext; -class NavigationUIData; -class WebContents; - -// Wrapper around ContentBrowserClient::CreateURLLoaderThrottles which inserts -// additional content specific throttles. -std::vector<std::unique_ptr<blink::URLLoaderThrottle>> -CreateContentBrowserURLLoaderThrottles( - const network::ResourceRequest& request, - BrowserContext* browser_context, - const base::RepeatingCallback<WebContents*()>& wc_getter, - NavigationUIData* navigation_ui_data, - int frame_tree_node_id); - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_URL_LOADER_THROTTLES_H_ |