| // Copyright 2019 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/browser/url_loader_factory_params_helper.h" | 
 |  | 
 | #include <optional> | 
 | #include <string_view> | 
 |  | 
 | #include "base/command_line.h" | 
 | #include "base/containers/lru_cache.h" | 
 | #include "base/no_destructor.h" | 
 | #include "content/browser/devtools/network_service_devtools_observer.h" | 
 | #include "content/browser/renderer_host/frame_tree_node.h" | 
 | #include "content/browser/renderer_host/navigation_request.h" | 
 | #include "content/browser/renderer_host/render_frame_host_impl.h" | 
 | #include "content/browser/storage_partition_impl.h" | 
 | #include "content/public/browser/content_browser_client.h" | 
 | #include "content/public/browser/render_process_host.h" | 
 | #include "content/public/browser/render_view_host.h" | 
 | #include "content/public/browser/runtime_feature_state/runtime_feature_state_document_data.h" | 
 | #include "content/public/common/content_client.h" | 
 | #include "content/public/common/content_switches.h" | 
 | #include "content/public/common/url_constants.h" | 
 | #include "net/base/features.h" | 
 | #include "net/base/isolation_info.h" | 
 | #include "net/cookies/cookie_setting_override.h" | 
 | #include "services/network/public/cpp/is_potentially_trustworthy.h" | 
 | #include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h" | 
 | #include "services/network/public/mojom/device_bound_sessions.mojom.h" | 
 | #include "services/network/public/mojom/early_hints.mojom.h" | 
 | #include "services/network/public/mojom/network_context.mojom.h" | 
 | #include "services/network/public/mojom/shared_dictionary_access_observer.mojom.h" | 
 | #include "services/network/public/mojom/url_loader.mojom-shared.h" | 
 | #include "third_party/blink/public/common/tokens/tokens.h" | 
 | #include "third_party/blink/public/common/web_preferences/web_preferences.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | // Helper used by the public URLLoaderFactoryParamsHelper::Create... methods. | 
 | // | 
 | // |origin| is the origin that will use the URLLoaderFactory. | 
 | // |origin| is typically the same as the origin in | 
 | // network::ResourceRequest::request_initiator, except when | 
 | // |is_for_isolated_world|.  See also the doc comment for | 
 | // extensions::URLLoaderFactoryManager::CreateFactory. | 
 | network::mojom::URLLoaderFactoryParamsPtr CreateParams( | 
 |     RenderProcessHost* process, | 
 |     const url::Origin& origin, | 
 |     const url::Origin& request_initiator_origin_lock, | 
 |     bool is_trusted, | 
 |     const std::optional<blink::LocalFrameToken>& top_frame_token, | 
 |     const net::IsolationInfo& isolation_info, | 
 |     network::mojom::ClientSecurityStatePtr client_security_state, | 
 |     mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> | 
 |         coep_reporter, | 
 |     mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> | 
 |         dip_reporter, | 
 |     bool allow_universal_access_from_file_urls, | 
 |     bool is_for_isolated_world, | 
 |     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, | 
 |     network::mojom::TrustTokenOperationPolicyVerdict | 
 |         trust_token_issuance_policy, | 
 |     network::mojom::TrustTokenOperationPolicyVerdict | 
 |         trust_token_redemption_policy, | 
 |     net::CookieSettingOverrides cookie_setting_overrides, | 
 |     std::string_view debug_tag, | 
 |     bool require_cross_site_request_for_cookies, | 
 |     bool is_for_service_worker) { | 
 |   DCHECK(process); | 
 |  | 
 |   network::mojom::URLLoaderFactoryParamsPtr params = | 
 |       network::mojom::URLLoaderFactoryParams::New(); | 
 |  | 
 |   params->process_id = process->GetDeprecatedID(); | 
 |   params->request_initiator_origin_lock = request_initiator_origin_lock; | 
 |  | 
 |   params->is_trusted = is_trusted; | 
 |   if (top_frame_token) | 
 |     params->top_frame_id = top_frame_token.value().value(); | 
 |   params->isolation_info = isolation_info; | 
 |  | 
 |   params->disable_web_security = | 
 |       base::CommandLine::ForCurrentProcess()->HasSwitch( | 
 |           switches::kDisableWebSecurity); | 
 |   params->client_security_state = std::move(client_security_state); | 
 |   params->coep_reporter = std::move(coep_reporter); | 
 |   params->dip_reporter = std::move(dip_reporter); | 
 |  | 
 |   if (params->disable_web_security) { | 
 |     // --disable-web-security also disables Opaque Response Blocking (ORB). | 
 |     params->is_orb_enabled = false; | 
 |   } else if (allow_universal_access_from_file_urls && | 
 |              origin.scheme() == url::kFileScheme) { | 
 |     // allow_universal_access_from_file_urls disables ORB (via | 
 |     // `is_orb_enabled`) and CORS (via `disable_web_security`) for requests | 
 |     // made from a file: |origin|. | 
 |     params->is_orb_enabled = false; | 
 |     params->disable_web_security = true; | 
 |   } else { | 
 |     params->is_orb_enabled = true; | 
 |   } | 
 |  | 
 |   params->trust_token_issuance_policy = trust_token_issuance_policy; | 
 |   params->trust_token_redemption_policy = trust_token_redemption_policy; | 
 |  | 
 |   GetContentClient()->browser()->OverrideURLLoaderFactoryParams( | 
 |       process->GetBrowserContext(), origin, is_for_isolated_world, | 
 |       is_for_service_worker, params.get()); | 
 |  | 
 |   params->cookie_observer = std::move(cookie_observer); | 
 |   params->trust_token_observer = std::move(trust_token_observer); | 
 |   params->shared_dictionary_observer = std::move(shared_dictionary_observer); | 
 |   params->url_loader_network_observer = std::move(url_loader_network_observer); | 
 |   params->devtools_observer = std::move(devtools_observer); | 
 |   params->device_bound_session_observer = | 
 |       std::move(device_bound_session_observer); | 
 |  | 
 |   params->cookie_setting_overrides = cookie_setting_overrides; | 
 |  | 
 |   params->debug_tag = std::string(debug_tag); | 
 |  | 
 |   params->require_cross_site_request_for_cookies = | 
 |       require_cross_site_request_for_cookies; | 
 |  | 
 |   if (URLLoaderFactoryParamsHelper::IsMainFrameOriginRecentlyAccessed( | 
 |           isolation_info)) { | 
 |     params->is_main_frame_origin_recently_accessed = true; | 
 |   } | 
 |  | 
 |   return params; | 
 | } | 
 |  | 
 | base::LRUCacheSet<url::Origin>& GetRecentlyAccessedOriginSet() { | 
 |   static base::NoDestructor<base::LRUCacheSet<url::Origin>> origin_set( | 
 |       net::features::kRecentlyAccessedOriginCacheSize.Get()); | 
 |   return *origin_set; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | network::mojom::URLLoaderFactoryParamsPtr | 
 | URLLoaderFactoryParamsHelper::CreateForFrame( | 
 |     RenderFrameHostImpl* frame, | 
 |     const url::Origin& frame_origin, | 
 |     const net::IsolationInfo& isolation_info, | 
 |     network::mojom::ClientSecurityStatePtr client_security_state, | 
 |     mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> | 
 |         coep_reporter, | 
 |     mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> | 
 |         dip_reporter, | 
 |     RenderProcessHost* process, | 
 |     network::mojom::TrustTokenOperationPolicyVerdict | 
 |         trust_token_issuance_policy, | 
 |     network::mojom::TrustTokenOperationPolicyVerdict | 
 |         trust_token_redemption_policy, | 
 |     net::CookieSettingOverrides cookie_setting_overrides, | 
 |     std::string_view debug_tag) { | 
 |   return CreateParams( | 
 |       process, | 
 |       frame_origin,  // origin | 
 |       frame_origin,  // request_initiator_origin_lock | 
 |       false,         // is_trusted | 
 |       frame->GetTopFrameToken(), isolation_info, | 
 |       std::move(client_security_state), std::move(coep_reporter), | 
 |       std::move(dip_reporter), | 
 |       frame->GetOrCreateWebPreferences().allow_universal_access_from_file_urls, | 
 |       false,  // is_for_isolated_world | 
 |       frame->CreateCookieAccessObserver( | 
 |           CookieAccessDetails::Source::kNonNavigation), | 
 |       frame->CreateTrustTokenAccessObserver(), | 
 |       frame->CreateSharedDictionaryAccessObserver(), | 
 |       frame->CreateURLLoaderNetworkObserver(), | 
 |       NetworkServiceDevToolsObserver::MakeSelfOwned(frame->frame_tree_node()), | 
 |       frame->CreateDeviceBoundSessionObserver(), trust_token_issuance_policy, | 
 |       trust_token_redemption_policy, cookie_setting_overrides, debug_tag, | 
 |       /*require_cross_site_request_for_cookies=*/false, | 
 |       /*is_for_service_worker=*/false); | 
 | } | 
 |  | 
 | // static | 
 | network::mojom::URLLoaderFactoryParamsPtr | 
 | URLLoaderFactoryParamsHelper::CreateForIsolatedWorld( | 
 |     RenderFrameHostImpl* frame, | 
 |     const url::Origin& isolated_world_origin, | 
 |     const url::Origin& main_world_origin, | 
 |     const net::IsolationInfo& isolation_info, | 
 |     network::mojom::ClientSecurityStatePtr client_security_state, | 
 |     network::mojom::TrustTokenOperationPolicyVerdict | 
 |         trust_token_issuance_policy, | 
 |     network::mojom::TrustTokenOperationPolicyVerdict | 
 |         trust_token_redemption_policy, | 
 |     net::CookieSettingOverrides cookie_setting_overrides) { | 
 |   return CreateParams( | 
 |       frame->GetProcess(), | 
 |       isolated_world_origin,  // origin | 
 |       main_world_origin,      // request_initiator_origin_lock | 
 |       false,                  // is_trusted | 
 |       frame->GetTopFrameToken(), isolation_info, | 
 |       std::move(client_security_state), | 
 |       mojo::NullRemote(),  // coep_reporter | 
 |       mojo::NullRemote(),  // dip_reporter | 
 |       frame->GetOrCreateWebPreferences().allow_universal_access_from_file_urls, | 
 |       true,  // is_for_isolated_world | 
 |       frame->CreateCookieAccessObserver( | 
 |           CookieAccessDetails::Source::kNonNavigation), | 
 |       frame->CreateTrustTokenAccessObserver(), | 
 |       frame->CreateSharedDictionaryAccessObserver(), | 
 |       frame->CreateURLLoaderNetworkObserver(), | 
 |       NetworkServiceDevToolsObserver::MakeSelfOwned(frame->frame_tree_node()), | 
 |       frame->CreateDeviceBoundSessionObserver(), trust_token_issuance_policy, | 
 |       trust_token_redemption_policy, cookie_setting_overrides, | 
 |       "ParamHelper::CreateForIsolatedWorld", | 
 |       /*require_cross_site_request_for_cookies=*/false, | 
 |       /*is_for_service_worker=*/false); | 
 | } | 
 |  | 
 | network::mojom::URLLoaderFactoryParamsPtr | 
 | URLLoaderFactoryParamsHelper::CreateForPrefetch( | 
 |     RenderFrameHostImpl* frame, | 
 |     network::mojom::ClientSecurityStatePtr client_security_state, | 
 |     net::CookieSettingOverrides cookie_setting_overrides) { | 
 |   // The factory client |is_trusted| to control the |network_isolation_key| in | 
 |   // each separate request (rather than forcing the client to use the key | 
 |   // specified in URLLoaderFactoryParams). | 
 |   const url::Origin& frame_origin = frame->GetLastCommittedOrigin(); | 
 |   return CreateParams( | 
 |       frame->GetProcess(), | 
 |       frame_origin,  // origin | 
 |       frame_origin,  // request_initiator_origin_lock | 
 |       true,          // is_trusted | 
 |       frame->GetTopFrameToken(), | 
 |       net::IsolationInfo(),  // isolation_info | 
 |       std::move(client_security_state), | 
 |       mojo::NullRemote(),  // coep_reporter | 
 |       mojo::NullRemote(),  // dip_reporter | 
 |       frame->GetOrCreateWebPreferences().allow_universal_access_from_file_urls, | 
 |       false,  // is_for_isolated_world | 
 |       frame->CreateCookieAccessObserver( | 
 |           CookieAccessDetails::Source::kNonNavigation), | 
 |       frame->CreateTrustTokenAccessObserver(), | 
 |       frame->CreateSharedDictionaryAccessObserver(), | 
 |       frame->CreateURLLoaderNetworkObserver(), | 
 |       NetworkServiceDevToolsObserver::MakeSelfOwned(frame->frame_tree_node()), | 
 |       frame->CreateDeviceBoundSessionObserver(), | 
 |       network::mojom::TrustTokenOperationPolicyVerdict::kForbid, | 
 |       network::mojom::TrustTokenOperationPolicyVerdict::kForbid, | 
 |       cookie_setting_overrides, "ParamHelper::CreateForPrefetch", | 
 |       /*require_cross_site_request_for_cookies=*/false, | 
 |       /*is_for_service_worker=*/false); | 
 | } | 
 |  | 
 | // static | 
 | // TODO(crbug.com/40190528): make sure client_security_state is no longer | 
 | // nullptr anywhere. | 
 | // TODO(crbug.com/40247160): Investigate whether to support cookie setting | 
 | // overrides (hardcoded empty set used for now). | 
 | network::mojom::URLLoaderFactoryParamsPtr | 
 | URLLoaderFactoryParamsHelper::CreateForWorker( | 
 |     RenderProcessHost* process, | 
 |     const url::Origin& request_initiator, | 
 |     const net::IsolationInfo& isolation_info, | 
 |     mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> | 
 |         coep_reporter, | 
 |     mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> | 
 |         dip_reporter, | 
 |     mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> | 
 |         url_loader_network_observer, | 
 |     mojo::PendingRemote<network::mojom::DevToolsObserver> devtools_observer, | 
 |     network::mojom::ClientSecurityStatePtr client_security_state, | 
 |     std::string_view debug_tag, | 
 |     bool require_cross_site_request_for_cookies, | 
 |     bool is_for_service_worker) { | 
 |   return CreateParams( | 
 |       process, | 
 |       request_initiator,  // origin | 
 |       request_initiator,  // request_initiator_origin_lock | 
 |       false,              // is_trusted | 
 |       std::nullopt,       // top_frame_token | 
 |       isolation_info, std::move(client_security_state), | 
 |       std::move(coep_reporter), std::move(dip_reporter), | 
 |       false,  // allow_universal_access_from_file_urls | 
 |       false,  // is_for_isolated_world | 
 |       static_cast<StoragePartitionImpl*>(process->GetStoragePartition()) | 
 |           ->CreateCookieAccessObserverForServiceWorker(), | 
 |       static_cast<StoragePartitionImpl*>(process->GetStoragePartition()) | 
 |           ->CreateTrustTokenAccessObserverForServiceWorker(), | 
 |       static_cast<StoragePartitionImpl*>(process->GetStoragePartition()) | 
 |           ->CreateSharedDictionaryAccessObserverForServiceWorker(), | 
 |       std::move(url_loader_network_observer), std::move(devtools_observer), | 
 |       static_cast<StoragePartitionImpl*>(process->GetStoragePartition()) | 
 |           ->CreateDeviceBoundSessionObserverForServiceWorker(), | 
 |       // Trust Token redemption and signing operations require the Permissions | 
 |       // Policy. It seems Permissions Policy in worker contexts | 
 |       // is currently an open issue (as of 06/21/2022): | 
 |       // https://github.com/w3c/webappsec-permissions-policy/issues/207. | 
 |       network::mojom::TrustTokenOperationPolicyVerdict::kPotentiallyPermit, | 
 |       network::mojom::TrustTokenOperationPolicyVerdict::kPotentiallyPermit, | 
 |       net::CookieSettingOverrides(), debug_tag, | 
 |       require_cross_site_request_for_cookies, is_for_service_worker); | 
 | } | 
 |  | 
 | // static | 
 | // TODO(crbug.com/40247160): Investigate whether to support cookie setting | 
 | // overrides (hardcoded empty set used for now). | 
 | network::mojom::URLLoaderFactoryParamsPtr | 
 | URLLoaderFactoryParamsHelper::CreateForEarlyHintsPreload( | 
 |     RenderProcessHost* process, | 
 |     const url::Origin& tentative_origin, | 
 |     NavigationRequest& navigation_request, | 
 |     const network::mojom::EarlyHints& early_hints, | 
 |     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::DeviceBoundSessionAccessObserver> | 
 |         device_bound_session_observer) { | 
 |   // TODO(crbug.com/40188470): Consider not using the speculative | 
 |   // RenderFrameHostImpl to create URLLoaderNetworkServiceObserver. | 
 |   // In general we should avoid using speculative RenderFrameHostImpl | 
 |   // to fill URLLoaderFactoryParams because some parameters can be calculated | 
 |   // only after the RenderFrameHostImpl is committed. | 
 |   // See also the design doc linked from the bug entry. It describes options | 
 |   // to create the observer without RenderFrameHostImpl. | 
 |   mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> | 
 |       url_loader_network_observer = navigation_request.frame_tree_node() | 
 |                                         ->current_frame_host() | 
 |                                         ->CreateURLLoaderNetworkObserver(); | 
 |  | 
 |   auto isolation_info = net::IsolationInfo::Create( | 
 |       net::IsolationInfo::RequestType::kOther, | 
 |       /*top_frame_origin=*/tentative_origin, /*frame_origin=*/tentative_origin, | 
 |       net::SiteForCookies::FromOrigin(tentative_origin)); | 
 |  | 
 |   // TODO(https://issues.chromium.org/issues/336754077): | 
 |   // Support Document-Isolation-Policy in early hints headers instead of passing | 
 |   // a default DocumentIsolationPolicy. | 
 |   network::mojom::ClientSecurityStatePtr client_security_state = | 
 |       network::mojom::ClientSecurityState::New( | 
 |           early_hints.headers->cross_origin_embedder_policy, | 
 |           network::IsOriginPotentiallyTrustworthy(tentative_origin), | 
 |           early_hints.ip_address_space, | 
 |           network::mojom::PrivateNetworkRequestPolicy::kBlock, | 
 |           network::DocumentIsolationPolicy()); | 
 |  | 
 |   return CreateParams( | 
 |       process, /*origin=*/tentative_origin, | 
 |       /*request_initiator_origin_lock=*/tentative_origin, | 
 |       /*is_trusted=*/false, /*top_frame_token=*/std::nullopt, isolation_info, | 
 |       std::move(client_security_state), | 
 |       /*coep_reporter=*/mojo::NullRemote(), | 
 |       /*dip_reporter=*/mojo::NullRemote(), | 
 |       /*allow_universal_access_from_file_urls=*/false, | 
 |       /*is_for_isolated_world=*/false, std::move(cookie_observer), | 
 |       std::move(trust_token_observer), std::move(shared_dictionary_observer), | 
 |       std::move(url_loader_network_observer), | 
 |       /*devtools_observer=*/mojo::NullRemote(), | 
 |       std::move(device_bound_session_observer), | 
 |       network::mojom::TrustTokenOperationPolicyVerdict::kForbid, | 
 |       network::mojom::TrustTokenOperationPolicyVerdict::kForbid, | 
 |       net::CookieSettingOverrides(), "ParamHelper::CreateForEarlyHintsPreload", | 
 |       /*require_cross_site_request_for_cookies=*/false, | 
 |       /*is_for_service_worker=*/false); | 
 | } | 
 |  | 
 | // static | 
 | void URLLoaderFactoryParamsHelper::OnMainFrameNavigation(url::Origin origin) { | 
 |   if (base::FeatureList::IsEnabled( | 
 |           net::features::kUpdateIsMainFrameOriginRecentlyAccessed)) { | 
 |     GetRecentlyAccessedOriginSet().Put(std::move(origin)); | 
 |   } | 
 | } | 
 |  | 
 | // static | 
 | bool URLLoaderFactoryParamsHelper::IsMainFrameOriginRecentlyAccessed( | 
 |     const net::IsolationInfo& isolation_info) { | 
 |   if (!base::FeatureList::IsEnabled( | 
 |           net::features::kUpdateIsMainFrameOriginRecentlyAccessed)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   const std::optional<url::Origin> top_frame_origin = | 
 |       isolation_info.top_frame_origin(); | 
 |   if (!top_frame_origin) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   auto& origin_set = GetRecentlyAccessedOriginSet(); | 
 |   return origin_set.Peek(top_frame_origin.value()) != origin_set.end(); | 
 | } | 
 |  | 
 | }  // namespace content |