|  | // Copyright 2017 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. | 
|  |  | 
|  | #include "content/browser/loader/navigation_url_loader_impl.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/debug/alias.h" | 
|  | #include "base/feature_list.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/optional.h" | 
|  | #include "base/rand_util.h" | 
|  | #include "base/stl_util.h" | 
|  | #include "base/task/post_task.h" | 
|  | #include "base/trace_event/trace_event.h" | 
|  | #include "build/build_config.h" | 
|  | #include "components/download/public/common/download_stats.h" | 
|  | #include "content/browser/appcache/appcache_navigation_handle.h" | 
|  | #include "content/browser/appcache/appcache_navigation_handle_core.h" | 
|  | #include "content/browser/appcache/appcache_request_handler.h" | 
|  | #include "content/browser/blob_storage/chrome_blob_storage_context.h" | 
|  | #include "content/browser/devtools/render_frame_devtools_agent_host.h" | 
|  | #include "content/browser/file_url_loader_factory.h" | 
|  | #include "content/browser/fileapi/file_system_url_loader_factory.h" | 
|  | #include "content/browser/frame_host/frame_tree_node.h" | 
|  | #include "content/browser/frame_host/navigation_request_info.h" | 
|  | #include "content/browser/loader/navigation_loader_interceptor.h" | 
|  | #include "content/browser/loader/navigation_url_loader_delegate.h" | 
|  | #include "content/browser/loader/resource_dispatcher_host_impl.h" | 
|  | #include "content/browser/loader/resource_request_info_impl.h" | 
|  | #include "content/browser/resource_context_impl.h" | 
|  | #include "content/browser/service_worker/service_worker_navigation_handle.h" | 
|  | #include "content/browser/service_worker/service_worker_navigation_handle_core.h" | 
|  | #include "content/browser/service_worker/service_worker_request_handler.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" | 
|  | #include "content/browser/web_package/signed_exchange_consts.h" | 
|  | #include "content/browser/web_package/signed_exchange_request_handler.h" | 
|  | #include "content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.h" | 
|  | #include "content/browser/web_package/signed_exchange_utils.h" | 
|  | #include "content/browser/webui/url_data_manager_backend.h" | 
|  | #include "content/browser/webui/web_ui_url_loader_factory_internal.h" | 
|  | #include "content/common/mime_sniffing_throttle.h" | 
|  | #include "content/common/navigation_subresource_loader_params.h" | 
|  | #include "content/common/net/record_load_histograms.h" | 
|  | #include "content/common/throttling_url_loader.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "content/public/browser/browser_task_traits.h" | 
|  | #include "content/public/browser/browser_thread.h" | 
|  | #include "content/public/browser/content_browser_client.h" | 
|  | #include "content/public/browser/download_utils.h" | 
|  | #include "content/public/browser/global_request_id.h" | 
|  | #include "content/public/browser/navigation_data.h" | 
|  | #include "content/public/browser/navigation_ui_data.h" | 
|  | #include "content/public/browser/plugin_service.h" | 
|  | #include "content/public/browser/resource_dispatcher_host_delegate.h" | 
|  | #include "content/public/browser/ssl_status.h" | 
|  | #include "content/public/browser/url_loader_request_interceptor.h" | 
|  | #include "content/public/common/content_features.h" | 
|  | #include "content/public/common/referrer.h" | 
|  | #include "content/public/common/url_constants.h" | 
|  | #include "content/public/common/url_utils.h" | 
|  | #include "content/public/common/webplugininfo.h" | 
|  | #include "net/base/load_flags.h" | 
|  | #include "net/cert/sct_status_flags.h" | 
|  | #include "net/cert/signed_certificate_timestamp_and_status.h" | 
|  | #include "net/http/http_content_disposition.h" | 
|  | #include "net/http/http_request_headers.h" | 
|  | #include "net/http/http_status_code.h" | 
|  | #include "net/ssl/ssl_info.h" | 
|  | #include "net/traffic_annotation/network_traffic_annotation.h" | 
|  | #include "net/url_request/redirect_util.h" | 
|  | #include "net/url_request/url_request.h" | 
|  | #include "net/url_request/url_request_context.h" | 
|  | #include "net/url_request/url_request_context_getter.h" | 
|  | #include "ppapi/buildflags/buildflags.h" | 
|  | #include "services/network/loader_util.h" | 
|  | #include "services/network/public/cpp/features.h" | 
|  | #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" | 
|  | #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" | 
|  | #include "services/network/public/mojom/request_context_frame_type.mojom.h" | 
|  | #include "services/network/public/mojom/url_loader_factory.mojom.h" | 
|  | #include "services/service_manager/public/cpp/connector.h" | 
|  | #include "third_party/blink/public/common/mime_util/mime_util.h" | 
|  | #include "third_party/blink/public/common/service_worker/service_worker_utils.h" | 
|  | #include "url/gurl.h" | 
|  |  | 
|  | #if defined(OS_ANDROID) | 
|  | #include "content/browser/android/content_url_loader_factory.h" | 
|  | #endif | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class NavigationLoaderInterceptorBrowserContainer | 
|  | : public NavigationLoaderInterceptor { | 
|  | public: | 
|  | explicit NavigationLoaderInterceptorBrowserContainer( | 
|  | std::unique_ptr<URLLoaderRequestInterceptor> browser_interceptor) | 
|  | : browser_interceptor_(std::move(browser_interceptor)) {} | 
|  |  | 
|  | ~NavigationLoaderInterceptorBrowserContainer() override = default; | 
|  |  | 
|  | void MaybeCreateLoader( | 
|  | const network::ResourceRequest& tentative_resource_request, | 
|  | ResourceContext* resource_context, | 
|  | LoaderCallback callback, | 
|  | FallbackCallback fallback_callback) override { | 
|  | browser_interceptor_->MaybeCreateLoader( | 
|  | tentative_resource_request, resource_context, std::move(callback)); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<URLLoaderRequestInterceptor> browser_interceptor_; | 
|  | }; | 
|  |  | 
|  | // Only used on the IO thread. | 
|  | base::LazyInstance<NavigationURLLoaderImpl::BeginNavigationInterceptor>::Leaky | 
|  | g_interceptor = LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | // Returns true if interception by NavigationLoaderInterceptors is enabled. | 
|  | // Both ServiceWorkerServicification and SignedExchange require the loader | 
|  | // interception. So even if NetworkService is not enabled, returns true when one | 
|  | // of them is enabled. | 
|  | bool IsLoaderInterceptionEnabled() { | 
|  | return base::FeatureList::IsEnabled(network::features::kNetworkService) || | 
|  | blink::ServiceWorkerUtils::IsServicificationEnabled() || | 
|  | signed_exchange_utils::IsSignedExchangeHandlingEnabled(); | 
|  | } | 
|  |  | 
|  | // Request ID for browser initiated requests. We start at -2 on the same lines | 
|  | // as ResourceDispatcherHostImpl. | 
|  | int g_next_request_id = -2; | 
|  | GlobalRequestID MakeGlobalRequestID() { | 
|  | return GlobalRequestID(-1, g_next_request_id--); | 
|  | } | 
|  |  | 
|  | size_t GetCertificateChainsSizeInKB(const net::SSLInfo& ssl_info) { | 
|  | base::Pickle cert_pickle; | 
|  | ssl_info.cert->Persist(&cert_pickle); | 
|  | base::Pickle unverified_cert_pickle; | 
|  | ssl_info.unverified_cert->Persist(&unverified_cert_pickle); | 
|  | return (cert_pickle.size() + unverified_cert_pickle.size()) / 1000; | 
|  | } | 
|  |  | 
|  | WebContents* GetWebContentsFromFrameTreeNodeID(int frame_tree_node_id) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | FrameTreeNode* frame_tree_node = | 
|  | FrameTreeNode::GloballyFindByID(frame_tree_node_id); | 
|  | if (!frame_tree_node) | 
|  | return nullptr; | 
|  |  | 
|  | return WebContentsImpl::FromFrameTreeNode(frame_tree_node); | 
|  | } | 
|  |  | 
|  | const net::NetworkTrafficAnnotationTag kNavigationUrlLoaderTrafficAnnotation = | 
|  | net::DefineNetworkTrafficAnnotation("navigation_url_loader", R"( | 
|  | semantics { | 
|  | sender: "Navigation URL Loader" | 
|  | description: | 
|  | "This request is issued by a main frame navigation to fetch the " | 
|  | "content of the page that is being navigated to." | 
|  | trigger: | 
|  | "Navigating Chrome (by clicking on a link, bookmark, history item, " | 
|  | "using session restore, etc)." | 
|  | data: | 
|  | "Arbitrary site-controlled data can be included in the URL, HTTP " | 
|  | "headers, and request body. Requests may include cookies and " | 
|  | "site-specific credentials." | 
|  | destination: WEBSITE | 
|  | } | 
|  | policy { | 
|  | cookies_allowed: YES | 
|  | cookies_store: "user" | 
|  | setting: "This feature cannot be disabled." | 
|  | chrome_policy { | 
|  | URLBlacklist { | 
|  | URLBlacklist: { entries: '*' } | 
|  | } | 
|  | } | 
|  | chrome_policy { | 
|  | URLWhitelist { | 
|  | URLWhitelist { } | 
|  | } | 
|  | } | 
|  | } | 
|  | comments: | 
|  | "Chrome would be unable to navigate to websites without this type of " | 
|  | "request. Using either URLBlacklist or URLWhitelist policies (or a " | 
|  | "combination of both) limits the scope of these requests." | 
|  | )"); | 
|  |  | 
|  | std::unique_ptr<network::ResourceRequest> CreateResourceRequest( | 
|  | NavigationRequestInfo* request_info, | 
|  | int frame_tree_node_id, | 
|  | bool allow_download) { | 
|  | // 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; | 
|  | new_request->url = request_info->common_params.url; | 
|  | new_request->site_for_cookies = request_info->site_for_cookies; | 
|  |  | 
|  | net::RequestPriority net_priority = net::HIGHEST; | 
|  | if (!request_info->is_main_frame && | 
|  | base::FeatureList::IsEnabled(features::kLowPriorityIframes)) { | 
|  | net_priority = net::LOWEST; | 
|  | } | 
|  | new_request->priority = net_priority; | 
|  |  | 
|  | new_request->render_frame_id = frame_tree_node_id; | 
|  |  | 
|  | // The code below to set fields like request_initiator, referrer, etc has | 
|  | // been copied from ResourceDispatcherHostImpl. We did not refactor the | 
|  | // common code into a function, because RDHI uses accessor functions on the | 
|  | // URLRequest class to set these fields. whereas we use ResourceRequest here. | 
|  | new_request->request_initiator = request_info->begin_params->initiator_origin; | 
|  | new_request->referrer = request_info->common_params.referrer.url; | 
|  | new_request->referrer_policy = Referrer::ReferrerPolicyForUrlRequest( | 
|  | request_info->common_params.referrer.policy); | 
|  | new_request->headers.AddHeadersFromString( | 
|  | request_info->begin_params->headers); | 
|  |  | 
|  | std::string accept_value = network::kFrameAcceptHeader; | 
|  | if (signed_exchange_utils::ShouldAdvertiseAcceptHeader( | 
|  | url::Origin::Create(request_info->common_params.url))) { | 
|  | DCHECK(!accept_value.empty()); | 
|  | accept_value.append(kAcceptHeaderSignedExchangeSuffix); | 
|  | } | 
|  |  | 
|  | new_request->headers.SetHeader(network::kAcceptHeader, accept_value); | 
|  |  | 
|  | new_request->resource_type = request_info->is_main_frame | 
|  | ? RESOURCE_TYPE_MAIN_FRAME | 
|  | : RESOURCE_TYPE_SUB_FRAME; | 
|  | if (request_info->is_main_frame) | 
|  | new_request->update_first_party_url_on_redirect = true; | 
|  |  | 
|  | int load_flags = request_info->begin_params->load_flags; | 
|  | if (request_info->is_main_frame) | 
|  | load_flags |= net::LOAD_MAIN_FRAME_DEPRECATED; | 
|  |  | 
|  | // Sync loads should have maximum priority and should be the only | 
|  | // requests that have the ignore limits flag set. | 
|  | DCHECK(!(load_flags & net::LOAD_IGNORE_LIMITS)); | 
|  |  | 
|  | new_request->load_flags = load_flags; | 
|  |  | 
|  | new_request->request_body = request_info->common_params.post_data.get(); | 
|  | new_request->report_raw_headers = request_info->report_raw_headers; | 
|  | new_request->allow_download = allow_download; | 
|  | new_request->enable_load_timing = true; | 
|  |  | 
|  | new_request->fetch_request_mode = network::mojom::FetchRequestMode::kNavigate; | 
|  | new_request->fetch_credentials_mode = | 
|  | network::mojom::FetchCredentialsMode::kInclude; | 
|  | new_request->fetch_redirect_mode = network::mojom::FetchRedirectMode::kManual; | 
|  | new_request->fetch_request_context_type = | 
|  | static_cast<int>(request_info->begin_params->request_context_type); | 
|  | new_request->upgrade_if_insecure = request_info->upgrade_if_insecure; | 
|  | new_request->throttling_profile_id = request_info->devtools_frame_token; | 
|  | return new_request; | 
|  | } | 
|  |  | 
|  | // Used only when NetworkService is disabled but IsLoaderInterceptionEnabled() | 
|  | // is true. | 
|  | std::unique_ptr<NavigationRequestInfo> CreateNavigationRequestInfoForRedirect( | 
|  | const NavigationRequestInfo& previous_request_info, | 
|  | const network::ResourceRequest& updated_resource_request) { | 
|  | DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); | 
|  | DCHECK(IsLoaderInterceptionEnabled()); | 
|  |  | 
|  | CommonNavigationParams new_common_params = | 
|  | previous_request_info.common_params; | 
|  | new_common_params.url = updated_resource_request.url; | 
|  | new_common_params.referrer = | 
|  | Referrer(updated_resource_request.referrer, | 
|  | Referrer::NetReferrerPolicyToBlinkReferrerPolicy( | 
|  | updated_resource_request.referrer_policy)); | 
|  | new_common_params.method = updated_resource_request.method; | 
|  | new_common_params.post_data = updated_resource_request.request_body; | 
|  |  | 
|  | mojom::BeginNavigationParamsPtr new_begin_params = | 
|  | previous_request_info.begin_params.Clone(); | 
|  | new_begin_params->headers = updated_resource_request.headers.ToString(); | 
|  |  | 
|  | return std::make_unique<NavigationRequestInfo>( | 
|  | std::move(new_common_params), std::move(new_begin_params), | 
|  | updated_resource_request.site_for_cookies, | 
|  | previous_request_info.is_main_frame, | 
|  | previous_request_info.parent_is_main_frame, | 
|  | previous_request_info.are_ancestors_secure, | 
|  | previous_request_info.frame_tree_node_id, | 
|  | previous_request_info.is_for_guests_only, | 
|  | previous_request_info.report_raw_headers, | 
|  | previous_request_info.is_prerendering, | 
|  | previous_request_info.upgrade_if_insecure, | 
|  | nullptr /* blob_url_loader_factory */, | 
|  | previous_request_info.devtools_navigation_token, | 
|  | previous_request_info.devtools_frame_token); | 
|  | } | 
|  |  | 
|  | // Called for requests that we don't have a URLLoaderFactory for. | 
|  | void UnknownSchemeCallback( | 
|  | bool handled_externally, | 
|  | const network::ResourceRequest& /* resource_request */, | 
|  | network::mojom::URLLoaderRequest request, | 
|  | network::mojom::URLLoaderClientPtr client) { | 
|  | client->OnComplete(network::URLLoaderCompletionStatus( | 
|  | handled_externally ? net::ERR_ABORTED : net::ERR_UNKNOWN_URL_SCHEME)); | 
|  | } | 
|  |  | 
|  | // Returns whether this URL can be handled by the default network service | 
|  | // URLLoader. | 
|  | bool IsURLHandledByDefaultLoader(const GURL& url) { | 
|  | // Data URLs are only handled by the network service if | 
|  | // |enable_data_url_support| is set in NetworkContextParams. This is set to | 
|  | // true for the context used by NavigationURLLoaderImpl, so in addition to | 
|  | // checking whether the URL is handled by the network service, we also need to | 
|  | // check for the data scheme. | 
|  | return IsURLHandledByNetworkService(url) || url.SchemeIs(url::kDataScheme); | 
|  | } | 
|  |  | 
|  | // Determines whether it is safe to redirect from |from_url| to |to_url|. | 
|  | bool IsRedirectSafe(const GURL& from_url, | 
|  | const GURL& to_url, | 
|  | ResourceContext* resource_context) { | 
|  | return IsSafeRedirectTarget(from_url, to_url) && | 
|  | GetContentClient()->browser()->IsSafeRedirectTarget(to_url, | 
|  | resource_context); | 
|  | } | 
|  |  | 
|  | }  // 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, | 
|  | ResourceContext* resource_context, | 
|  | const GURL& url, | 
|  | bool is_main_frame, | 
|  | network::mojom::URLLoaderFactoryRequest proxied_factory_request, | 
|  | network::mojom::URLLoaderFactoryPtrInfo proxied_factory_info, | 
|  | 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)), | 
|  | resource_context_(resource_context), | 
|  | url_(url), | 
|  | is_main_frame_(is_main_frame), | 
|  | owner_(owner), | 
|  | response_loader_binding_(this), | 
|  | proxied_factory_request_(std::move(proxied_factory_request)), | 
|  | proxied_factory_info_(std::move(proxied_factory_info)), | 
|  | known_schemes_(std::move(known_schemes)), | 
|  | bypass_redirect_checks_(bypass_redirect_checks), | 
|  | weak_factory_(this) {} | 
|  |  | 
|  | ~URLLoaderRequestController() override { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  |  | 
|  | // 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_, resource_request_->resource_type, | 
|  | 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; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { | 
|  | // TODO(arthursonzogni): This is a temporary option. Remove this as soon | 
|  | // as the InterceptingResourceHandler is removed. | 
|  | // See https://crbug.com/791049. | 
|  | options |= network::mojom::kURLLoadOptionPauseOnResponseStarted; | 
|  | } | 
|  |  | 
|  | return options; | 
|  | } | 
|  |  | 
|  | SingleRequestURLLoaderFactory::RequestHandler | 
|  | CreateDefaultRequestHandlerForNonNetworkService( | 
|  | net::URLRequestContextGetter* url_request_context_getter, | 
|  | storage::FileSystemContext* upload_file_system_context, | 
|  | ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core, | 
|  | AppCacheNavigationHandleCore* appcache_handle_core, | 
|  | bool was_request_intercepted) const { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  | DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); | 
|  | DCHECK(started_); | 
|  |  | 
|  | return base::BindOnce( | 
|  | &URLLoaderRequestController::CreateNonNetworkServiceURLLoader, | 
|  | weak_factory_.GetWeakPtr(), | 
|  | base::Unretained(url_request_context_getter), | 
|  | base::Unretained(upload_file_system_context), | 
|  | std::make_unique<NavigationRequestInfo>(*request_info_), | 
|  | // If the request has already been intercepted, the request should not | 
|  | // be intercepted again. | 
|  | // S13nServiceWorker: Requests are intercepted by S13nServiceWorker | 
|  | // before the default request handler when needed, so we never need to | 
|  | // pass |service_worker_navigation_handle_core| here. | 
|  | base::Unretained( | 
|  | blink::ServiceWorkerUtils::IsServicificationEnabled() || | 
|  | was_request_intercepted | 
|  | ? nullptr | 
|  | : service_worker_navigation_handle_core), | 
|  | base::Unretained(was_request_intercepted ? nullptr | 
|  | : appcache_handle_core)); | 
|  | } | 
|  |  | 
|  | void CreateNonNetworkServiceURLLoader( | 
|  | net::URLRequestContextGetter* url_request_context_getter, | 
|  | storage::FileSystemContext* upload_file_system_context, | 
|  | std::unique_ptr<NavigationRequestInfo> request_info, | 
|  | ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core, | 
|  | AppCacheNavigationHandleCore* appcache_handle_core, | 
|  | const network::ResourceRequest& /* resource_request */, | 
|  | network::mojom::URLLoaderRequest url_loader, | 
|  | network::mojom::URLLoaderClientPtr url_loader_client) { | 
|  | // |resource_request| is unused here. Its info may not be the same as | 
|  | // |request_info|, because URLLoaderThrottles may have rewritten it. We | 
|  | // don't propagate the fields to |request_info| here because the request | 
|  | // will usually go to ResourceDispatcherHost which does its own request | 
|  | // modification independent of URLLoaderThrottles. | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  | DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); | 
|  | DCHECK(started_); | 
|  |  | 
|  | default_loader_used_ = true; | 
|  | if (signed_exchange_utils::IsSignedExchangeHandlingEnabled()) { | 
|  | // TODO(falken): Understand and add a comment about why | 
|  | // SignedExchangeRequestHandler is the only interceptor being added here. | 
|  | DCHECK(!network_loader_factory_); | 
|  | // 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. | 
|  | interceptors_.push_back(std::make_unique<SignedExchangeRequestHandler>( | 
|  | url::Origin::Create(request_info->common_params.url), | 
|  | request_info->common_params.url, | 
|  | GetURLLoaderOptions(request_info->is_main_frame), | 
|  | request_info->frame_tree_node_id, | 
|  | request_info->devtools_navigation_token, | 
|  | request_info->devtools_frame_token, request_info->report_raw_headers, | 
|  | request_info->begin_params->load_flags, | 
|  | base::MakeRefCounted< | 
|  | SignedExchangeURLLoaderFactoryForNonNetworkService>( | 
|  | resource_context_, url_request_context_getter), | 
|  | base::BindRepeating( | 
|  | &URLLoaderRequestController::CreateURLLoaderThrottles, | 
|  | base::Unretained(this)))); | 
|  | } | 
|  |  | 
|  | uint32_t options = GetURLLoaderOptions(request_info->is_main_frame); | 
|  |  | 
|  | bool intercepted = false; | 
|  | if (g_interceptor.Get()) { | 
|  | intercepted = g_interceptor.Get().Run( | 
|  | &url_loader, frame_tree_node_id_, 0 /* request_id */, options, | 
|  | *resource_request_.get(), &url_loader_client, | 
|  | net::MutableNetworkTrafficAnnotationTag( | 
|  | kNavigationUrlLoaderTrafficAnnotation)); | 
|  | } | 
|  |  | 
|  | // The ResourceDispatcherHostImpl can be null in unit tests. | 
|  | if (!intercepted && ResourceDispatcherHostImpl::Get()) { | 
|  | ResourceDispatcherHostImpl::Get()->BeginNavigationRequest( | 
|  | resource_context_, url_request_context_getter->GetURLRequestContext(), | 
|  | upload_file_system_context, *request_info, | 
|  | std::move(navigation_ui_data_), std::move(url_loader_client), | 
|  | std::move(url_loader), service_worker_navigation_handle_core, | 
|  | appcache_handle_core, options, global_request_id_); | 
|  | } | 
|  |  | 
|  | // TODO(arthursonzogni): Detect when the ResourceDispatcherHost didn't | 
|  | // create a URLLoader. When it doesn't, do not send OnRequestStarted(). | 
|  | base::PostTaskWithTraits( | 
|  | FROM_HERE, {BrowserThread::UI}, | 
|  | base::BindOnce(&NavigationURLLoaderImpl::OnRequestStarted, owner_, | 
|  | base::TimeTicks::Now())); | 
|  | } | 
|  |  | 
|  | // TODO(arthursonzogni): See if this could eventually be unified with Start(). | 
|  | void StartWithoutNetworkService( | 
|  | net::URLRequestContextGetter* url_request_context_getter, | 
|  | storage::FileSystemContext* upload_file_system_context, | 
|  | ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core, | 
|  | AppCacheNavigationHandleCore* appcache_handle_core, | 
|  | std::unique_ptr<NavigationRequestInfo> request_info, | 
|  | std::unique_ptr<NavigationUIData> navigation_ui_data) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  | DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); | 
|  | DCHECK(!started_); | 
|  | started_ = true; | 
|  | request_info_ = std::move(request_info); | 
|  | frame_tree_node_id_ = request_info_->frame_tree_node_id; | 
|  | web_contents_getter_ = base::BindRepeating( | 
|  | &GetWebContentsFromFrameTreeNodeID, frame_tree_node_id_); | 
|  | navigation_ui_data_ = std::move(navigation_ui_data); | 
|  | // The ResourceDispatcherHostImpl can be null in unit tests. | 
|  | ResourceDispatcherHostImpl* rph = ResourceDispatcherHostImpl::Get(); | 
|  | if (rph) | 
|  | global_request_id_ = rph->MakeGlobalRequestID(); | 
|  |  | 
|  | default_request_handler_factory_ = base::BindRepeating( | 
|  | &URLLoaderRequestController:: | 
|  | CreateDefaultRequestHandlerForNonNetworkService, | 
|  | // base::Unretained(this) is safe since | 
|  | // |default_request_handler_factory_| could be called only from |this|. | 
|  | base::Unretained(this), base::Unretained(url_request_context_getter), | 
|  | base::Unretained(upload_file_system_context), | 
|  | base::Unretained(service_worker_navigation_handle_core), | 
|  | base::Unretained(appcache_handle_core)); | 
|  |  | 
|  | // 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_ = ThrottlingURLLoader::CreateLoaderAndStart( | 
|  | network::SharedURLLoaderFactory::Create( | 
|  | std::move(request_info_->blob_url_loader_factory)), | 
|  | CreateURLLoaderThrottles(), -1 /* routing_id */, 0 /* request_id? */, | 
|  | network::mojom::kURLLoadOptionNone, resource_request_.get(), this, | 
|  | kNavigationUrlLoaderTrafficAnnotation, | 
|  | base::ThreadTaskRunnerHandle::Get()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If S13nServiceWorker is disabled, just use | 
|  | // |default_request_handler_factory_| and return. The non network service | 
|  | // request handling goes through ResourceDispatcherHost which has legacy | 
|  | // hooks for service worker (ServiceWorkerRequestInterceptor), so no service | 
|  | // worker interception is needed here. | 
|  | if (!blink::ServiceWorkerUtils::IsServicificationEnabled() || | 
|  | !service_worker_navigation_handle_core) { | 
|  | url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( | 
|  | base::MakeRefCounted<SingleRequestURLLoaderFactory>( | 
|  | default_request_handler_factory_.Run( | 
|  | false /* was_request_intercepted */)), | 
|  | CreateURLLoaderThrottles(), -1 /* routing_id */, 0 /* request_id */, | 
|  | network::mojom::kURLLoadOptionNone, resource_request_.get(), | 
|  | this /* client */, kNavigationUrlLoaderTrafficAnnotation, | 
|  | base::ThreadTaskRunnerHandle::Get()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Otherwise, if S13nServiceWorker is enabled, create an interceptor so | 
|  | // S13nServiceWorker has a chance to intercept the request. | 
|  | std::unique_ptr<NavigationLoaderInterceptor> service_worker_interceptor = | 
|  | CreateServiceWorkerInterceptor(*request_info_, | 
|  | service_worker_navigation_handle_core); | 
|  | // If an interceptor is not created for some reasons (e.g. the origin is not | 
|  | // secure), we no longer have to go through the rest of the network service | 
|  | // code. | 
|  | if (!service_worker_interceptor) { | 
|  | url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( | 
|  | base::MakeRefCounted<SingleRequestURLLoaderFactory>( | 
|  | default_request_handler_factory_.Run( | 
|  | false /* was_request_intercepted */)), | 
|  | CreateURLLoaderThrottles(), -1 /* routing_id */, 0 /* request_id */, | 
|  | network::mojom::kURLLoadOptionNone, resource_request_.get(), | 
|  | this /* client */, kNavigationUrlLoaderTrafficAnnotation, | 
|  | base::ThreadTaskRunnerHandle::Get()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | interceptors_.push_back(std::move(service_worker_interceptor)); | 
|  |  | 
|  | Restart(); | 
|  | } | 
|  |  | 
|  | void Start( | 
|  | std::unique_ptr<network::SharedURLLoaderFactoryInfo> | 
|  | network_loader_factory_info, | 
|  | ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core, | 
|  | AppCacheNavigationHandleCore* appcache_handle_core, | 
|  | std::unique_ptr<NavigationRequestInfo> request_info, | 
|  | std::unique_ptr<NavigationUIData> navigation_ui_data, | 
|  | network::mojom::URLLoaderFactoryPtrInfo factory_for_webui, | 
|  | int frame_tree_node_id, | 
|  | std::unique_ptr<service_manager::Connector> connector) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  | DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); | 
|  | DCHECK(!started_); | 
|  | global_request_id_ = MakeGlobalRequestID(); | 
|  | frame_tree_node_id_ = frame_tree_node_id; | 
|  | started_ = true; | 
|  | web_contents_getter_ = | 
|  | base::Bind(&GetWebContentsFromFrameTreeNodeID, frame_tree_node_id); | 
|  | navigation_ui_data_ = std::move(navigation_ui_data); | 
|  |  | 
|  | DCHECK(network_loader_factory_info); | 
|  | network_loader_factory_ = network::SharedURLLoaderFactory::Create( | 
|  | std::move(network_loader_factory_info)); | 
|  |  | 
|  | if (resource_request_->request_body) { | 
|  | GetBodyBlobDataHandles(resource_request_->request_body.get(), | 
|  | resource_context_, &blob_handles_); | 
|  | } | 
|  |  | 
|  | // 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_ = 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; | 
|  | } | 
|  |  | 
|  | // 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_ = 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; | 
|  | } | 
|  |  | 
|  | if (service_worker_navigation_handle_core) { | 
|  | std::unique_ptr<NavigationLoaderInterceptor> service_worker_interceptor = | 
|  | CreateServiceWorkerInterceptor(*request_info, | 
|  | service_worker_navigation_handle_core); | 
|  | if (service_worker_interceptor) | 
|  | interceptors_.push_back(std::move(service_worker_interceptor)); | 
|  | } | 
|  |  | 
|  | if (appcache_handle_core) { | 
|  | std::unique_ptr<NavigationLoaderInterceptor> appcache_interceptor = | 
|  | AppCacheRequestHandler::InitializeForMainResourceNetworkService( | 
|  | *resource_request_, appcache_handle_core->host()->GetWeakPtr(), | 
|  | network_loader_factory_); | 
|  | if (appcache_interceptor) | 
|  | interceptors_.push_back(std::move(appcache_interceptor)); | 
|  | } | 
|  |  | 
|  | if (signed_exchange_utils::IsSignedExchangeHandlingEnabled()) { | 
|  | // Signed Exchange is currently disabled when Network Service is enabled | 
|  | // (https://crbug.com/849935), but still create | 
|  | // SignedExchangeRequestHandler in order to show error message (and | 
|  | // devtools warning) to users. | 
|  |  | 
|  | // 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. | 
|  | interceptors_.push_back(std::make_unique<SignedExchangeRequestHandler>( | 
|  | url::Origin::Create(request_info->common_params.url), | 
|  | request_info->common_params.url, | 
|  | GetURLLoaderOptions(request_info->is_main_frame), | 
|  | request_info->frame_tree_node_id, | 
|  | request_info->devtools_navigation_token, | 
|  | request_info->devtools_frame_token, request_info->report_raw_headers, | 
|  | request_info->begin_params->load_flags, network_loader_factory_, | 
|  | base::BindRepeating( | 
|  | &URLLoaderRequestController::CreateURLLoaderThrottles, | 
|  | base::Unretained(this)))); | 
|  | } | 
|  |  | 
|  | std::vector<std::unique_ptr<URLLoaderRequestInterceptor>> | 
|  | browser_interceptors = GetContentClient() | 
|  | ->browser() | 
|  | ->WillCreateURLLoaderRequestInterceptors( | 
|  | navigation_ui_data_.get(), | 
|  | request_info->frame_tree_node_id); | 
|  | if (!browser_interceptors.empty()) { | 
|  | for (auto& browser_interceptor : browser_interceptors) { | 
|  | interceptors_.push_back( | 
|  | std::make_unique<NavigationLoaderInterceptorBrowserContainer>( | 
|  | std::move(browser_interceptor))); | 
|  | } | 
|  | } | 
|  |  | 
|  | Restart(); | 
|  | } | 
|  |  | 
|  | // This could be called multiple times to follow a chain of redirects. | 
|  | void Restart() { | 
|  | DCHECK(IsLoaderInterceptionEnabled()); | 
|  | // 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, only certain | 
|  | // schemes are handled by the default URL loader. We need to make sure the | 
|  | // redirected URL is a handled scheme, otherwise reset the loader so the | 
|  | // correct non-network service loader can be used. | 
|  | if (!default_loader_used_ || | 
|  | (base::FeatureList::IsEnabled(network::features::kNetworkService) && | 
|  | !IsURLHandledByDefaultLoader(resource_request_->url))) { | 
|  | url_loader_.reset(); | 
|  | } | 
|  | interceptor_index_ = 0; | 
|  | received_response_ = false; | 
|  | MaybeStartLoader(nullptr /* interceptor */, | 
|  | {} /* single_request_handler */); | 
|  | } | 
|  |  | 
|  | // |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, | 
|  | SingleRequestURLLoaderFactory::RequestHandler single_request_handler) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  | DCHECK(IsLoaderInterceptionEnabled()); | 
|  | DCHECK(started_); | 
|  |  | 
|  | if (single_request_handler) { | 
|  | // |interceptor| wants to handle the request with | 
|  | // |single_request_handler|. | 
|  | DCHECK(interceptor); | 
|  |  | 
|  | std::vector<std::unique_ptr<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<MimeSniffingThrottle>()); | 
|  |  | 
|  | default_loader_used_ = false; | 
|  | url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( | 
|  | base::MakeRefCounted<SingleRequestURLLoaderFactory>( | 
|  | std::move(single_request_handler)), | 
|  | 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(); | 
|  |  | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | // 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_, resource_context_, | 
|  | base::BindOnce(&URLLoaderRequestController::MaybeStartLoader, | 
|  | base::Unretained(this), next_interceptor), | 
|  | base::BindOnce( | 
|  | &URLLoaderRequestController::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_modified_request_headers_)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // No interceptors wanted to handle this request. | 
|  | uint32_t options = network::mojom::kURLLoadOptionNone; | 
|  | scoped_refptr<network::SharedURLLoaderFactory> factory = | 
|  | PrepareForNonInterceptedRequest(&options); | 
|  | url_loader_ = 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. | 
|  | void FallbackToNonInterceptedRequest(bool reset_subresource_loader_params) { | 
|  | if (reset_subresource_loader_params) | 
|  | subresource_loader_params_.reset(); | 
|  |  | 
|  | // Non-NetworkService: | 
|  | // Cancel state on ResourceDispatcherHostImpl so it doesn't complain about | 
|  | // reusing the request_id after redirects. Otherwise the following sequence | 
|  | // can happen: | 
|  | // RDHI Start(request_id) -> Redirect -> SW interception -> SW fallback to | 
|  | // network -> RDHI Start(request_id). | 
|  | if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { | 
|  | DCHECK(ResourceDispatcherHostImpl::Get()); | 
|  | ResourceDispatcherHostImpl::Get()->CancelRequest( | 
|  | global_request_id_.child_id, global_request_id_.request_id); | 
|  | } | 
|  |  | 
|  | // |url_loader_| is using the factory for the interceptor that decided to | 
|  | // fallback, so restart it with the non-interceptor factory. | 
|  | DCHECK(url_loader_); | 
|  | uint32_t options = network::mojom::kURLLoadOptionNone; | 
|  | scoped_refptr<network::SharedURLLoaderFactory> factory = | 
|  | PrepareForNonInterceptedRequest(&options); | 
|  | url_loader_->RestartWithFactory(std::move(factory), options); | 
|  | } | 
|  |  | 
|  | scoped_refptr<network::SharedURLLoaderFactory> | 
|  | PrepareForNonInterceptedRequest(uint32_t* out_options) { | 
|  | // If NetworkService is not enabled (which means we come here because one of | 
|  | // the loader interceptors is enabled), use the default request handler | 
|  | // instead of going through the NetworkService path. | 
|  | if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { | 
|  | DCHECK(!interceptors_.empty()); | 
|  | DCHECK(default_request_handler_factory_); | 
|  | // The only way to come here is to enable ServiceWorkerServicification or | 
|  | // SignedExchange without NetworkService. We know that their request | 
|  | // interceptors have already intercepted and decided not to handle the | 
|  | // request. | 
|  | DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled() || | 
|  | signed_exchange_utils::IsSignedExchangeHandlingEnabled()); | 
|  | default_loader_used_ = true; | 
|  | // Update |request_info_| when following a redirect. | 
|  | if (url_chain_.size() > 0) { | 
|  | request_info_ = CreateNavigationRequestInfoForRedirect( | 
|  | *request_info_, *resource_request_); | 
|  | } | 
|  |  | 
|  | // When |subresource_loader_params_| has its value, the request should not | 
|  | // be intercepted by any other interceptors since it means that a request | 
|  | // interceptor already intercepted the request and it attached its info to | 
|  | // the request. | 
|  | bool was_request_intercepted = subresource_loader_params_.has_value(); | 
|  |  | 
|  | // TODO(falken): Determine whether GetURLLoaderOptions() can be called | 
|  | // here like below. It looks like |default_request_handler_factory_| just | 
|  | // calls that. | 
|  | *out_options = network::mojom::kURLLoadOptionNone; | 
|  | return base::MakeRefCounted<SingleRequestURLLoaderFactory>( | 
|  | default_request_handler_factory_.Run(was_request_intercepted)); | 
|  | } | 
|  |  | 
|  | // 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 (!IsURLHandledByDefaultLoader(resource_request_->url)) { | 
|  | if (known_schemes_.find(resource_request_->url.scheme()) == | 
|  | known_schemes_.end()) { | 
|  | bool handled = GetContentClient()->browser()->HandleExternalProtocol( | 
|  | resource_request_->url, web_contents_getter_, | 
|  | ChildProcessHost::kInvalidUniqueID, navigation_ui_data_.get(), | 
|  | resource_request_->resource_type == RESOURCE_TYPE_MAIN_FRAME, | 
|  | static_cast<ui::PageTransition>(resource_request_->transition_type), | 
|  | resource_request_->has_user_gesture); | 
|  | factory = base::MakeRefCounted<SingleRequestURLLoaderFactory>( | 
|  | base::BindOnce(UnknownSchemeCallback, handled)); | 
|  | } else { | 
|  | network::mojom::URLLoaderFactoryPtr& non_network_factory = | 
|  | non_network_url_loader_factories_[resource_request_->url.scheme()]; | 
|  | if (!non_network_factory.is_bound()) { | 
|  | base::PostTaskWithTraits( | 
|  | FROM_HERE, {BrowserThread::UI}, | 
|  | base::BindOnce(&NavigationURLLoaderImpl :: | 
|  | BindNonNetworkURLLoaderFactoryRequest, | 
|  | owner_, frame_tree_node_id_, | 
|  | resource_request_->url, | 
|  | mojo::MakeRequest(&non_network_factory))); | 
|  | } | 
|  | factory = | 
|  | base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( | 
|  | non_network_factory.get()); | 
|  | } | 
|  | } 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. We explicitly avoid | 
|  | // proxying the data URL case here. | 
|  | if (proxied_factory_request_.is_pending() && | 
|  | !resource_request_->url.SchemeIs(url::kDataScheme)) { | 
|  | DCHECK(proxied_factory_info_.is_valid()); | 
|  | // We don't worry about reconnection since it's a single navigation. | 
|  | network_loader_factory_->Clone(std::move(proxied_factory_request_)); | 
|  | factory = base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( | 
|  | std::move(proxied_factory_info_)); | 
|  | } else { | 
|  | factory = network_loader_factory_; | 
|  | } | 
|  | } | 
|  | url_chain_.push_back(resource_request_->url); | 
|  | *out_options = GetURLLoaderOptions(resource_request_->resource_type == | 
|  | RESOURCE_TYPE_MAIN_FRAME); | 
|  | return factory; | 
|  | } | 
|  |  | 
|  | void FollowRedirect( | 
|  | const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  | DCHECK(!redirect_info_.new_url.is_empty()); | 
|  |  | 
|  | if (!IsLoaderInterceptionEnabled()) { | 
|  | url_loader_->FollowRedirect(modified_request_headers); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // 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_, | 
|  | modified_request_headers, &resource_request_->headers, | 
|  | &should_clear_upload); | 
|  | if (should_clear_upload) { | 
|  | // The request body is no longer applicable. | 
|  | resource_request_->request_body = nullptr; | 
|  | blob_handles_.clear(); | 
|  | } | 
|  |  | 
|  | resource_request_->url = redirect_info_.new_url; | 
|  | resource_request_->method = redirect_info_.new_method; | 
|  | resource_request_->site_for_cookies = redirect_info_.new_site_for_cookies; | 
|  | resource_request_->referrer = GURL(redirect_info_.new_referrer); | 
|  | resource_request_->referrer_policy = redirect_info_.new_referrer_policy; | 
|  | 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_modified_request_headers_ = modified_request_headers; | 
|  |  | 
|  | if (signed_exchange_utils::NeedToCheckRedirectedURLForAcceptHeader()) { | 
|  | // Currently we send the SignedExchange accept header only for the limited | 
|  | // origins when SignedHTTPExchangeOriginTrial feature is enabled without | 
|  | // SignedHTTPExchange feature. We need to put the SignedExchange accept | 
|  | // header on when redirecting to the origins in the OriginList of | 
|  | // SignedHTTPExchangeAcceptHeader field trial, and need to remove it when | 
|  | // redirecting to out of the OriginList. | 
|  | if (!url_loader_modified_request_headers_) | 
|  | url_loader_modified_request_headers_ = net::HttpRequestHeaders(); | 
|  | std::string accept_value = network::kFrameAcceptHeader; | 
|  | if (signed_exchange_utils::ShouldAdvertiseAcceptHeader( | 
|  | url::Origin::Create(resource_request_->url))) { | 
|  | DCHECK(!accept_value.empty()); | 
|  | accept_value.append(kAcceptHeaderSignedExchangeSuffix); | 
|  | } | 
|  | url_loader_modified_request_headers_->SetHeader(network::kAcceptHeader, | 
|  | accept_value); | 
|  | resource_request_->headers.SetHeader(network::kAcceptHeader, | 
|  | accept_value); | 
|  | } | 
|  |  | 
|  | Restart(); | 
|  | } | 
|  |  | 
|  | base::Optional<SubresourceLoaderParams> TakeSubresourceLoaderParams() { | 
|  | return std::move(subresource_loader_params_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // network::mojom::URLLoaderClient implementation: | 
|  | void OnReceiveResponse(const network::ResourceResponseHead& head) override { | 
|  | // Record the SCT histogram before checking if anything wants to intercept | 
|  | // the response, so interceptors like AppCache and extensions can't hide | 
|  | // values from the histograms. | 
|  | RecordSCTHistogramIfNeeded(head.ssl_info); | 
|  |  | 
|  | received_response_ = true; | 
|  |  | 
|  | // 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; | 
|  |  | 
|  | network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints; | 
|  |  | 
|  | // Currently only plugin handlers may intercept the response. Don't treat | 
|  | // the response as download if it has been handled by plugins. | 
|  | bool response_intercepted = false; | 
|  | if (url_loader_) { | 
|  | url_loader_client_endpoints = url_loader_->Unbind(); | 
|  | response_intercepted = url_loader_->response_intercepted(); | 
|  | } else { | 
|  | url_loader_client_endpoints = | 
|  | network::mojom::URLLoaderClientEndpoints::New( | 
|  | response_url_loader_.PassInterface(), | 
|  | response_loader_binding_.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) { | 
|  | OnComplete(network::URLLoaderCompletionStatus(net::ERR_ABORTED)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool is_download; | 
|  | bool is_stream; | 
|  | // If there is not an explicit PreviewsState set on the request, turn | 
|  | // Previews off. | 
|  | PreviewsState previews_state = PREVIEWS_OFF; | 
|  | std::unique_ptr<NavigationData> cloned_navigation_data; | 
|  |  | 
|  | if (IsLoaderInterceptionEnabled()) { | 
|  | bool must_download = download_utils::MustDownload( | 
|  | url_, head.headers.get(), head.mime_type); | 
|  | bool known_mime_type = blink::IsSupportedMimeType(head.mime_type); | 
|  |  | 
|  | bool is_download_if_not_handled_by_plugin = | 
|  | !response_intercepted && (must_download || !known_mime_type); | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_PLUGINS) | 
|  | if (!response_intercepted && !must_download && !known_mime_type) { | 
|  | CheckPluginAndContinueOnReceiveResponse( | 
|  | head, std::move(url_loader_client_endpoints), | 
|  | is_download_if_not_handled_by_plugin, std::vector<WebPluginInfo>()); | 
|  | return; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | is_download = is_download_if_not_handled_by_plugin; | 
|  | is_stream = false; | 
|  |  | 
|  | // If NetworkService is on, or an interceptor handled the request, the | 
|  | // request doesn't use ResourceDispatcherHost so | 
|  | // CallOnReceivedResponse and return here. | 
|  | if (base::FeatureList::IsEnabled(network::features::kNetworkService) || | 
|  | !default_loader_used_) { | 
|  | CallOnReceivedResponse(head, std::move(url_loader_client_endpoints), | 
|  | std::move(cloned_navigation_data), is_download, | 
|  | is_stream, previews_state); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // NetworkService is off and an interceptor didn't handle the request, | 
|  | // so it went to ResourceDispatcherHost. | 
|  | ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get(); | 
|  | net::URLRequest* url_request = rdh->GetURLRequest(global_request_id_); | 
|  |  | 
|  | // The |url_request| maybe have been removed from the resource dispatcher | 
|  | // host during the time it took for OnReceiveResponse() to be received. | 
|  | if (url_request) { | 
|  | ResourceRequestInfoImpl* info = | 
|  | ResourceRequestInfoImpl::ForRequest(url_request); | 
|  | is_download = !response_intercepted && info->IsDownload(); | 
|  | is_stream = info->is_stream(); | 
|  | previews_state = info->GetPreviewsState(); | 
|  | if (rdh->delegate()) { | 
|  | NavigationData* navigation_data = | 
|  | rdh->delegate()->GetNavigationData(url_request); | 
|  |  | 
|  | // Clone the embedder's NavigationData before moving it to the UI | 
|  | // thread. | 
|  | if (navigation_data) | 
|  | cloned_navigation_data = navigation_data->Clone(); | 
|  | } | 
|  |  | 
|  | // non-S13nServiceWorker: | 
|  | // This is similar to what is done in | 
|  | // ServiceWorkerControlleeHandler::MaybeCreateSubresourceLoaderParams() | 
|  | // (which is used when S13nServiceWorker is on). It takes the matching | 
|  | // ControllerServiceWorkerInfo (if any) associated with the request. It | 
|  | // will be sent to the renderer process and used to intercept requests. | 
|  | ServiceWorkerProviderHost* sw_provider_host = | 
|  | ServiceWorkerRequestHandler::GetProviderHost(url_request); | 
|  | if (sw_provider_host && sw_provider_host->controller()) { | 
|  | DCHECK(!blink::ServiceWorkerUtils::IsServicificationEnabled()); | 
|  | subresource_loader_params_ = SubresourceLoaderParams(); | 
|  | subresource_loader_params_->controller_service_worker_info = | 
|  | mojom::ControllerServiceWorkerInfo::New(); | 
|  | subresource_loader_params_->controller_service_worker_info->mode = | 
|  | sw_provider_host->GetControllerMode(); | 
|  | base::WeakPtr<ServiceWorkerObjectHost> sw_object_host = | 
|  | sw_provider_host->GetOrCreateServiceWorkerObjectHost( | 
|  | sw_provider_host->controller()); | 
|  | if (sw_object_host) { | 
|  | subresource_loader_params_->controller_service_worker_object_host = | 
|  | sw_object_host; | 
|  | subresource_loader_params_->controller_service_worker_info | 
|  | ->object_info = sw_object_host->CreateIncompleteObjectInfo(); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | is_download = is_stream = false; | 
|  | } | 
|  |  | 
|  | CallOnReceivedResponse(head, std::move(url_loader_client_endpoints), | 
|  | std::move(cloned_navigation_data), is_download, | 
|  | is_stream, previews_state); | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_PLUGINS) | 
|  | void CheckPluginAndContinueOnReceiveResponse( | 
|  | const network::ResourceResponseHead& 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; | 
|  | // It's ok to pass -1 for the render process and frame ID since that's | 
|  | // only used for plugin overridding. We don't actually care if we get an | 
|  | // overridden plugin or not, since all we care about is the presence of a | 
|  | // plugin. Note that this is what the MimeSniffingResourceHandler code | 
|  | // path does as well for navigations. | 
|  | bool has_plugin = PluginService::GetInstance()->GetPluginInfo( | 
|  | -1 /* render_process_id */, -1 /* render_frame_id */, resource_context_, | 
|  | 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(), 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(head, std::move(url_loader_client_endpoints), | 
|  | nullptr, is_download, false /* is_stream */, | 
|  | PREVIEWS_OFF /* previews_state */); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void CallOnReceivedResponse( | 
|  | const network::ResourceResponseHead& head, | 
|  | network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, | 
|  | std::unique_ptr<NavigationData> cloned_navigation_data, | 
|  | bool is_download, | 
|  | bool is_stream, | 
|  | PreviewsState previews_state) { | 
|  | scoped_refptr<network::ResourceResponse> response( | 
|  | new network::ResourceResponse()); | 
|  | response->head = head; | 
|  |  | 
|  | // Make a copy of the ResourceResponse before it is passed to another | 
|  | // thread. | 
|  | // | 
|  | // TODO(davidben): This copy could be avoided if ResourceResponse weren't | 
|  | // reference counted and the loader stack passed unique ownership of the | 
|  | // response. https://crbug.com/416050 | 
|  | base::PostTaskWithTraits( | 
|  | FROM_HERE, {BrowserThread::UI}, | 
|  | base::BindOnce(&NavigationURLLoaderImpl::OnReceiveResponse, owner_, | 
|  | response->DeepCopy(), | 
|  | std::move(url_loader_client_endpoints), | 
|  | std::move(cloned_navigation_data), global_request_id_, | 
|  | is_download, is_stream, previews_state)); | 
|  | } | 
|  |  | 
|  | void OnReceiveRedirect(const net::RedirectInfo& redirect_info, | 
|  | const network::ResourceResponseHead& head) override { | 
|  | if (base::FeatureList::IsEnabled(network::features::kNetworkService) && | 
|  | !bypass_redirect_checks_ && | 
|  | !IsRedirectSafe(url_, redirect_info.new_url, resource_context_)) { | 
|  | OnComplete(network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (--redirect_limit_ == 0) { | 
|  | OnComplete( | 
|  | network::URLLoaderCompletionStatus(net::ERR_TOO_MANY_REDIRECTS)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // 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; | 
|  |  | 
|  | scoped_refptr<network::ResourceResponse> response( | 
|  | new network::ResourceResponse()); | 
|  | response->head = head; | 
|  | url_ = redirect_info.new_url; | 
|  |  | 
|  | // Make a copy of the ResourceResponse before it is passed to another | 
|  | // thread. | 
|  | // | 
|  | // TODO(davidben): This copy could be avoided if ResourceResponse weren't | 
|  | // reference counted and the loader stack passed unique ownership of the | 
|  | // response. https://crbug.com/416050 | 
|  | base::PostTaskWithTraits( | 
|  | FROM_HERE, {BrowserThread::UI}, | 
|  | base::BindOnce(&NavigationURLLoaderImpl::OnReceiveRedirect, owner_, | 
|  | redirect_info, response->DeepCopy())); | 
|  | } | 
|  |  | 
|  | void OnUploadProgress(int64_t current_position, | 
|  | int64_t total_size, | 
|  | OnUploadProgressCallback callback) override {} | 
|  | void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override {} | 
|  | void OnTransferSizeUpdated(int32_t transfer_size_diff) override {} | 
|  |  | 
|  | void OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle) override { | 
|  | // Not reached. At this point, the loader and client endpoints must have | 
|  | // been unbound and forwarded to the renderer. | 
|  |  | 
|  | // TODO(crbug.com/882661): Remove these aliases when the linked bug is | 
|  | // fixed. | 
|  | bool received_response = received_response_; | 
|  | base::debug::Alias(&received_response); | 
|  | bool default_loader_used = default_loader_used_; | 
|  | base::debug::Alias(&default_loader_used); | 
|  | DEBUG_ALIAS_FOR_GURL(url, url_); | 
|  | size_t chain_size = url_chain_.size(); | 
|  | base::debug::Alias(&chain_size); | 
|  | GURL url0; | 
|  | GURL url1; | 
|  | GURL url2; | 
|  | GURL url3; | 
|  | GURL url4; | 
|  | GURL url5; | 
|  | if (url_chain_.size() > 0) | 
|  | url0 = url_chain_[0]; | 
|  | if (url_chain_.size() > 1) | 
|  | url1 = url_chain_[1]; | 
|  | if (url_chain_.size() > 2) | 
|  | url2 = url_chain_[2]; | 
|  | if (url_chain_.size() > 3) | 
|  | url3 = url_chain_[3]; | 
|  | if (url_chain_.size() > 4) | 
|  | url4 = url_chain_[4]; | 
|  | if (url_chain_.size() > 5) | 
|  | url5 = url_chain_[5]; | 
|  | DEBUG_ALIAS_FOR_GURL(url0_buf, url0); | 
|  | DEBUG_ALIAS_FOR_GURL(url1_buf, url1); | 
|  | DEBUG_ALIAS_FOR_GURL(url2_buf, url2); | 
|  | DEBUG_ALIAS_FOR_GURL(url3_buf, url3); | 
|  | DEBUG_ALIAS_FOR_GURL(url4_buf, url4); | 
|  | DEBUG_ALIAS_FOR_GURL(url5_buf, url5); | 
|  | CHECK(false); | 
|  | } | 
|  |  | 
|  | void OnComplete(const network::URLLoaderCompletionStatus& status) override { | 
|  | RecordSCTHistogramIfNeeded(status.ssl_info); | 
|  |  | 
|  | UMA_HISTOGRAM_BOOLEAN( | 
|  | "Navigation.URLLoaderNetworkService.OnCompleteHasSSLInfo", | 
|  | status.ssl_info.has_value()); | 
|  | if (status.ssl_info.has_value()) { | 
|  | UMA_HISTOGRAM_MEMORY_KB( | 
|  | "Navigation.URLLoaderNetworkService.OnCompleteCertificateChainsSize", | 
|  | GetCertificateChainsSizeInKB(status.ssl_info.value())); | 
|  | } | 
|  |  | 
|  | if (status.error_code != net::OK && !received_response_) { | 
|  | // 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(network::ResourceResponseHead())) | 
|  | return; | 
|  | } | 
|  | status_ = status; | 
|  |  | 
|  | base::PostTaskWithTraits( | 
|  | FROM_HERE, {BrowserThread::UI}, | 
|  | base::BindOnce(&NavigationURLLoaderImpl::OnComplete, owner_, 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( | 
|  | const network::ResourceResponseHead& response) { | 
|  | if (!IsLoaderInterceptionEnabled()) | 
|  | return false; | 
|  |  | 
|  | if (!default_loader_used_) | 
|  | return false; | 
|  |  | 
|  | for (auto& interceptor : interceptors_) { | 
|  | network::mojom::URLLoaderClientRequest response_client_request; | 
|  | if (interceptor->MaybeCreateLoaderForResponse( | 
|  | response, &response_url_loader_, &response_client_request, | 
|  | url_loader_.get())) { | 
|  | if (response_loader_binding_.is_bound()) | 
|  | response_loader_binding_.Close(); | 
|  | response_loader_binding_.Bind(std::move(response_client_request)); | 
|  | default_loader_used_ = false; | 
|  | url_loader_.reset(); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::vector<std::unique_ptr<URLLoaderThrottle>> CreateURLLoaderThrottles() { | 
|  | return GetContentClient()->browser()->CreateURLLoaderThrottles( | 
|  | *resource_request_, resource_context_, web_contents_getter_, | 
|  | navigation_ui_data_.get(), frame_tree_node_id_); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<NavigationLoaderInterceptor> CreateServiceWorkerInterceptor( | 
|  | const NavigationRequestInfo& request_info, | 
|  | ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core) | 
|  | const { | 
|  | const ResourceType resource_type = request_info.is_main_frame | 
|  | ? RESOURCE_TYPE_MAIN_FRAME | 
|  | : RESOURCE_TYPE_SUB_FRAME; | 
|  | network::mojom::RequestContextFrameType frame_type = | 
|  | request_info.is_main_frame | 
|  | ? network::mojom::RequestContextFrameType::kTopLevel | 
|  | : network::mojom::RequestContextFrameType::kNested; | 
|  | storage::BlobStorageContext* blob_storage_context = GetBlobStorageContext( | 
|  | GetChromeBlobStorageContextForResourceContext(resource_context_)); | 
|  | return ServiceWorkerRequestHandler::InitializeForNavigationNetworkService( | 
|  | resource_request_->url, resource_context_, | 
|  | service_worker_navigation_handle_core, blob_storage_context, | 
|  | request_info.begin_params->skip_service_worker, resource_type, | 
|  | request_info.begin_params->request_context_type, frame_type, | 
|  | request_info.are_ancestors_secure, request_info.common_params.post_data, | 
|  | web_contents_getter_); | 
|  | } | 
|  |  | 
|  | void RecordSCTHistogramIfNeeded( | 
|  | const base::Optional<net::SSLInfo>& ssl_info) { | 
|  | if (is_main_frame_ && url_.SchemeIsCryptographic() && | 
|  | ssl_info.has_value()) { | 
|  | int num_valid_scts = 0; | 
|  | for (const auto& signed_certificate_timestamps : | 
|  | ssl_info->signed_certificate_timestamps) { | 
|  | if (signed_certificate_timestamps.status == net::ct::SCT_STATUS_OK) | 
|  | ++num_valid_scts; | 
|  | } | 
|  | UMA_HISTOGRAM_COUNTS_100( | 
|  | "Net.CertificateTransparency.MainFrameValidSCTCount", num_valid_scts); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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; | 
|  | ResourceContext* resource_context_; | 
|  | base::Callback<WebContents*()> web_contents_getter_; | 
|  | std::unique_ptr<NavigationUIData> navigation_ui_data_; | 
|  | scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory_; | 
|  |  | 
|  | std::unique_ptr<ThrottlingURLLoader> url_loader_; | 
|  |  | 
|  | // Caches the modified request headers provided by clients during redirect, | 
|  | // will be consumed by next |url_loader_->FollowRedirect()|. | 
|  | base::Optional<net::HttpRequestHeaders> url_loader_modified_request_headers_; | 
|  |  | 
|  | BlobHandles blob_handles_; | 
|  | std::vector<GURL> url_chain_; | 
|  |  | 
|  | // Current URL that is being navigated, updated after redirection. | 
|  | GURL url_; | 
|  |  | 
|  | const bool is_main_frame_; | 
|  |  | 
|  | // 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 binding for loaders created for responses received from the | 
|  | // network loader. | 
|  | mojo::Binding<network::mojom::URLLoaderClient> response_loader_binding_; | 
|  |  | 
|  | // URLLoader instance for response loaders, i.e loaders created for handing | 
|  | // responses received from the network URLLoader. | 
|  | network::mojom::URLLoaderPtr response_url_loader_; | 
|  |  | 
|  | // Set to true if we receive a valid response from a URLLoader, i.e. | 
|  | // URLLoaderClient::OnReceivedResponse() is called. | 
|  | bool received_response_ = false; | 
|  |  | 
|  | bool started_ = false; | 
|  |  | 
|  | // Lazily initialized and used in the case of non-network resource | 
|  | // navigations. Keyed by URL scheme. | 
|  | std::map<std::string, network::mojom::URLLoaderFactoryPtr> | 
|  | 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 but IsLoaderInterceptionEnabled() is true. | 
|  | // 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 request, in which case these | 
|  | // fields will contain input (info) and output (request) endpoints for the | 
|  | // proxy. If this controller is handling a request for which proxying is | 
|  | // supported, requests will be plumbed through these endpoints. | 
|  | // | 
|  | // Note that these are only used for requests that go to the Network Service. | 
|  | network::mojom::URLLoaderFactoryRequest proxied_factory_request_; | 
|  | network::mojom::URLLoaderFactoryPtrInfo proxied_factory_info_; | 
|  |  | 
|  | // The schemes that this loader can use. For anything else we'll try external | 
|  | // protocol handlers. | 
|  | std::set<std::string> known_schemes_; | 
|  |  | 
|  | // If true, redirect checks will be handled in a proxy, and not here. | 
|  | bool bypass_redirect_checks_; | 
|  |  | 
|  | mutable base::WeakPtrFactory<URLLoaderRequestController> weak_factory_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(URLLoaderRequestController); | 
|  | }; | 
|  |  | 
|  | // TODO(https://crbug.com/790734): pass |navigation_ui_data| along with the | 
|  | // request so that it could be modified. | 
|  | NavigationURLLoaderImpl::NavigationURLLoaderImpl( | 
|  | ResourceContext* resource_context, | 
|  | StoragePartition* storage_partition, | 
|  | std::unique_ptr<NavigationRequestInfo> request_info, | 
|  | std::unique_ptr<NavigationUIData> navigation_ui_data, | 
|  | ServiceWorkerNavigationHandle* service_worker_navigation_handle, | 
|  | AppCacheNavigationHandle* appcache_handle, | 
|  | NavigationURLLoaderDelegate* delegate, | 
|  | std::vector<std::unique_ptr<NavigationLoaderInterceptor>> | 
|  | initial_interceptors) | 
|  | : delegate_(delegate), | 
|  | allow_download_(request_info->common_params.allow_download), | 
|  | weak_factory_(this) { | 
|  | 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); | 
|  |  | 
|  | ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core = | 
|  | service_worker_navigation_handle | 
|  | ? service_worker_navigation_handle->core() | 
|  | : nullptr; | 
|  |  | 
|  | AppCacheNavigationHandleCore* appcache_handle_core = | 
|  | appcache_handle ? appcache_handle->core() : nullptr; | 
|  |  | 
|  | std::unique_ptr<network::ResourceRequest> new_request = CreateResourceRequest( | 
|  | request_info.get(), frame_tree_node_id, allow_download_); | 
|  | new_request->transition_type = request_info->common_params.transition; | 
|  |  | 
|  | if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { | 
|  | DCHECK(!request_controller_); | 
|  | request_controller_ = std::make_unique<URLLoaderRequestController>( | 
|  | /* initial_interceptors = */ | 
|  | std::vector<std::unique_ptr<NavigationLoaderInterceptor>>(), | 
|  | std::move(new_request), resource_context, | 
|  | request_info->common_params.url, request_info->is_main_frame, | 
|  | /* proxied_url_loader_factory_request */ nullptr, | 
|  | /* proxied_url_loader_factory_info */ nullptr, std::set<std::string>(), | 
|  | /* bypass_redirect_checks */ false, weak_factory_.GetWeakPtr()); | 
|  |  | 
|  | base::PostTaskWithTraits( | 
|  | FROM_HERE, {BrowserThread::IO}, | 
|  | base::BindOnce( | 
|  | &URLLoaderRequestController::StartWithoutNetworkService, | 
|  | base::Unretained(request_controller_.get()), | 
|  | base::RetainedRef(storage_partition->GetURLRequestContext()), | 
|  | base::Unretained(storage_partition->GetFileSystemContext()), | 
|  | base::Unretained(service_worker_navigation_handle_core), | 
|  | base::Unretained(appcache_handle_core), std::move(request_info), | 
|  | std::move(navigation_ui_data))); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check if a web UI scheme wants to handle this request. | 
|  | FrameTreeNode* frame_tree_node = | 
|  | FrameTreeNode::GloballyFindByID(frame_tree_node_id); | 
|  | network::mojom::URLLoaderFactoryPtrInfo factory_for_webui; | 
|  | const auto& schemes = URLDataManagerBackend::GetWebUISchemes(); | 
|  | std::string scheme = new_request->url.scheme(); | 
|  | if (base::ContainsValue(schemes, scheme)) { | 
|  | factory_for_webui = CreateWebUIURLLoaderBinding( | 
|  | frame_tree_node->current_frame_host(), scheme) | 
|  | .PassInterface(); | 
|  | } | 
|  |  | 
|  | network::mojom::URLLoaderFactoryPtrInfo proxied_factory_info; | 
|  | network::mojom::URLLoaderFactoryRequest proxied_factory_request; | 
|  | bool bypass_redirect_checks = false; | 
|  | auto* partition = static_cast<StoragePartitionImpl*>(storage_partition); | 
|  | if (frame_tree_node) { | 
|  | // |frame_tree_node| may be null in some unit test environments. | 
|  | GetContentClient() | 
|  | ->browser() | 
|  | ->RegisterNonNetworkNavigationURLLoaderFactories( | 
|  | frame_tree_node_id, &non_network_url_loader_factories_); | 
|  |  | 
|  | // Navigation requests are not associated with any particular | 
|  | // |network::ResourceRequest::request_initiator| origin - using an opaque | 
|  | // origin instead. | 
|  | url::Origin navigation_request_initiator = url::Origin(); | 
|  | // The embedder may want to proxy all network-bound URLLoaderFactory | 
|  | // requests 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. | 
|  | network::mojom::URLLoaderFactoryPtrInfo factory_info; | 
|  | auto factory_request = mojo::MakeRequest(&factory_info); | 
|  | bool use_proxy = GetContentClient()->browser()->WillCreateURLLoaderFactory( | 
|  | partition->browser_context(), frame_tree_node->current_frame_host(), | 
|  | true /* is_navigation */, navigation_request_initiator, | 
|  | &factory_request, &bypass_redirect_checks); | 
|  | if (RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( | 
|  | frame_tree_node->current_frame_host(), true, false, | 
|  | &factory_request)) { | 
|  | use_proxy = true; | 
|  | } | 
|  | if (use_proxy) { | 
|  | proxied_factory_request = std::move(factory_request); | 
|  | proxied_factory_info = std::move(factory_info); | 
|  | } | 
|  |  | 
|  | const std::string storage_domain; | 
|  | non_network_url_loader_factories_[url::kFileSystemScheme] = | 
|  | CreateFileSystemURLLoaderFactory(frame_tree_node->current_frame_host(), | 
|  | /*is_navigation=*/true, | 
|  | partition->GetFileSystemContext(), | 
|  | storage_domain); | 
|  | } | 
|  |  | 
|  | non_network_url_loader_factories_[url::kFileScheme] = | 
|  | std::make_unique<FileURLLoaderFactory>( | 
|  | partition->browser_context()->GetPath(), | 
|  | base::CreateSequencedTaskRunnerWithTraits( | 
|  | {base::MayBlock(), base::TaskPriority::BEST_EFFORT, | 
|  | base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); | 
|  |  | 
|  | #if defined(OS_ANDROID) | 
|  | non_network_url_loader_factories_[url::kContentScheme] = | 
|  | std::make_unique<ContentURLLoaderFactory>( | 
|  | base::CreateSequencedTaskRunnerWithTraits( | 
|  | {base::MayBlock(), base::TaskPriority::BEST_EFFORT, | 
|  | 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); | 
|  |  | 
|  | DCHECK(!request_controller_); | 
|  | request_controller_ = std::make_unique<URLLoaderRequestController>( | 
|  | std::move(initial_interceptors), std::move(new_request), resource_context, | 
|  | request_info->common_params.url, request_info->is_main_frame, | 
|  | std::move(proxied_factory_request), std::move(proxied_factory_info), | 
|  | std::move(known_schemes), bypass_redirect_checks, | 
|  | weak_factory_.GetWeakPtr()); | 
|  | base::PostTaskWithTraits( | 
|  | FROM_HERE, {BrowserThread::IO}, | 
|  | base::BindOnce( | 
|  | &URLLoaderRequestController::Start, | 
|  | base::Unretained(request_controller_.get()), | 
|  | partition->url_loader_factory_getter()->GetNetworkFactoryInfo(), | 
|  | service_worker_navigation_handle_core, appcache_handle_core, | 
|  | std::move(request_info), std::move(navigation_ui_data), | 
|  | std::move(factory_for_webui), frame_tree_node_id, | 
|  | ServiceManagerConnection::GetForProcess()->GetConnector()->Clone())); | 
|  | } | 
|  |  | 
|  | NavigationURLLoaderImpl::~NavigationURLLoaderImpl() { | 
|  | BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, | 
|  | request_controller_.release()); | 
|  | } | 
|  |  | 
|  | void NavigationURLLoaderImpl::FollowRedirect( | 
|  | const base::Optional<std::vector<std::string>>& | 
|  | to_be_removed_request_headers, | 
|  | const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { | 
|  | base::PostTaskWithTraits( | 
|  | FROM_HERE, {BrowserThread::IO}, | 
|  | base::BindOnce(&URLLoaderRequestController::FollowRedirect, | 
|  | base::Unretained(request_controller_.get()), | 
|  | modified_request_headers)); | 
|  | } | 
|  |  | 
|  | void NavigationURLLoaderImpl::ProceedWithResponse() {} | 
|  |  | 
|  | void NavigationURLLoaderImpl::OnReceiveResponse( | 
|  | scoped_refptr<network::ResourceResponse> response, | 
|  | network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, | 
|  | std::unique_ptr<NavigationData> navigation_data, | 
|  | const GlobalRequestID& global_request_id, | 
|  | bool is_download, | 
|  | bool is_stream, | 
|  | PreviewsState previews_state) { | 
|  | TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this, | 
|  | "&NavigationURLLoaderImpl", this, "success", true); | 
|  |  | 
|  | // TODO(scottmg): This needs to do more of what | 
|  | // NavigationResourceHandler::OnResponseStarted() does. | 
|  |  | 
|  | delegate_->OnResponseStarted( | 
|  | std::move(response), std::move(url_loader_client_endpoints), | 
|  | std::move(navigation_data), global_request_id, | 
|  | allow_download_ && is_download, is_stream, previews_state, | 
|  | request_controller_->TakeSubresourceLoaderParams()); | 
|  | } | 
|  |  | 
|  | void NavigationURLLoaderImpl::OnReceiveRedirect( | 
|  | const net::RedirectInfo& redirect_info, | 
|  | scoped_refptr<network::ResourceResponse> response) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | delegate_->OnRequestRedirected(redirect_info, std::move(response)); | 
|  | } | 
|  |  | 
|  | void NavigationURLLoaderImpl::OnComplete( | 
|  | const network::URLLoaderCompletionStatus& status) { | 
|  | if (status.error_code == net::OK) | 
|  | return; | 
|  |  | 
|  | TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this, | 
|  | "&NavigationURLLoaderImpl", this, "success", false); | 
|  |  | 
|  | delegate_->OnRequestFailed(status); | 
|  | } | 
|  |  | 
|  | void NavigationURLLoaderImpl::SetBeginNavigationInterceptorForTesting( | 
|  | const BeginNavigationInterceptor& interceptor) { | 
|  | DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::IO) || | 
|  | BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
|  | DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); | 
|  | g_interceptor.Get() = interceptor; | 
|  | } | 
|  |  | 
|  | void NavigationURLLoaderImpl::OnRequestStarted(base::TimeTicks timestamp) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | delegate_->OnRequestStarted(timestamp); | 
|  | } | 
|  |  | 
|  | void NavigationURLLoaderImpl::BindNonNetworkURLLoaderFactoryRequest( | 
|  | int frame_tree_node_id, | 
|  | const GURL& url, | 
|  | network::mojom::URLLoaderFactoryRequest factory) { | 
|  | auto it = non_network_url_loader_factories_.find(url.scheme()); | 
|  | if (it == non_network_url_loader_factories_.end()) { | 
|  | DVLOG(1) << "Ignoring request with unknown scheme: " << url.spec(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Navigation requests are not associated with any particular | 
|  | // |network::ResourceRequest::request_initiator| origin - using an opaque | 
|  | // origin instead. | 
|  | url::Origin navigation_request_initiator = url::Origin(); | 
|  |  | 
|  | FrameTreeNode* frame_tree_node = | 
|  | FrameTreeNode::GloballyFindByID(frame_tree_node_id); | 
|  | auto* frame = frame_tree_node->current_frame_host(); | 
|  | GetContentClient()->browser()->WillCreateURLLoaderFactory( | 
|  | frame->GetSiteInstance()->GetBrowserContext(), frame, | 
|  | true /* is_navigation */, navigation_request_initiator, &factory, | 
|  | nullptr /* bypass_redirect_checks */); | 
|  | it->second->Clone(std::move(factory)); | 
|  | } | 
|  |  | 
|  | }  // namespace content |