| // 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/download/resource_downloader.h" |
| |
| #include "content/browser/blob_storage/blob_url_loader_factory.h" |
| #include "content/browser/download/download_utils.h" |
| #include "content/common/throttling_url_loader.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/browser_side_navigation_policy.h" |
| #include "storage/browser/fileapi/file_system_context.h" |
| |
| namespace content { |
| |
| // This class is only used for providing the WebContents to DownloadItemImpl. |
| class RequestHandle : public DownloadRequestHandleInterface { |
| public: |
| RequestHandle(int render_process_id, |
| int render_frame_id, |
| int frame_tree_node_id) |
| : render_process_id_(render_process_id), |
| render_frame_id_(render_frame_id), |
| frame_tree_node_id_(frame_tree_node_id) {} |
| RequestHandle(RequestHandle&& other) |
| : render_process_id_(other.render_process_id_), |
| render_frame_id_(other.render_frame_id_), |
| frame_tree_node_id_(other.frame_tree_node_id_) {} |
| |
| // DownloadRequestHandleInterface |
| WebContents* GetWebContents() const override { |
| DCHECK(IsBrowserSideNavigationEnabled()); |
| |
| WebContents* web_contents = WebContents::FromRenderFrameHost( |
| RenderFrameHost::FromID(render_process_id_, render_frame_id_)); |
| if (web_contents) |
| return web_contents; |
| |
| return WebContents::FromFrameTreeNodeId(frame_tree_node_id_); |
| } |
| |
| DownloadManager* GetDownloadManager() const override { return nullptr; } |
| void PauseRequest() const override {} |
| void ResumeRequest() const override {} |
| void CancelRequest(bool user_cancel) const override {} |
| |
| private: |
| int render_process_id_; |
| int render_frame_id_; |
| int frame_tree_node_id_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RequestHandle); |
| }; |
| |
| // static |
| std::unique_ptr<ResourceDownloader> ResourceDownloader::BeginDownload( |
| base::WeakPtr<UrlDownloadHandler::Delegate> delegate, |
| std::unique_ptr<DownloadUrlParameters> params, |
| std::unique_ptr<ResourceRequest> request, |
| scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter, |
| scoped_refptr<storage::FileSystemContext> file_system_context, |
| uint32_t download_id, |
| bool is_parallel_request) { |
| mojom::URLLoaderFactoryPtr* factory = |
| params->url().SchemeIs(url::kBlobScheme) |
| ? url_loader_factory_getter->GetBlobFactory() |
| : url_loader_factory_getter->GetNetworkFactory(); |
| auto downloader = std::make_unique<ResourceDownloader>( |
| delegate, std::move(request), |
| std::make_unique<DownloadSaveInfo>(params->GetSaveInfo()), download_id, |
| params->guid(), is_parallel_request, params->is_transient(), |
| params->fetch_error_body()); |
| downloader->Start(factory, file_system_context, std::move(params)); |
| return downloader; |
| } |
| |
| // static |
| std::unique_ptr<ResourceDownloader> |
| ResourceDownloader::InterceptNavigationResponse( |
| base::WeakPtr<UrlDownloadHandler::Delegate> delegate, |
| std::unique_ptr<ResourceRequest> resource_request, |
| const scoped_refptr<ResourceResponse>& response, |
| mojo::ScopedDataPipeConsumerHandle consumer_handle, |
| const SSLStatus& ssl_status, |
| int frame_tree_node_id, |
| std::unique_ptr<ThrottlingURLLoader> url_loader, |
| std::vector<GURL> url_chain, |
| base::Optional<network::URLLoaderCompletionStatus> status) { |
| auto downloader = std::make_unique<ResourceDownloader>( |
| delegate, std::move(resource_request), |
| std::make_unique<DownloadSaveInfo>(), content::DownloadItem::kInvalidId, |
| std::string(), false, false, false); |
| downloader->InterceptResponse( |
| std::move(url_loader), response, std::move(consumer_handle), ssl_status, |
| frame_tree_node_id, std::move(url_chain), std::move(status)); |
| return downloader; |
| } |
| |
| ResourceDownloader::ResourceDownloader( |
| base::WeakPtr<UrlDownloadHandler::Delegate> delegate, |
| std::unique_ptr<ResourceRequest> resource_request, |
| std::unique_ptr<DownloadSaveInfo> save_info, |
| uint32_t download_id, |
| std::string guid, |
| bool is_parallel_request, |
| bool is_transient, |
| bool fetch_error_body) |
| : delegate_(delegate), |
| resource_request_(std::move(resource_request)), |
| response_handler_(resource_request_.get(), |
| this, |
| std::move(save_info), |
| is_parallel_request, |
| is_transient, |
| fetch_error_body), |
| blob_client_binding_(&response_handler_), |
| download_id_(download_id), |
| guid_(guid), |
| weak_ptr_factory_(this) {} |
| |
| ResourceDownloader::~ResourceDownloader() = default; |
| |
| void ResourceDownloader::Start( |
| mojom::URLLoaderFactoryPtr* factory, |
| scoped_refptr<storage::FileSystemContext> file_system_context, |
| std::unique_ptr<DownloadUrlParameters> download_url_parameters) { |
| callback_ = download_url_parameters->callback(); |
| if (download_url_parameters->url().SchemeIs(url::kBlobScheme)) { |
| mojom::URLLoaderRequest url_loader_request; |
| mojom::URLLoaderClientPtr client; |
| blob_client_binding_.Bind(mojo::MakeRequest(&client)); |
| BlobURLLoaderFactory::CreateLoaderAndStart( |
| std::move(url_loader_request), *(resource_request_.get()), |
| std::move(client), download_url_parameters->GetBlobDataHandle(), |
| file_system_context.get()); |
| } else { |
| url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( |
| factory->get(), std::vector<std::unique_ptr<URLLoaderThrottle>>(), |
| 0, // routing_id |
| 0, // request_id |
| mojom::kURLLoadOptionSendSSLInfo | mojom::kURLLoadOptionSniffMimeType, |
| *(resource_request_.get()), &response_handler_, |
| download_url_parameters->GetNetworkTrafficAnnotation()); |
| url_loader_->SetPriority(net::RequestPriority::IDLE, |
| 0 /* intra_priority_value */); |
| } |
| } |
| |
| void ResourceDownloader::InterceptResponse( |
| std::unique_ptr<ThrottlingURLLoader> url_loader, |
| const scoped_refptr<ResourceResponse>& response, |
| mojo::ScopedDataPipeConsumerHandle consumer_handle, |
| const SSLStatus& ssl_status, |
| int frame_tree_node_id, |
| std::vector<GURL> url_chain, |
| base::Optional<network::URLLoaderCompletionStatus> status) { |
| url_loader_ = std::move(url_loader); |
| url_loader_->set_forwarding_client(&response_handler_); |
| net::SSLInfo info; |
| info.cert_status = ssl_status.cert_status; |
| frame_tree_node_id_ = frame_tree_node_id; |
| response_handler_.SetURLChain(std::move(url_chain)); |
| response_handler_.OnReceiveResponse(response->head, |
| base::Optional<net::SSLInfo>(info), |
| mojom::DownloadedTempFilePtr()); |
| response_handler_.OnStartLoadingResponseBody(std::move(consumer_handle)); |
| if (status.has_value()) |
| response_handler_.OnComplete(status.value()); |
| } |
| |
| void ResourceDownloader::OnResponseStarted( |
| std::unique_ptr<DownloadCreateInfo> download_create_info, |
| mojom::DownloadStreamHandlePtr stream_handle) { |
| download_create_info->download_id = download_id_; |
| download_create_info->guid = guid_; |
| download_create_info->request_handle.reset(new RequestHandle( |
| resource_request_->origin_pid, resource_request_->render_frame_id, |
| frame_tree_node_id_)); |
| |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::BindOnce(&UrlDownloadHandler::Delegate::OnUrlDownloadStarted, |
| delegate_, std::move(download_create_info), |
| std::make_unique<DownloadManager::InputStream>( |
| std::move(stream_handle)), |
| callback_)); |
| } |
| |
| void ResourceDownloader::OnReceiveRedirect() { |
| url_loader_->FollowRedirect(); |
| } |
| |
| } // namespace content |