| 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
|
|
|