| // Copyright 2018 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/worker_host/worker_script_fetcher.h" |
| |
| #include "base/feature_list.h" |
| #include "content/browser/worker_host/worker_script_loader.h" |
| #include "content/browser/worker_host/worker_script_loader_factory.h" |
| #include "content/common/throttling_url_loader.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/common/url_loader_throttle.h" |
| #include "net/traffic_annotation/network_traffic_annotation.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/cpp/resource_response.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 { |
| URLBlacklist { |
| URLBlacklist: { entries: '*' } |
| } |
| } |
| chrome_policy { |
| URLWhitelist { |
| URLWhitelist { } |
| } |
| } |
| } |
| )"); |
| |
| } // namespace |
| |
| void WorkerScriptFetcher::CreateAndStart( |
| std::unique_ptr<WorkerScriptLoaderFactory> script_loader_factory, |
| std::vector<std::unique_ptr<URLLoaderThrottle>> throttles, |
| std::unique_ptr<network::ResourceRequest> resource_request, |
| CreateAndStartCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); |
| // This fetcher will delete itself. See the class level comment. |
| (new WorkerScriptFetcher(std::move(script_loader_factory), |
| std::move(resource_request), std::move(callback))) |
| ->Start(std::move(throttles)); |
| } |
| |
| 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)), |
| resource_request_(std::move(resource_request)), |
| callback_(std::move(callback)), |
| response_url_loader_binding_(this) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| } |
| |
| WorkerScriptFetcher::~WorkerScriptFetcher() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| } |
| |
| void WorkerScriptFetcher::Start( |
| std::vector<std::unique_ptr<URLLoaderThrottle>> throttles) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| auto shared_url_loader_factory = |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| script_loader_factory_.get()); |
| |
| // SharedWorker doesn't have a frame. |
| // TODO(nhiroki): Make the caller pass the frame id to support dedicated |
| // workers (https://crbug.com/906991). |
| int32_t routing_id = MSG_ROUTING_NONE; |
| |
| // NetworkService is not interested in the request ID. |
| int request_id = -1; |
| |
| url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( |
| std::move(shared_url_loader_factory), std::move(throttles), routing_id, |
| request_id, network::mojom::kURLLoadOptionNone, resource_request_.get(), |
| this, kWorkerScriptLoadTrafficAnnotation, |
| base::ThreadTaskRunnerHandle::Get()); |
| } |
| |
| void WorkerScriptFetcher::OnReceiveResponse( |
| const network::ResourceResponseHead& head) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| base::WeakPtr<WorkerScriptLoader> script_loader = |
| script_loader_factory_->GetScriptLoader(); |
| if (script_loader && script_loader->default_loader_used_) { |
| // If the default network loader was used to handle the URL load request we |
| // need to see if the request interceptors want to potentially create a new |
| // loader for the response, e.g. AppCache's fallback. |
| DCHECK(!response_url_loader_); |
| network::mojom::URLLoaderClientRequest response_client_request; |
| if (script_loader->MaybeCreateLoaderForResponse(head, &response_url_loader_, |
| &response_client_request, |
| url_loader_.get())) { |
| DCHECK(response_url_loader_); |
| response_url_loader_binding_.Bind(std::move(response_client_request)); |
| subresource_loader_params_ = script_loader->TakeSubresourceLoaderParams(); |
| url_loader_.reset(); |
| // OnReceiveResponse() will be called again. |
| return; |
| } |
| } |
| |
| blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params = |
| blink::mojom::WorkerMainScriptLoadParams::New(); |
| |
| // Fill in params for loading worker's main script and subresources. |
| main_script_load_params->response_head = head; |
| if (url_loader_) { |
| // The main script was served by a request interceptor or the default |
| // network loader. |
| DCHECK(!response_url_loader_); |
| main_script_load_params->url_loader_client_endpoints = |
| url_loader_->Unbind(); |
| subresource_loader_params_ = script_loader->TakeSubresourceLoaderParams(); |
| } else { |
| // The main script was served by the default network loader first, and then |
| // a request interceptor created another loader |response_url_loader_| for |
| // serving an alternative response. |
| DCHECK(response_url_loader_); |
| DCHECK(response_url_loader_binding_.is_bound()); |
| main_script_load_params->url_loader_client_endpoints = |
| network::mojom::URLLoaderClientEndpoints::New( |
| response_url_loader_.PassInterface(), |
| response_url_loader_binding_.Unbind()); |
| } |
| |
| for (size_t i = 0; i < redirect_infos_.size(); ++i) { |
| main_script_load_params->redirect_infos.emplace_back(redirect_infos_[i]); |
| main_script_load_params->redirect_response_heads.emplace_back( |
| redirect_response_heads_[i]); |
| } |
| |
| std::move(callback_).Run(std::move(main_script_load_params), |
| std::move(subresource_loader_params_), |
| true /* success */); |
| delete this; |
| } |
| |
| void WorkerScriptFetcher::OnReceiveRedirect( |
| const net::RedirectInfo& redirect_info, |
| const network::ResourceResponseHead& head) { |
| redirect_infos_.push_back(redirect_info); |
| redirect_response_heads_.push_back(head); |
| url_loader_->FollowRedirect({}, /* removed_headers */ |
| {} /* modified_headers */); |
| } |
| |
| void WorkerScriptFetcher::OnUploadProgress(int64_t current_position, |
| int64_t total_size, |
| OnUploadProgressCallback callback) { |
| NOTREACHED(); |
| } |
| |
| void WorkerScriptFetcher::OnReceiveCachedMetadata(mojo_base::BigBuffer data) { |
| NOTREACHED(); |
| } |
| |
| void WorkerScriptFetcher::OnTransferSizeUpdated(int32_t transfer_size_diff) { |
| NOTREACHED(); |
| } |
| |
| void WorkerScriptFetcher::OnStartLoadingResponseBody( |
| mojo::ScopedDataPipeConsumerHandle body) { |
| // Not reached. At this point, the loader and client endpoints must have |
| // been unbound and forwarded to the renderer. |
| NOTREACHED(); |
| } |
| |
| void WorkerScriptFetcher::OnComplete( |
| const network::URLLoaderCompletionStatus& status) { |
| // We can reach here only when loading fails before receiving a response head. |
| DCHECK_NE(net::OK, status.error_code); |
| std::move(callback_).Run(nullptr /* main_script_load_params */, |
| base::nullopt /* subresource_loader_params */, |
| false /* success */); |
| delete this; |
| } |
| |
| } // namespace content |