Index: content/browser/download/url_downloader.cc |
diff --git a/content/browser/download/url_downloader.cc b/content/browser/download/url_downloader.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fd6d0dfdc4ed8ea9f18c67384974bd240492d5e1 |
--- /dev/null |
+++ b/content/browser/download/url_downloader.cc |
@@ -0,0 +1,233 @@ |
+// Copyright 2013 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/url_downloader.h" |
+ |
+#include "base/bind.h" |
+#include "base/callback.h" |
+#include "content/browser/download/download_create_info.h" |
+#include "content/browser/download/download_interrupt_reasons_impl.h" |
+#include "content/browser/download/download_request_handle.h" |
+#include "content/browser/download/download_request_model.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/download_url_parameters.h" |
+#include "net/url_request/url_request.h" |
+ |
+namespace content { |
+ |
+class UrlDownloader::RequestHandle : public DownloadRequestHandle { |
+ public: |
+ RequestHandle(scoped_ptr<UrlDownloader> downloader); |
+ virtual ~RequestHandle(); |
+ |
+ private: |
+ // DownloadRequestHandle. |
+ virtual void Start(const RequestStartedCallback& callback) OVERRIDE; |
+ virtual void PauseRequest() const OVERRIDE; |
+ virtual void ResumeRequest() const OVERRIDE; |
+ virtual void CancelRequest() const OVERRIDE; |
+ virtual std::string DebugString() const OVERRIDE; |
+ |
+ scoped_ptr<UrlDownloader> downloader_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(RequestHandle); |
+}; |
+ |
+UrlDownloader::RequestHandle::RequestHandle( |
+ scoped_ptr<UrlDownloader> downloader) |
+ : downloader_(downloader.Pass()) {} |
+ |
+UrlDownloader::RequestHandle::~RequestHandle() { |
+ BrowserThread::DeleteSoon( |
+ BrowserThread::IO, FROM_HERE, downloader_.release()); |
+} |
+ |
+void UrlDownloader::RequestHandle::Start( |
+ const RequestStartedCallback& callback) { |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ base::Bind(&UrlDownloader::Start, |
+ base::Unretained(downloader_.get()), |
+ callback)); |
+} |
+ |
+void UrlDownloader::RequestHandle::PauseRequest() const { |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ base::Bind(&UrlDownloader::PauseRequest, |
+ base::Unretained(downloader_.get()))); |
+} |
+ |
+void UrlDownloader::RequestHandle::ResumeRequest() const { |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ base::Bind(&UrlDownloader::ResumeRequest, |
+ base::Unretained(downloader_.get()))); |
+} |
+ |
+void UrlDownloader::RequestHandle::CancelRequest() const { |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ base::Bind(&UrlDownloader::CancelRequest, |
+ base::Unretained(downloader_.get()))); |
+} |
+ |
+std::string UrlDownloader::RequestHandle::DebugString() const { |
+ return std::string(); |
+} |
+ |
+// static |
+scoped_ptr<DownloadRequestHandle> UrlDownloader::CreateDownloadRequest( |
+ scoped_ptr<DownloadUrlParameters> params) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ scoped_ptr<UrlDownloader> downloader(new UrlDownloader(params.Pass())); |
+ scoped_ptr<DownloadRequestHandle> request_handle( |
+ new RequestHandle(downloader.Pass())); |
+ return request_handle.Pass(); |
+} |
+ |
+UrlDownloader::UrlDownloader(scoped_ptr<DownloadUrlParameters> params) |
+ : params_(params.Pass()), |
+ child_process_id_(params_->render_process_host_id()), |
+ is_request_active_(true) { |
+ // Can be created on the UI thread. |
+} |
+ |
+UrlDownloader::~UrlDownloader() {} |
+ |
+void UrlDownloader::Start(const RequestStartedCallback& callback) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ started_callback_ = callback; |
+ request_ = DownloadRequestModel::CreateRequest(*params_, this).Pass(); |
+ if (!request_) { |
+ scoped_ptr<DownloadCreateInfo> empty_create_info; |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(callback, |
+ DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, |
+ base::Passed(&empty_create_info))); |
+ return; |
+ } |
+ |
+ scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); |
+ save_info->file_path = params_->file_path(); |
+ save_info->suggested_name = params_->suggested_name(); |
+ save_info->offset = params_->offset(); |
+ save_info->hash_state = params_->hash_state(); |
+ save_info->prompt_for_save_location = params_->prompt(); |
+ save_info->file_stream = params_->GetFileStream(); |
+ |
+ // TODO(asanka): This request will not have the LOAD_MAYBE_USER_GESTURE flag |
+ // set. This is correct since this is a programmatic download. Make sure that |
+ // this doesn't affect resumption since a download that was considered safe |
+ // before may be considered dangerous on resumption. |
+ |
+ request_model_.reset( |
+ new DownloadRequestModel(request_.get(), save_info.Pass())); |
+ request_->Start(); |
+} |
+ |
+void UrlDownloader::OnReceivedRedirect(net::URLRequest* request, |
+ const GURL& new_url, |
+ bool* defer_redirect) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ if (!DownloadRequestModel::IsRequestAllowed(new_url, child_process_id_)) { |
+ request_model_->OnDownloadInterrupted( |
+ DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST); |
+ request_->Cancel(); |
+ return; |
+ } |
+ request_model_->OnRequestRedirected(new_url); |
+} |
+ |
+void UrlDownloader::OnResponseStarted(net::URLRequest* request) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ if (!request_->status().is_success()) { |
+ DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( |
+ static_cast<net::Error>(request_->status().error()), |
+ DOWNLOAD_INTERRUPT_FROM_NETWORK); |
+ scoped_ptr<DownloadCreateInfo> empty_create_info; |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind( |
+ started_callback_, reason, base::Passed(&empty_create_info))); |
+ request_->Cancel(); |
+ return; |
+ } |
+ |
+ scoped_ptr<DownloadCreateInfo> create_info = |
+ request_model_->OnResponseStarted( |
+ std::string(), |
+ false, |
+ PAGE_TRANSITION_LINK, |
+ base::Bind(&UrlDownloader::ResumeRequest, base::Unretained(this))); |
+ BrowserThread::PostTask(BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(started_callback_, |
+ DOWNLOAD_INTERRUPT_REASON_NONE, |
+ base::Passed(&create_info))); |
+ ReadNextChunk(); |
+} |
+ |
+void UrlDownloader::OnReadCompleted(net::URLRequest* request, int bytes_read) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ HandleCompletedRead(bytes_read); |
+ ReadNextChunk(); |
+} |
+ |
+void UrlDownloader::PauseRequest() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ request_model_->OnPauseRequest(); |
+} |
+ |
+void UrlDownloader::ResumeRequest() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ if (request_model_->OnResumeRequest() == |
+ DownloadRequestModel::READY_TO_READ && |
+ !is_request_active_ && |
+ request_->status().is_success()) |
+ ReadNextChunk(); |
+} |
+ |
+void UrlDownloader::CancelRequest() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ request_->Cancel(); |
+} |
+ |
+void UrlDownloader::ReadNextChunk() { |
+ net::IOBuffer* buffer = NULL; |
+ int buf_size = 0; |
+ int bytes_read = 0; |
+ bool read_succeeded = false; |
+ |
+ if (!is_request_active_) |
+ return; |
+ |
+ do { |
+ request_model_->OnWillRead(&buffer, &buf_size, -1); |
+ DCHECK_LT(0, buf_size); |
+ |
+ // TODO(asanka): Check for re-entrancy issues. Can Read() result in a call |
+ // into OnReadCompleted()? |
+ read_succeeded = request_->Read(buffer, buf_size, &bytes_read); |
+ |
+ // Read returns false if IO is pending or if the read failed. |
+ if (read_succeeded || !request_->status().is_success()) |
+ HandleCompletedRead(bytes_read); |
+ } while(read_succeeded && is_request_active_); |
+} |
+ |
+void UrlDownloader::HandleCompletedRead(int bytes_read) { |
+ DownloadRequestModel::ReadState read_state = |
+ request_model_->OnReadCompleted(bytes_read); |
+ is_request_active_ = (read_state == DownloadRequestModel::READY_TO_READ); |
+ if (bytes_read == 0 || !request_->status().is_success()) { |
+ request_model_->OnResponseCompleted(); |
+ is_request_active_ = false; |
+ } |
+} |
+ |
+} // namespace content |