blob: f76a82dc7b01228135e4e7c014339886904c837e [file] [log] [blame]
// Copyright 2018 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/worker_host/worker_script_fetcher.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/task/single_thread_task_runner.h"
#include "content/browser/data_url_loader_factory.h"
#include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/devtools/network_service_devtools_observer.h"
#include "content/browser/file_system/file_system_url_loader_factory.h"
#include "content/browser/loader/browser_initiated_resource_request.h"
#include "content/browser/loader/file_url_loader_factory.h"
#include "content/browser/loader/url_loader_factory_utils.h"
#include "content/browser/navigation_subresource_loader_params.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_main_resource_handle.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/url_loader_factory_params_helper.h"
#include "content/browser/worker_host/worker_script_loader.h"
#include "content/browser/worker_host/worker_script_loader_factory.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/url_loader_throttles.h"
#include "content/public/browser/web_ui_url_loader_factory.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/referrer.h"
#include "content/public/common/url_constants.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/isolation_info.h"
#include "net/http/http_request_headers.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/constants.h"
#include "services/network/public/cpp/ip_address_space_util.h"
#include "services/network/public/cpp/record_ontransfersizeupdate_utils.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "services/network/public/mojom/early_hints.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/common/loader/throttling_url_loader.h"
#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
namespace content {
namespace {
const net::NetworkTrafficAnnotationTag kWorkerScriptLoadTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("worker_script_load",
R"(
semantics {
sender: "Web Worker Script Load"
description:
"This request is issued by Web Worker to fetch its main script."
trigger:
"Calling new Worker() or SharedWorker()."
data: "Anything the initiator wants to send."
destination: OTHER
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting: "This request can be prevented by disabling JavaScript."
chrome_policy {
URLBlocklist {
URLBlocklist: { entries: '*' }
}
}
chrome_policy {
URLAllowlist {
URLAllowlist { }
}
}
}
)");
// TODO(nhiroki): Align this function with AddAdditionalRequestHeaders() in
// navigation_request.cc, FrameFetchContext, and WorkerFetchContext.
void AddAdditionalRequestHeaders(network::ResourceRequest* resource_request,
BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// TODO(nhiroki): Return early when the request is neither HTTP nor HTTPS
// (i.e., Blob URL or Data URL). This should be checked by
// SchemeIsHTTPOrHTTPS(), but currently cross-origin workers on extensions
// are allowed and the check doesn't work well. See https://crbug.com/867302.
// Set the "Accept" header.
resource_request->headers.SetHeaderIfMissing(
net::HttpRequestHeaders::kAccept, network::kDefaultAcceptHeaderValue);
blink::RendererPreferences renderer_preferences;
GetContentClient()->browser()->UpdateRendererPreferencesForWorker(
browser_context, &renderer_preferences);
UpdateAdditionalHeadersForBrowserInitiatedRequest(
&resource_request->headers, browser_context,
/*should_update_existing_headers=*/false, renderer_preferences,
/*is_for_worker_script=*/true);
}
void DidCreateScriptLoader(
WorkerScriptFetcher::CompletionCallback callback,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories,
const network::mojom::ClientSecurityStatePtr& client_security_state,
std::optional<GlobalRenderFrameHostId> ancestor_render_frame_host_id,
const GURL& initial_request_url,
blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
SubresourceLoaderParams subresource_loader_params,
const network::URLLoaderCompletionStatus* completion_status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_NE(main_script_load_params.is_null(), completion_status == nullptr);
DCHECK(!(main_script_load_params.is_null() &&
subresource_loader_params.controller_service_worker_info));
TRACE_EVENT("loading", "DidCreateScriptLoader");
// Prepare the controller service worker info to pass to the renderer.
blink::mojom::ControllerServiceWorkerInfoPtr controller;
base::WeakPtr<ServiceWorkerObjectHost> controller_service_worker_object_host;
if (subresource_loader_params.controller_service_worker_info) {
controller =
std::move(subresource_loader_params.controller_service_worker_info);
controller_service_worker_object_host =
subresource_loader_params.controller_service_worker_object_host;
}
// Figure out the final response URL.
GURL final_response_url;
network::mojom::IPAddressSpace response_address_space =
network::mojom::IPAddressSpace::kUnknown;
// The load succeeded iff `main_script_load_params` is not nullptr.
if (main_script_load_params) {
final_response_url = WorkerScriptFetcher::DetermineFinalResponseUrl(
initial_request_url, main_script_load_params.get());
response_address_space = network::CalculateResourceAddressSpace(
final_response_url,
main_script_load_params->response_head->remote_endpoint);
} else if (completion_status->cors_error_status) {
response_address_space =
completion_status->cors_error_status->resource_address_space;
if (response_address_space == network::mojom::IPAddressSpace::kUnknown) {
response_address_space =
completion_status->cors_error_status->target_address_space;
}
}
if (client_security_state && ancestor_render_frame_host_id) {
// Attempt to log the private network access on the ancestor RFHI, if still
// alive. There is no content/ API to log a `WebFeature` use for a worker.
auto* ancestor_render_frame_host =
RenderFrameHostImpl::FromID(*ancestor_render_frame_host_id);
if (ancestor_render_frame_host &&
network::IsLessPublicAddressSpace(
response_address_space, client_security_state->ip_address_space)) {
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
ancestor_render_frame_host,
blink::mojom::WebFeature::kPrivateNetworkAccessFetchedWorkerScript);
}
}
std::move(callback).Run(
std::move(subresource_loader_factories),
std::move(main_script_load_params), std::move(controller),
std::move(controller_service_worker_object_host), final_response_url);
}
bool ShouldCreateWebUILoader(RenderFrameHostImpl* creator_render_frame_host) {
if (!creator_render_frame_host)
return false;
if (creator_render_frame_host->GetWebUI() == nullptr)
return false;
auto requesting_scheme =
creator_render_frame_host->GetLastCommittedOrigin().scheme();
if (requesting_scheme == kChromeUIScheme)
return true;
if (requesting_scheme == kChromeUIUntrustedScheme)
return true;
if (requesting_scheme == kChromeDevToolsScheme)
return true;
return false;
}
} // namespace
void WorkerScriptFetcher::CreateAndStart(
int worker_process_id,
const DedicatedOrSharedWorkerToken& worker_token,
const GURL& initial_request_url,
RenderFrameHostImpl* ancestor_render_frame_host,
RenderFrameHostImpl* creator_render_frame_host,
const net::SiteForCookies& site_for_cookies,
const url::Origin& request_initiator,
const blink::StorageKey& request_initiator_storage_key,
const net::IsolationInfo& trusted_isolation_info,
network::mojom::ClientSecurityStatePtr client_security_state,
network::mojom::CredentialsMode credentials_mode,
blink::mojom::FetchClientSettingsObjectPtr
outside_fetch_client_settings_object,
network::mojom::RequestDestination request_destination,
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
ServiceWorkerMainResourceHandle* service_worker_handle,
scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_override,
StoragePartitionImpl* storage_partition,
const std::string& storage_domain,
ukm::SourceId worker_source_id,
DevToolsAgentHostImpl* devtools_agent_host,
const base::UnguessableToken& devtools_worker_token,
bool require_cross_site_request_for_cookies,
bool has_storage_access,
CompletionCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(client_security_state);
DCHECK(storage_partition);
DCHECK(devtools_agent_host);
DCHECK(request_destination == network::mojom::RequestDestination::kWorker ||
request_destination ==
network::mojom::RequestDestination::kSharedWorker)
<< static_cast<int>(request_destination);
BrowserContext* browser_context = storage_partition->browser_context();
if (!browser_context || browser_context->ShutdownStarted()) {
// The browser is shutting down. Just drop this request.
return;
}
bool constructor_uses_file_url =
request_initiator.scheme() == url::kFileScheme;
// TODO(crbug.com/41472712): Filesystem URL support on shared workers
// are now broken.
bool filesystem_url_support =
request_destination == network::mojom::RequestDestination::kWorker;
// Set up the factory bundle for non-NetworkService URLs, e.g.,
// chrome-extension:// URLs. One factory bundle is consumed by the browser
// for WorkerScriptLoaderFactory, and one is sent to the renderer for
// subresource loading.
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
factory_bundle_for_browser = CreateFactoryBundle(
LoaderType::kMainResource, worker_process_id, storage_partition,
storage_domain, constructor_uses_file_url, filesystem_url_support,
creator_render_frame_host, request_initiator_storage_key);
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories = CreateFactoryBundle(
LoaderType::kSubResource, worker_process_id, storage_partition,
storage_domain, constructor_uses_file_url, filesystem_url_support,
creator_render_frame_host, request_initiator_storage_key);
// Create a resource request for initiating worker script fetch from the
// browser process.
std::unique_ptr<network::ResourceRequest> resource_request;
// Determine the referrer for the worker script request based on the spec.
// https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
Referrer sanitized_referrer = Referrer::SanitizeForRequest(
initial_request_url,
Referrer(outside_fetch_client_settings_object->outgoing_referrer,
outside_fetch_client_settings_object->referrer_policy));
resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = initial_request_url;
resource_request->site_for_cookies = site_for_cookies;
resource_request->request_initiator = request_initiator;
resource_request->referrer = sanitized_referrer.url,
resource_request->referrer_policy = Referrer::ReferrerPolicyForUrlRequest(
outside_fetch_client_settings_object->referrer_policy);
resource_request->destination = request_destination;
resource_request->credentials_mode = credentials_mode;
// To be used for the first party context check.
resource_request->trusted_params = network::ResourceRequest::TrustedParams();
resource_request->trusted_params->isolation_info =
ancestor_render_frame_host->GetStorageKey().ToPartialNetIsolationInfo();
resource_request->has_storage_access = has_storage_access;
// For a classic worker script request:
// https://html.spec.whatwg.org/C/#fetch-a-classic-worker-script
// Step 1: "Let request be a new request whose ..., mode is "same-origin",
// ..."
//
// For a module worker script request:
// https://html.spec.whatwg.org/C/#fetch-a-single-module-script
// Step 6: "If destination is "worker" or "sharedworker" and the top-level
// module fetch flag is set, then set request's mode to "same-origin"."
resource_request->mode = network::mojom::RequestMode::kSameOrigin;
switch (request_destination) {
case network::mojom::RequestDestination::kWorker:
resource_request->resource_type =
static_cast<int>(blink::mojom::ResourceType::kWorker);
break;
case network::mojom::RequestDestination::kSharedWorker:
resource_request->resource_type =
static_cast<int>(blink::mojom::ResourceType::kSharedWorker);
break;
default:
NOTREACHED() << static_cast<int>(request_destination);
break;
}
// Upgrade the request to an a priori authenticated URL, if appropriate.
// https://w3c.github.io/webappsec-upgrade-insecure-requests/#upgrade-request
resource_request->upgrade_if_insecure =
outside_fetch_client_settings_object->insecure_requests_policy ==
blink::mojom::InsecureRequestsPolicy::kUpgrade;
AddAdditionalRequestHeaders(resource_request.get(), browser_context);
// Notify that the request for fetching the main worker script is about to
// start to DevTools. It fires `Network.onRequestWillBeSent` event. For
// dedicated workers, `creator_render_frame_host` can be null when a worker
// is nested. So reports to DevTools in the ancestor's frame instead. For
// shared workers, `ancestor_render_frame_host` and
// `creator_render_frame_host` are always same.
devtools_instrumentation::OnWorkerMainScriptRequestWillBeSent(
FrameTreeNode::From(ancestor_render_frame_host), devtools_worker_token,
*resource_request);
WorkerScriptFetcher::CreateScriptLoader(
worker_process_id, worker_token, initial_request_url,
ancestor_render_frame_host, creator_render_frame_host,
trusted_isolation_info, std::move(client_security_state),
std::move(resource_request), std::move(factory_bundle_for_browser),
std::move(subresource_loader_factories),
std::move(service_worker_context), service_worker_handle,
std::move(blob_url_loader_factory),
std::move(url_loader_factory_override), worker_source_id,
devtools_agent_host, devtools_worker_token,
require_cross_site_request_for_cookies, std::move(callback));
}
void WorkerScriptFetcher::CreateScriptLoader(
int worker_process_id,
const DedicatedOrSharedWorkerToken& worker_token,
const GURL& initial_request_url,
RenderFrameHostImpl* ancestor_render_frame_host,
RenderFrameHostImpl* creator_render_frame_host,
const net::IsolationInfo& trusted_isolation_info,
network::mojom::ClientSecurityStatePtr client_security_state,
std::unique_ptr<network::ResourceRequest> resource_request,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
factory_bundle_for_browser_info,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories,
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
ServiceWorkerMainResourceHandle* service_worker_handle,
scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_override,
ukm::SourceId worker_source_id,
DevToolsAgentHostImpl* devtools_agent_host,
const base::UnguessableToken& devtools_worker_token,
bool require_cross_site_request_for_cookies,
WorkerScriptFetcher::CompletionCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(devtools_agent_host);
DCHECK(client_security_state);
TRACE_EVENT("loading", "WorkerScriptFetcher::CreateScriptLoader");
RenderProcessHost* factory_process =
RenderProcessHost::FromID(worker_process_id);
DCHECK(factory_process); // Checked by callers of the Start method.
BrowserContext* browser_context = factory_process->GetBrowserContext();
DCHECK(browser_context); // Checked in the Start method.
// Do not enforce COEP on the main script fetch.
client_security_state->cross_origin_embedder_policy =
network::CrossOriginEmbedderPolicy();
// Create the URL loader factory for WorkerScriptLoaderFactory to use to load
// the main script.
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
if (blob_url_loader_factory) {
// If we have a blob_url_loader_factory just use that directly rather than
// creating a new URLLoaderFactoryBundle.
url_loader_factory = std::move(blob_url_loader_factory);
} else if (url_loader_factory_override) {
// For unit tests.
url_loader_factory = std::move(url_loader_factory_override);
} else {
// Add the default factory to the bundle for browser.
DCHECK(factory_bundle_for_browser_info);
mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
url_loader_network_observer;
mojo::PendingRemote<network::mojom::DevToolsObserver> devtools_observer;
// If we have a |creator_render_frame_host| associate the load with that
// RenderFrameHostImpl. Note that |factory_process| may be different than
// the |creator_render_frame_host|'s RenderProcessHost.
if (creator_render_frame_host) {
url_loader_network_observer =
factory_process->GetStoragePartition()
->CreateURLLoaderNetworkObserverForFrame(
creator_render_frame_host->GetProcess()->GetID(),
creator_render_frame_host->GetRoutingID());
devtools_observer = NetworkServiceDevToolsObserver::MakeSelfOwned(
creator_render_frame_host->GetDevToolsFrameToken().ToString());
}
const url::Origin& request_initiator = *resource_request->request_initiator;
// TODO(crbug.com/40122194): Pass the Mojo remote which is connected
// to the COEP reporter in DedicatedWorkerHost.
network::mojom::URLLoaderFactoryParamsPtr factory_params =
URLLoaderFactoryParamsHelper::CreateForWorker(
factory_process, request_initiator, trusted_isolation_info,
/*coep_reporter=*/mojo::NullRemote(),
std::move(url_loader_network_observer),
std::move(devtools_observer), client_security_state.Clone(),
/*debug_tag=*/"CreateScriptLoader",
require_cross_site_request_for_cookies);
// We are sure the URLLoaderFactory made with the param is only used within
// `WorkerScriptFetcher` in the browser process. We can mark this trusted
// safely.
factory_params->is_trusted = true;
bool bypass_redirect_checks = false;
// TODO(crbug.com/40139181): The UKM ID could be computed.
constexpr ukm::SourceIdObj source_id = ukm::kInvalidSourceIdObj;
url_loader_factory::CreateAndConnectToPendingReceiver(
factory_bundle_for_browser_info->pending_default_factory()
.InitWithNewPipeAndPassReceiver(),
ContentBrowserClient::URLLoaderFactoryType::kWorkerMainResource,
url_loader_factory::TerminalParams::ForNetworkContext(
factory_process->GetStoragePartition()->GetNetworkContext(),
std::move(factory_params),
url_loader_factory::HeaderClientOption::kAllow,
url_loader_factory::FactoryOverrideOption::kAllow),
url_loader_factory::ContentClientParams(
browser_context, creator_render_frame_host,
factory_process->GetID(), request_initiator, net::IsolationInfo(),
source_id, &bypass_redirect_checks),
devtools_instrumentation::WillCreateURLLoaderFactoryParams::
ForWorkerMainScript(devtools_agent_host, devtools_worker_token));
factory_bundle_for_browser_info->set_bypass_redirect_checks(
bypass_redirect_checks);
url_loader_factory = base::MakeRefCounted<blink::URLLoaderFactoryBundle>(
std::move(factory_bundle_for_browser_info));
}
// Start loading a web worker main script.
// TODO(nhiroki): Figure out what we should do in |wc_getter| for loading web
// worker's main script. Returning the WebContents of the closest ancestor's
// frame is a possible option, but it doesn't work when a shared worker
// creates a dedicated worker after the closest ancestor's frame is gone. The
// frame tree node ID has the same issue.
base::RepeatingCallback<WebContents*()> wc_getter =
base::BindRepeating([]() -> WebContents* { return nullptr; });
std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles =
CreateContentBrowserURLLoaderThrottles(
*resource_request, browser_context, wc_getter,
nullptr /* navigation_ui_data */, RenderFrameHost::kNoFrameTreeNodeId,
/*navigation_id=*/std::nullopt);
// Create a BrowserContext getter using |service_worker_context|.
// This context is aware of shutdown and safely returns a nullptr
// instead of a destroyed BrowserContext in that case.
auto browser_context_getter =
base::BindRepeating(&ServiceWorkerContextWrapper::browser_context,
std::move(service_worker_context));
std::optional<GlobalRenderFrameHostId> ancestor_render_frame_host_id;
if (ancestor_render_frame_host) {
ancestor_render_frame_host_id = ancestor_render_frame_host->GetGlobalId();
}
// This fetcher will delete itself. See the class level comment.
auto* script_fetcher = new WorkerScriptFetcher(
std::make_unique<WorkerScriptLoaderFactory>(
worker_process_id, worker_token, trusted_isolation_info,
service_worker_handle, browser_context_getter,
std::move(url_loader_factory), worker_source_id),
std::move(resource_request),
base::BindOnce(DidCreateScriptLoader, std::move(callback),
std::move(subresource_loader_factories),
std::move(client_security_state),
std::move(ancestor_render_frame_host_id),
initial_request_url));
script_fetcher->Start(std::move(throttles));
}
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
WorkerScriptFetcher::CreateFactoryBundle(
LoaderType loader_type,
int worker_process_id,
StoragePartitionImpl* storage_partition,
const std::string& storage_domain,
bool file_support,
bool filesystem_url_support,
RenderFrameHostImpl* creator_render_frame_host,
const blink::StorageKey& request_initiator_storage_key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
non_network_factories.emplace(url::kDataScheme,
DataURLLoaderFactory::Create());
if (filesystem_url_support) {
// TODO(https://crbug.com/986188): Pass ChildProcessHost::kInvalidUniqueID
// instead of valid `worker_process_id` for `factory_bundle_for_browser`
// once CanCommitURL-like check is implemented in PlzWorker.
non_network_factories.emplace(
url::kFileSystemScheme,
CreateFileSystemURLLoaderFactory(
worker_process_id, RenderFrameHost::kNoFrameTreeNodeId,
storage_partition->GetFileSystemContext(), storage_domain,
request_initiator_storage_key));
}
if (file_support) {
// USER_VISIBLE because worker script fetch may affect the UI.
base::TaskPriority file_factory_priority = base::TaskPriority::USER_VISIBLE;
non_network_factories.emplace(
url::kFileScheme, FileURLLoaderFactory::Create(
storage_partition->browser_context()->GetPath(),
storage_partition->browser_context()
->GetSharedCorsOriginAccessList(),
file_factory_priority));
}
switch (loader_type) {
case LoaderType::kMainResource:
GetContentClient()
->browser()
->RegisterNonNetworkWorkerMainResourceURLLoaderFactories(
storage_partition->browser_context(), &non_network_factories);
break;
case LoaderType::kSubResource:
GetContentClient()
->browser()
->RegisterNonNetworkSubresourceURLLoaderFactories(
worker_process_id, MSG_ROUTING_NONE,
request_initiator_storage_key.origin(), &non_network_factories);
break;
}
// Create WebUI loader for chrome://, chrome-untrusted://, or devtools://
// workers from WebUI frames of the same scheme.
if (ShouldCreateWebUILoader(creator_render_frame_host)) {
auto requesting_scheme =
creator_render_frame_host->GetLastCommittedOrigin().scheme();
non_network_factories.emplace(
requesting_scheme,
CreateWebUIURLLoaderFactory(
creator_render_frame_host, requesting_scheme,
/*allowed_hosts=*/base::flat_set<std::string>()));
}
auto factory_bundle =
std::make_unique<blink::PendingURLLoaderFactoryBundle>();
for (auto& pair : non_network_factories) {
const std::string& scheme = pair.first;
mojo::PendingRemote<network::mojom::URLLoaderFactory>& pending_remote =
pair.second;
factory_bundle->pending_scheme_specific_factories().emplace(
scheme, std::move(pending_remote));
}
return factory_bundle;
}
GURL WorkerScriptFetcher::DetermineFinalResponseUrl(
const GURL& initial_request_url,
blink::mojom::WorkerMainScriptLoadParams* main_script_load_params) {
DCHECK(main_script_load_params);
network::mojom::URLResponseHead* url_response_head =
main_script_load_params->response_head.get();
// First check the URL list from the service worker.
if (!url_response_head->url_list_via_service_worker.empty()) {
DCHECK(url_response_head->was_fetched_via_service_worker);
return url_response_head->url_list_via_service_worker.back();
}
// Then check the list of redirects.
if (!main_script_load_params->redirect_infos.empty())
return main_script_load_params->redirect_infos.back().new_url;
// No redirection happened. The initial request URL was used for the response.
return initial_request_url;
}
WorkerScriptFetcher::WorkerScriptFetcher(
std::unique_ptr<WorkerScriptLoaderFactory> script_loader_factory,
std::unique_ptr<network::ResourceRequest> resource_request,
CreateAndStartCallback callback)
: script_loader_factory_(std::move(script_loader_factory)),
request_id_(GlobalRequestID::MakeBrowserInitiated().request_id),
resource_request_(std::move(resource_request)),
callback_(std::move(callback)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
WorkerScriptFetcher::~WorkerScriptFetcher() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void WorkerScriptFetcher::Start(
std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto shared_url_loader_factory =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
script_loader_factory_.get());
url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart(
std::move(shared_url_loader_factory), std::move(throttles), request_id_,
network::mojom::kURLLoadOptionNone, resource_request_.get(), this,
kWorkerScriptLoadTrafficAnnotation,
base::SingleThreadTaskRunner::GetCurrentDefault());
}
void WorkerScriptFetcher::OnReceiveEarlyHints(
network::mojom::EarlyHintsPtr early_hints) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void WorkerScriptFetcher::OnReceiveResponse(
network::mojom::URLResponseHeadPtr response_head,
mojo::ScopedDataPipeConsumerHandle body,
std::optional<mojo_base::BigBuffer> cached_metadata) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!cached_metadata);
if (!body)
return;
CHECK(!main_script_load_params_);
CHECK(url_loader_);
main_script_load_params_ = blink::mojom::WorkerMainScriptLoadParams::New();
main_script_load_params_->request_id = request_id_;
main_script_load_params_->response_head = std::move(response_head);
main_script_load_params_->response_body = std::move(body);
// The main script was served by a request interceptor or the default
// network loader.
main_script_load_params_->url_loader_client_endpoints = url_loader_->Unbind();
main_script_load_params_->redirect_infos = std::move(redirect_infos_);
main_script_load_params_->redirect_response_heads =
std::move(redirect_response_heads_);
subresource_loader_params_ =
script_loader_factory_->GetScriptLoader()->TakeSubresourceLoaderParams();
// Currently `parsed_headers` is null when FileURLLoader is used.
if (main_script_load_params_->response_head->parsed_headers) {
std::move(callback_).Run(std::move(main_script_load_params_),
std::move(subresource_loader_params_),
nullptr /* completion_status */);
delete this;
return;
}
GetNetworkService()->ParseHeaders(
resource_request_->url, main_script_load_params_->response_head->headers,
base::BindOnce(&WorkerScriptFetcher::DidParseHeaders,
weak_factory_.GetWeakPtr()));
}
void WorkerScriptFetcher::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr response_head) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
redirect_infos_.push_back(redirect_info);
redirect_response_heads_.push_back(std::move(response_head));
url_loader_->FollowRedirect({}, /* removed_headers */
{}, /* modified_headers */
{} /* modified_cors_exempt_headers */);
}
void WorkerScriptFetcher::OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback callback) {
NOTREACHED();
}
void WorkerScriptFetcher::OnTransferSizeUpdated(int32_t transfer_size_diff) {
network::RecordOnTransferSizeUpdatedUMA(
network::OnTransferSizeUpdatedFrom::kWorkerScriptFetcher);
NOTREACHED();
}
void WorkerScriptFetcher::OnComplete(
const network::URLLoaderCompletionStatus& status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status.error_code == net::OK) {
// It's possible to reach here when the `response_head_` doesn't have a
// `parsed_headers` and ask NetworkService to parse headers in
// OnReceiveResponse(). DidParseHeaders() will be called eventually
// and `this` will be deleted in it.
return;
}
std::move(callback_).Run(/*main_script_load_params=*/nullptr,
/*subresource_loader_params=*/{}, &status);
delete this;
}
void WorkerScriptFetcher::DidParseHeaders(
network::mojom::ParsedHeadersPtr parsed_headers) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(main_script_load_params_);
main_script_load_params_->response_head->parsed_headers =
std::move(parsed_headers);
std::move(callback_).Run(std::move(main_script_load_params_),
std::move(subresource_loader_params_),
nullptr /* completion_status */);
delete this;
}
} // namespace content