| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_IMPL_H_ |
| #define CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_IMPL_H_ |
| |
| #include <optional> |
| |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/time/time.h" |
| #include "content/browser/loader/navigation_url_loader.h" |
| #include "content/browser/loader/response_head_update_params.h" |
| #include "content/browser/navigation_subresource_loader_params.h" |
| #include "content/common/content_export.h" |
| #include "content/public/browser/frame_tree_node_id.h" |
| #include "content/public/browser/global_request_id.h" |
| #include "content/public/browser/ssl_status.h" |
| #include "content/public/browser/weak_document_ptr.h" |
| #include "content/public/common/buildflags.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "net/base/load_timing_info.h" |
| #include "net/cookies/cookie_setting_override.h" |
| #include "net/url_request/url_request.h" |
| #include "services/metrics/public/cpp/ukm_source_id.h" |
| #include "services/network/public/cpp/record_ontransfersizeupdate_utils.h" |
| #include "services/network/public/cpp/single_request_url_loader_factory.h" |
| #include "services/network/public/mojom/accept_ch_frame_observer.mojom.h" |
| #include "services/network/public/mojom/device_bound_sessions.mojom.h" |
| #include "services/network/public/mojom/network_context.mojom-forward.h" |
| #include "services/network/public/mojom/service_worker_router_info.mojom-forward.h" |
| #include "services/network/public/mojom/shared_dictionary_access_observer.mojom.h" |
| #include "services/network/public/mojom/url_loader.mojom.h" |
| #include "services/network/public/mojom/url_loader_factory.mojom.h" |
| #include "services/network/public/mojom/url_response_head.mojom.h" |
| #include "third_party/blink/public/common/navigation/navigation_policy.h" |
| #include "third_party/blink/public/common/tokens/tokens.h" |
| #include "third_party/blink/public/mojom/loader/referrer.mojom-forward.h" |
| |
| namespace blink { |
| class URLLoaderThrottle; |
| } |
| |
| namespace net { |
| struct RedirectInfo; |
| } |
| |
| namespace network { |
| class URLLoaderFactoryBuilder; |
| } // namespace network |
| |
| namespace content { |
| |
| class BrowserContext; |
| class FrameTreeNode; |
| class NavigationEarlyHintsManager; |
| class NavigationLoaderInterceptor; |
| class PrefetchedSignedExchangeCache; |
| class SignedExchangeRequestHandler; |
| class StoragePartition; |
| class StoragePartitionImpl; |
| class WebContents; |
| struct WebPluginInfo; |
| |
| class CONTENT_EXPORT NavigationURLLoaderImpl |
| : public NavigationURLLoader, |
| public network::mojom::URLLoaderClient, |
| public network::mojom::AcceptCHFrameObserver { |
| public: |
| // The caller is responsible for ensuring that `delegate` outlives the loader. |
| NavigationURLLoaderImpl( |
| BrowserContext* browser_context, |
| StoragePartition* storage_partition, |
| std::unique_ptr<NavigationRequestInfo> request_info, |
| std::unique_ptr<NavigationUIData> navigation_ui_data, |
| ServiceWorkerMainResourceHandle* service_worker_handle, |
| scoped_refptr<PrefetchedSignedExchangeCache> |
| prefetched_signed_exchange_cache, |
| NavigationURLLoaderDelegate* delegate, |
| mojo::PendingRemote<network::mojom::CookieAccessObserver> cookie_observer, |
| mojo::PendingRemote<network::mojom::TrustTokenAccessObserver> |
| trust_token_observer, |
| mojo::PendingRemote<network::mojom::SharedDictionaryAccessObserver> |
| shared_dictionary_observer, |
| mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> |
| url_loader_network_observer, |
| mojo::PendingRemote<network::mojom::DevToolsObserver> devtools_observer, |
| mojo::PendingRemote<network::mojom::DeviceBoundSessionAccessObserver> |
| device_bound_session_observer, |
| std::vector<std::unique_ptr<NavigationLoaderInterceptor>> |
| initial_interceptors); |
| |
| NavigationURLLoaderImpl(const NavigationURLLoaderImpl&) = delete; |
| NavigationURLLoaderImpl& operator=(const NavigationURLLoaderImpl&) = delete; |
| |
| ~NavigationURLLoaderImpl() override; |
| |
| // Creates a URLLoaderFactory for a navigation. The factory uses |
| // `header_client`. This should have the same settings as the factory from |
| // the ReconnectableURLLoaderFactoryForIOThread. Called on the UI thread. |
| static mojo::PendingRemote<network::mojom::URLLoaderFactory> |
| CreateURLLoaderFactoryWithHeaderClient( |
| mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient> |
| header_client, |
| network::URLLoaderFactoryBuilder factory_builder, |
| StoragePartitionImpl* partition, |
| std::optional<net::CookieSettingOverrides> devtools_cookie_overrides, |
| std::optional<net::CookieSettingOverrides> cookie_overrides); |
| |
| // NavigationURLLoader implementation: |
| // 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() override; |
| void FollowRedirect( |
| std::vector<std::string> removed_headers, |
| net::HttpRequestHeaders modified_headers, |
| net::HttpRequestHeaders modified_cors_exempt_headers) override; |
| bool SetNavigationTimeout(base::TimeDelta timeout) override; |
| void CancelNavigationTimeout() override; |
| |
| void TriggerTimeoutForTesting(); |
| const network::ResourceRequest& GetResourceRequestForTesting() const; |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(NavigationURLLoaderImplTest, |
| OnAcceptCHFrameReceivedUKM); |
| |
| // Creates a terminal URLLoaderFactory only for a known non-network scheme. |
| static mojo::PendingRemote<network::mojom::URLLoaderFactory> |
| CreateTerminalNonNetworkLoaderFactory(BrowserContext* browser_context, |
| StoragePartitionImpl* storage_partition, |
| FrameTreeNode* frame_tree_node, |
| const GURL& url); |
| // Creates a complete URLLoaderFactory for non-network-service-bound requests. |
| // Unlike `CreateTerminalNonNetworkLoaderFactory()`, this supports |
| // ContentBrowserClient/DevTools interception, external protocols, and unknown |
| // schemes. |
| // `is_cacheable` indicates whether the returned factory can be cached. |
| static std::pair</*is_cacheable=*/bool, |
| scoped_refptr<network::SharedURLLoaderFactory>> |
| CreateNonNetworkLoaderFactory( |
| BrowserContext* browser_context, |
| StoragePartitionImpl* storage_partition, |
| FrameTreeNode* frame_tree_node, |
| const ukm::SourceIdObj& ukm_id, |
| NavigationUIData* navigation_ui_data, |
| const NavigationRequestInfo& request_info, |
| base::RepeatingCallback<WebContents*()> web_contents_getter, |
| const network::ResourceRequest& resource_request); |
| // Like `CreateNonNetworkLoaderFactory()`, but caches the factory in |
| // `non_network_url_loader_factories_` if `is_cacheable` is true, and reuses |
| // it when the same scheme is used more than once in a navigational redirect |
| // chain. This is rare because non-network schemes basically don't redirect, |
| // but can actually happen e.g. in extension scheme's dynamic URLs (see |
| // `DynamicOriginBrowserTest.DynamicUrl` unit test). |
| // TODO(crbug.com/40251638): Consider removing the caching, as caches are |
| // often source of bug. The caching mechanism is left here to keep the |
| // existing behavior. |
| scoped_refptr<network::SharedURLLoaderFactory> |
| GetOrCreateNonNetworkLoaderFactory(); |
| |
| // Creates a SharedURLLoaderFactory for network-service-bound requests. |
| static scoped_refptr<network::SharedURLLoaderFactory> |
| CreateNetworkLoaderFactory(BrowserContext* browser_context, |
| StoragePartitionImpl* storage_partition, |
| FrameTreeNode* frame_tree_node, |
| const ukm::SourceIdObj& ukm_id, |
| bool* bypass_redirect_checks); |
| |
| void BindAndInterceptNonNetworkURLLoaderFactoryReceiver( |
| const GURL& url, |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver); |
| |
| void CreateInterceptors(); |
| |
| // This could be called multiple times to follow a chain of redirects. |
| // This internally rather recreates another loader than actually following the |
| // redirects. |
| void Restart(); |
| |
| // `interceptor_result` is the result from the current interceptor (or nullopt |
| // if not called via `LoaderCallback`). |
| // `next_interceptor` indicates the index of the next interceptor to check. |
| void MaybeStartLoader( |
| size_t next_interceptor_index, |
| std::optional<NavigationLoaderInterceptor::Result> interceptor_result); |
| |
| // Called from `MaybeStartLoader` when the request is elected to be |
| // intercepted. Intercepts the request with `single_request_factory`. |
| void StartInterceptedRequest( |
| scoped_refptr<network::SharedURLLoaderFactory> single_request_factory); |
| |
| // Start a loader with the default behavior. This should be used when no |
| // interceptors have elected to handle the request in the first place. |
| void StartNonInterceptedRequest(ResponseHeadUpdateParams head_update_params); |
| |
| // This is the `fallback_callback_for_service_worker` 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. |
| // |
| // This is a static method + WeakPtr `self` argument, to return `nullptr` when |
| // `self` is destroyed (`base::BindOnce` + non-static member method + |
| // `WeakPtr` doesn't work if the return type is not `void`). |
| static network::mojom::URLLoaderFactory* FallbackToNonInterceptedRequest( |
| base::WeakPtr<NavigationURLLoaderImpl> self, |
| ResponseHeadUpdateParams head_update_params); |
| |
| void CreateThrottlingLoaderAndStart( |
| scoped_refptr<network::SharedURLLoaderFactory> factory, |
| std::vector<std::unique_ptr<blink::URLLoaderThrottle>> |
| additional_throttles); |
| |
| #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( |
| const network::URLLoaderCompletionStatus& status, |
| 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); |
| |
| // When `clear_parsed_headers_for_testing` is true (which is only allowed in |
| // tests), `head->parsed_headers` is cleared to enforce and test the async |
| // `ParseHeaders()` path. https://crbug.com/434182226 |
| void ParseHeaders( |
| const GURL& url, |
| network::mojom::URLResponseHeadPtr head, |
| base::OnceCallback<void(network::mojom::URLResponseHeadPtr)> continuation, |
| bool clear_parsed_headers_for_testing); |
| |
| void NotifyResponseStarted( |
| network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, |
| mojo::ScopedDataPipeConsumerHandle response_body, |
| const GlobalRequestID& global_request_id, |
| bool is_download, |
| network::mojom::URLResponseHeadPtr response_head); |
| |
| void NotifyRequestRedirected(net::RedirectInfo redirect_info, |
| network::mojom::URLResponseHeadPtr response); |
| |
| void NotifyRequestFailed(const network::URLLoaderCompletionStatus& status); |
| |
| // network::mojom::URLLoaderClient implementation: |
| void OnReceiveEarlyHints(network::mojom::EarlyHintsPtr early_hints) override; |
| void OnReceiveResponse( |
| network::mojom::URLResponseHeadPtr head, |
| mojo::ScopedDataPipeConsumerHandle response_body, |
| std::optional<mojo_base::BigBuffer> cached_metadata) 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 OnTransferSizeUpdated(int32_t transfer_size_diff) override; |
| void OnComplete(const network::URLLoaderCompletionStatus& status) override; |
| |
| // network::mojom::AcceptCHFrameObserver implementation |
| void OnAcceptCHFrameReceived( |
| const url::Origin& origin, |
| const std::vector<network::mojom::WebClientHintsType>& accept_ch_frame, |
| OnAcceptCHFrameReceivedCallback callback) override; |
| void Clone(mojo::PendingReceiver<network::mojom::AcceptCHFrameObserver> |
| listener) override; |
| |
| // Records UKM for the navigation load. |
| void RecordReceivedResponseUkmForOutermostMainFrame(); |
| |
| // Record ServiceWorker and the static routing API evaluation related results. |
| void MaybeRecordServiceWorkerMainResourceInfo( |
| const network::mojom::URLResponseHeadPtr& head); |
| |
| raw_ptr<NavigationURLLoaderDelegate, DanglingUntriaged> delegate_; |
| raw_ptr<BrowserContext> browser_context_; |
| raw_ptr<StoragePartitionImpl> storage_partition_; |
| raw_ptr<ServiceWorkerMainResourceHandle> service_worker_handle_; |
| |
| std::unique_ptr<network::ResourceRequest> resource_request_; |
| std::unique_ptr<NavigationRequestInfo> request_info_; |
| |
| // Current URL that is being navigated, updated after redirection. |
| GURL url_; |
| |
| const FrameTreeNodeId frame_tree_node_id_; |
| const GlobalRequestID global_request_id_; |
| net::RedirectInfo redirect_info_; |
| int redirect_limit_ = net::URLRequest::kMaxRedirects; |
| int accept_ch_restart_limit_ = net::URLRequest::kMaxRedirects; |
| base::RepeatingCallback<WebContents*()> web_contents_getter_; |
| std::unique_ptr<NavigationUIData> navigation_ui_data_; |
| |
| scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory_; |
| |
| SubresourceLoaderParams subresource_loader_params_; |
| |
| std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors_; |
| |
| // Set to true if the default URLLoader (network service) was used for the |
| // current navigation. |
| bool default_loader_used_ = false; |
| |
| // Set to true if we receive a valid response from a URLLoader, i.e. |
| // URLLoaderClient::OnReceiveResponse() is called. |
| bool received_response_ = false; |
| |
| 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. |
| std::optional<network::URLLoaderCompletionStatus> status_; |
| |
| // 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; |
| |
| mojo::ScopedDataPipeConsumerHandle response_body_; |
| |
| // Lazily initialized and used in the case of non-network resource |
| // navigations. Keyed by URL scheme. |
| std::map<std::string, scoped_refptr<network::SharedURLLoaderFactory>> |
| non_network_url_loader_factories_; |
| |
| // `NavigationURLLoaderImpl` performs the loading (receiving the |
| // `URLLoaderClient` callbacks and related operations) in multiple ways (e.g. |
| // through `url_loader_` or `response_loader_receiver_`). `LoaderHolder` |
| // centralizes these, to ensure that: |
| // - All related operations are cancelled when `Reset()` is called. |
| // - The state transitions are consistent. |
| // |
| // Note: This isn't a complete encapsulation. Some states and operations |
| // (especially related to https://crbug.com/434182226) are centralized and |
| // checked in `LoaderHolder`, while other operations are still performed |
| // directly via `url_loader()`. |
| class LoaderHolder final { |
| public: |
| explicit LoaderHolder(network::mojom::URLLoaderClient* receiver); |
| ~LoaderHolder(); |
| |
| // Right now, `State` is used only for `DUMP_WILL_BE_CHECK()`ing and the |
| // other underlying members (e.g. `url_loader_`) should be used to check |
| // state-dependent conditions. |
| // TODO(https://crbug.com/434182226): Once the `DUMP_WILL_BE_CHECK()`s stick |
| // and are turned into `CHECK()`s, `State` must match with `url_loader_` |
| // etc. and thus should be used also for non-CHECK purposes. |
| enum class State { |
| // Neither of the loader or receiver is active right now. |
| // This can be a transient state when processing a redirect (after |
| // resetting the previous redirect leg and just before starting the next |
| // leg). |
| kNone, |
| |
| // The loading is ongoing and the `NavigationURLLoaderImpl`'s |
| // `URLLoaderClient` methods are called via `url_loader_` (the primary |
| // cases). |
| // The `URLLoaderClient` methods are called directly (synchronously) by |
| // `blink::ThrottlingURLLoader`. |
| kLoadingViaLoader, |
| |
| // The loading is ongoing and the `NavigationURLLoaderImpl`'s |
| // `URLLoaderClient` methods are called via |
| // `response_loader_receiver_` (for `MaybeCreateLoaderForResponse()`). |
| // The `URLLoaderClient` methods are called via mojo. |
| kLoadingViaReceiver, |
| |
| // All loading via `NavigationURLLoaderImpl` is done and further |
| // operation on `LoaderHolder` or calls to `NavigationURLLoaderImpl`'s |
| // `URLLoaderClient` methods shouldn't be made, except for some operations |
| // directly through `url_loader()`. |
| kUnbound, |
| }; |
| |
| State state() const { return state_; } |
| |
| blink::ThrottlingURLLoader* url_loader() const { return url_loader_.get(); } |
| mojo::PendingRemote<network::mojom::URLLoader>* response_url_loader() { |
| return &response_url_loader_; |
| } |
| |
| // Cancel the current loading, if any. |
| // Any associated pending operations should be cancelled. |
| // Transitions to `State::kNone`. |
| // |
| // Note: The "exclusive tasks" (see `ExclusiveTaskState` below) can't be |
| // gracefully cancelled here and thus `Reset()` should be called only when |
| // there should always be no exclusive tasks. See also `ResetForFailure()`. |
| void Reset(); |
| |
| // When the caller wants to start a new loading operation while there can be |
| // existing exclusive tasks, the caller should check `HasExclusiveTask()`, |
| // and if there are exclusive tasks, call `ResetForFailure()` and fail the |
| // entire loading instead. |
| // |
| // This is similar to `Reset()`, but also instructs exclusive tasks to be |
| // cancelled. |
| void ResetForFailure(); |
| |
| // For starting loading via `url_loader` (transitioning from `kNone` to |
| // `kLoadingViaLoader`). THe caller should actually start the loading by |
| // calling `url_loader->Start()`. |
| // Transitions to `State::kLoadingViaLoader`. |
| void SetLoader(std::unique_ptr<blink::ThrottlingURLLoader> url_loader); |
| |
| // Switches to loading via `pending_receiver` (transitioning from |
| // `kLoadingViaLoader` to `kLoadingViaReceiver`). The caller might already |
| // call `url_loader()->Unbind()` etc. |
| // Transitions to `State::kLoadingViaReceiver`. |
| void BindReceiver( |
| mojo::PendingReceiver<network::mojom::URLLoaderClient> pending_receiver, |
| scoped_refptr<base::SequencedTaskRunner> task_runner); |
| |
| // Unbind the endpoints from ``NavigationURLLoaderImpl`` to |
| // `URLLoaderClientEndpointsPtr` (transitioning to `kUnbound`). |
| // Transitions to `State::kUnbound`. |
| [[nodiscard]] network::mojom::URLLoaderClientEndpointsPtr Unbind(); |
| |
| // Redirect handling: the expected sequence is: |
| // 1. `NavigationURLLoaderImpl::OnReceiveRedirect()` |
| // 2. `LoaderHolder::SetModifiedHeadersOnRedirect()` |
| // 3. Either: |
| // - `LoaderHolder::FollowRedirect()`, when we want and can continue |
| // using the current `url_loader` for the next redirect leg, or |
| // - `LoaderHolder::ResetForFollowRedirect()` then |
| // `LoaderHolder::SetLoader()`, when we start the next redirect leg |
| // with a new loader. |
| // Also `LoaderHolder::Reset()` can be called at any time during this to |
| // cancel loading. |
| // TODO(https://crbug.com/434182226): Add `CHECK()`s to confirm these |
| // sequences. |
| |
| // Cache the modified request headers provided by clients during redirect. |
| // They will be consumed by next `FollowRedirect()` or |
| // `ResetForFollowRedirect()`. |
| void SetModifiedHeadersOnRedirect( |
| std::vector<std::string> removed_headers, |
| net::HttpRequestHeaders modified_headers, |
| net::HttpRequestHeaders modified_cors_exempt_headers); |
| |
| // Follows the redirect using the current `url_loader_`. |
| void FollowRedirect(); |
| |
| // Similar to `ResetLoader()`, but also calls |
| // `URLLoader::ResetForFollowRedirect()` if needed. |
| void ResetForFollowRedirect(network::ResourceRequest& resource_request); |
| |
| // See the `ExclusiveTaskState` comment below. |
| // TODO(https://crbug.com/434182226): Add more exclusive tasks handing. |
| enum ExclusiveTaskType { |
| // From `OnReceiveRedirect()` until `FollowRedirect()`. |
| // This contains two possible async tasks: |
| // - Waiting for `network.mojom.NetworkService::ParseHeaders()` and |
| // - Waiting for `NavigationURLLoaderDelegate`: from |
| // `OnRequestRedirected()` until |
| // `NavigationURLLoaderImpl::FollowRedirect()` is called. |
| kRedirect, |
| |
| // Waiting for `NavigationLoaderInterceptor::MaybeCreateLoader()`. |
| // From `NavigationURLLoaderImpl::Restart()` |
| // Until `NavigationURLLoaderImpl::StartNonInterceptedRequest()` or |
| // `NavigationURLLoaderImpl::StartInterceptedRequest()`. |
| kInterceptor, |
| }; |
| void OnExclusiveTaskStarted(ExclusiveTaskType exclusive_task_type); |
| void OnExclusiveTaskCompleted(ExclusiveTaskType exclusive_task_type); |
| bool HasExclusiveTask() const; |
| // Should be called only during an exclusive task. |
| bool ShouldCancelExclusiveTask(ExclusiveTaskType exclusive_task_type) const; |
| |
| bool receiver_is_bound_for_check() const; |
| |
| private: |
| void ResetInternal(); |
| void CheckState() const; |
| |
| State state_ = State::kNone; |
| |
| // `NavigationURLLoaderImpl`'s `URLLoaderClient` methods are called either |
| // via `url_loader_` or `response_loader_receiver_`. |
| // See also the comment at `State` above for details. |
| std::unique_ptr<blink::ThrottlingURLLoader> url_loader_; |
| mojo::Receiver<network::mojom::URLLoaderClient> response_loader_receiver_; |
| |
| // URLLoader instance for response loaders, i.e loaders created for handling |
| // responses received from the network URLLoader. |
| // |
| // NOTE: This looks like coupled with |
| // `LoaderHolder::response_loader_receiver_` but actually isn't, because |
| // `response_url_loader_` is never touched during |
| // `MaybeCreateLoaderForResponse()` (at least within Chromium codesearch). |
| // For now this is kept here as-is but probably can be removed. |
| mojo::PendingRemote<network::mojom::URLLoader> response_url_loader_; |
| |
| struct ModifiedHeadersOnRedirect final { |
| ModifiedHeadersOnRedirect( |
| std::vector<std::string> removed_headers, |
| net::HttpRequestHeaders modified_headers, |
| net::HttpRequestHeaders modified_cors_exempt_headers); |
| ModifiedHeadersOnRedirect(const ModifiedHeadersOnRedirect&) = delete; |
| ModifiedHeadersOnRedirect(ModifiedHeadersOnRedirect&&) = delete; |
| ~ModifiedHeadersOnRedirect(); |
| |
| std::vector<std::string> removed_headers_; |
| net::HttpRequestHeaders modified_headers_; |
| net::HttpRequestHeaders modified_cors_exempt_headers_; |
| }; |
| std::optional<ModifiedHeadersOnRedirect> modified_headers_on_redirect_; |
| |
| // `NavigationURLLoaderImpl` can be waiting for a certain (possibly async) |
| // "exclusive task" and can't start a new request nor receive |
| // URLLoaderClient method calls until the task completes. For example, |
| // `NavigationURLLoaderImpl`, `NavigationLoaderInterceptor` and |
| // `NavigationRequest` are going through checks before sending an initial or |
| // redirected request and in the middle of updating the request and other |
| // states. |
| // |
| // Not all arbitrary async operations are considered exclusive tasks here. |
| // For example, waiting for URLLoaderClient calls from `url_loader_` or |
| // `response_loader_receiver_` aren't considered exclusive tasks, because |
| // e.g. we can cancel the `url_loader_`, issue a synthetic redirect response |
| // as if it would be received from `url_loader_` and continue on the |
| // synthetic redirect. |
| // |
| // Original design doc: |
| // https://docs.google.com/document/d/1xCq9l9mYc3WE1adspyaX7FnPArxKDyz8BcRGTlpQIu8/edit?usp=sharing |
| enum ExclusiveTaskState { |
| kNoExclusiveTask, |
| |
| // There is an exclusive task, and therefore: |
| // - No URLLoaderClient methods should be called (except for unexpected |
| // `OnComplete()` or error events like timeout). |
| // - No actions that could initiate a new request/redirect etc. are |
| // allowed. When attempting such actions, `NavigationURLLoaderImpl` |
| // should call `ResetForFailure()` and make the navigation fail |
| // immediately. |
| // |
| // There should be at most one exclusive task at a time. |
| kHasExclusiveTask, |
| |
| // After `ResetForFailure()` is called, navigation fails and existing |
| // exclusive tasks should be cancelled. |
| kCancelExclusiveTask, |
| }; |
| |
| ExclusiveTaskState exclusive_task_state_ = |
| ExclusiveTaskState::kNoExclusiveTask; |
| std::optional<ExclusiveTaskType> current_exclusive_task_type_; |
| }; |
| |
| LoaderHolder loader_holder_{this}; |
| |
| std::unique_ptr<NavigationEarlyHintsManager> early_hints_manager_; |
| |
| // Cleared after `Start()`. |
| scoped_refptr<PrefetchedSignedExchangeCache> |
| prefetched_signed_exchange_cache_; |
| |
| // While it's not expected to have two active Remote ends for the same |
| // NavigationURLLoaderImpl, when a TrustedParam is copied all of the pipes are |
| // cloned instead of being destroyed. |
| mojo::ReceiverSet<network::mojom::AcceptCHFrameObserver> |
| accept_ch_frame_observers_; |
| |
| // Timer used for triggering (optional) early timeout on the navigation. |
| base::OneShotTimer timeout_timer_; |
| |
| // The time this loader was created. |
| base::TimeTicks loader_creation_time_; |
| |
| // Whether the navigation processed an ACCEPT_CH frame in the TLS handshake. |
| bool received_accept_ch_frame_ = false; |
| |
| // UKM source id used for recording events associated with navigation loading. |
| const ukm::SourceId ukm_source_id_; |
| |
| // If this navigation was intercepted by a worker but the worker didn't handle |
| // it, we still expose some parameters like the worker timing as part of the |
| // response. |
| ResponseHeadUpdateParams head_update_params_; |
| |
| base::WeakPtrFactory<NavigationURLLoaderImpl> weak_factory_{this}; |
| }; |
| |
| // Creates a `ResourceRequest` and sets fields common to navigation and |
| // prefetch. |
| // This roughly corresponds to: |
| // - Step 3 of |
| // https://html.spec.whatwg.org/multipage/browsing-the-web.html#create-navigation-params-by-fetching |
| // - Step 2 of |
| // https://wicg.github.io/nav-speculation/prefetch.html#create-a-navigation-request |
| // and their surrounding steps. |
| // |
| // This helper method is used to create consistent navigational |
| // `ResourceRequest`s (exposed to the network service and ServiceWorker fetch |
| // handlers) and make them look similar, regardless of whether they are created |
| // for prefetches or non-prefetch navigations. |
| std::unique_ptr<network::ResourceRequest> CreateResourceRequestForNavigation( |
| const std::string& method, |
| const GURL& url, |
| network::mojom::RequestDestination destination, |
| const blink::mojom::Referrer& referrer, |
| const net::IsolationInfo& isolation_info, |
| mojo::PendingRemote<network::mojom::DevToolsObserver> devtools_observer, |
| net::RequestPriority priority, |
| bool is_main_frame); |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_IMPL_H_ |