Index: content/browser/download/download_resource_handler.cc |
=================================================================== |
--- content/browser/download/download_resource_handler.cc (revision 137742) |
+++ content/browser/download/download_resource_handler.cc (working copy) |
@@ -69,9 +69,10 @@ |
started_cb_(started_cb), |
save_info_(save_info), |
buffer_(new content::DownloadBuffer), |
- is_paused_(false), |
last_buffer_size_(0), |
- bytes_read_(0) { |
+ bytes_read_(0), |
+ pause_count_(0), |
+ was_deferred_(false) { |
download_stats::RecordDownloadCount(download_stats::UNTHROTTLED_COUNT); |
} |
@@ -93,7 +94,8 @@ |
// Send the download creation information to the download thread. |
bool DownloadResourceHandler::OnResponseStarted( |
int request_id, |
- content::ResourceResponse* response) { |
+ content::ResourceResponse* response, |
+ bool* defer) { |
VLOG(20) << __FUNCTION__ << "()" << DebugString() |
<< " request_id = " << request_id; |
download_start_time_ = base::TimeTicks::Now(); |
@@ -104,8 +106,8 @@ |
std::string content_disposition; |
request_->GetResponseHeaderByName("content-disposition", |
&content_disposition); |
- set_content_disposition(content_disposition); |
- set_content_length(response->content_length); |
+ SetContentDisposition(content_disposition); |
+ SetContentLength(response->content_length); |
const ResourceRequestInfoImpl* request_info = |
ResourceRequestInfoImpl::ForRequest(request_); |
@@ -127,7 +129,7 @@ |
info->remote_address = request_->GetSocketAddress().host(); |
download_stats::RecordDownloadMimeType(info->mime_type); |
- DownloadRequestHandle request_handle(global_id_.child_id, |
+ DownloadRequestHandle request_handle(this, global_id_.child_id, |
render_view_id_, global_id_.request_id); |
// Get the last modified time and etag. |
@@ -159,19 +161,11 @@ |
info->referrer_charset = request_->context()->referrer_charset(); |
info->save_info = save_info_; |
- |
BrowserThread::PostTask( |
BrowserThread::UI, FROM_HERE, |
base::Bind(&DownloadResourceHandler::StartOnUIThread, this, |
base::Passed(&info), request_handle)); |
- // We can't start saving the data before we create the file on disk and have a |
- // download id. The request will be un-paused in |
- // DownloadFileManager::CreateDownloadFile. |
- ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, |
- global_id_.request_id, |
- true); |
- |
return true; |
} |
@@ -205,7 +199,29 @@ |
} |
// Pass the buffer to the download file writer. |
-bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { |
+bool DownloadResourceHandler::OnReadCompleted(int request_id, int* bytes_read, |
+ bool* defer) { |
+ if (!read_buffer_) { |
+ // Ignore spurious OnReadCompleted! Deferring from OnReadCompleted tells |
+ // the ResourceDispatcherHost that we did not consume the data. |
+ // ResumeDeferredRequest then repeats the last OnReadCompleted call. |
+ // TODO(darin): Fix the ResourceDispatcherHost to avoid this hack! |
+ return true; |
+ } |
+ |
+ if (pause_count_ > 0) { |
+ *defer = was_deferred_ = true; |
+ return true; |
+ } |
+ |
+ if (download_id_ == DownloadId::Invalid()) { |
+ // We can't start saving the data before we create the file on disk and |
+ // have a download id. The request will be un-paused in |
+ // DownloadFileManager::CreateDownloadFile. |
+ *defer = was_deferred_ = true; |
+ return true; |
+ } |
+ |
base::TimeTicks now(base::TimeTicks::Now()); |
if (!last_read_time_.is_null()) { |
double seconds_since_last_read = (now - last_read_time_).InSecondsF(); |
@@ -240,8 +256,10 @@ |
// We schedule a pause outside of the read loop if there is too much file |
// writing work to do. |
- if (vector_size > kLoadsToWrite) |
- StartPauseTimer(); |
+ if (vector_size > kLoadsToWrite) { |
+ *defer = was_deferred_ = true; |
+ CheckWriteProgressLater(); |
+ } |
return true; |
} |
@@ -355,33 +373,41 @@ |
} |
DownloadId download_id = download_manager->delegate()->GetNextId(); |
info->download_id = download_id; |
+ |
+ // NOTE: StartDownload triggers creation of the download destination file |
+ // that will hold the downloaded data. SetDownloadID unblocks the |
+ // DownloadResourceHandler to begin forwarding network data to the download |
+ // destination file. The sequence of these two steps is critical as creation |
+ // of the downloaded destination file has to happen before we attempt to |
+ // append data to it. Both of those operations happen on the FILE thread. |
+ |
+ download_file_manager_->StartDownload(info.release(), handle); |
+ |
BrowserThread::PostTask( |
BrowserThread::IO, FROM_HERE, |
- base::Bind(&DownloadResourceHandler::set_download_id, this, |
- info->download_id)); |
- // It's safe to continue on with download initiation before we have |
- // confirmation that that download_id_ has been set on the IO thread, as any |
- // messages generated by the UI thread that affect the IO thread will be |
- // behind the message posted above. |
- download_file_manager_->StartDownload(info.release(), handle); |
+ base::Bind(&DownloadResourceHandler::SetDownloadID, this, |
+ download_id)); |
+ |
CallStartedCB(download_id, net::OK); |
} |
-void DownloadResourceHandler::set_download_id(content::DownloadId id) { |
+void DownloadResourceHandler::SetDownloadID(content::DownloadId id) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ |
download_id_ = id; |
+ MaybeResumeRequest(); |
} |
// If the content-length header is not present (or contains something other |
// than numbers), the incoming content_length is -1 (unknown size). |
// Set the content length to 0 to indicate unknown size to DownloadManager. |
-void DownloadResourceHandler::set_content_length(const int64& content_length) { |
+void DownloadResourceHandler::SetContentLength(const int64& content_length) { |
content_length_ = 0; |
if (content_length > 0) |
content_length_ = content_length; |
} |
-void DownloadResourceHandler::set_content_disposition( |
+void DownloadResourceHandler::SetContentDisposition( |
const std::string& content_disposition) { |
content_disposition_ = content_disposition; |
} |
@@ -390,34 +416,36 @@ |
if (!buffer_.get()) |
return; // The download completed while we were waiting to run. |
- size_t contents_size = buffer_->size(); |
+ if (buffer_->size() > kLoadsToWrite) { |
+ // We'll come back later and see if it's okay to unpause the request. |
+ CheckWriteProgressLater(); |
+ return; |
+ } |
- bool should_pause = contents_size > kLoadsToWrite; |
+ MaybeResumeRequest(); |
+} |
- // We'll come back later and see if it's okay to unpause the request. |
- if (should_pause) |
- StartPauseTimer(); |
+void DownloadResourceHandler::PauseRequest() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
- if (is_paused_ != should_pause) { |
- ResourceDispatcherHostImpl::Get()->PauseRequest(global_id_.child_id, |
- global_id_.request_id, |
- should_pause); |
- is_paused_ = should_pause; |
- } |
+ ++pause_count_; |
} |
-DownloadResourceHandler::~DownloadResourceHandler() { |
- // This won't do anything if the callback was called before. |
- // If it goes through, it will likely be because OnWillStart() returned |
- // false somewhere in the chain of resource handlers. |
- CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); |
+void DownloadResourceHandler::ResumeRequest() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ DCHECK_LT(0, pause_count_); |
+ |
+ --pause_count_; |
+ MaybeResumeRequest(); |
} |
-void DownloadResourceHandler::StartPauseTimer() { |
- if (!pause_timer_.IsRunning()) |
- pause_timer_.Start(FROM_HERE, |
- base::TimeDelta::FromMilliseconds(kThrottleTimeMs), this, |
- &DownloadResourceHandler::CheckWriteProgress); |
+void DownloadResourceHandler::CancelRequest() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ |
+ ResourceDispatcherHostImpl::Get()->CancelRequest( |
+ global_id_.child_id, |
+ global_id_.request_id, |
+ false); |
} |
std::string DownloadResourceHandler::DebugString() const { |
@@ -440,3 +468,37 @@ |
render_view_id_, |
save_info_.file_path.value().c_str()); |
} |
+ |
+DownloadResourceHandler::~DownloadResourceHandler() { |
+ // This won't do anything if the callback was called before. |
+ // If it goes through, it will likely be because OnWillStart() returned |
+ // false somewhere in the chain of resource handlers. |
+ CallStartedCB(download_id_, net::ERR_ACCESS_DENIED); |
+} |
+ |
+void DownloadResourceHandler::CheckWriteProgressLater() { |
+ if (!check_write_progress_timer_.IsRunning()) { |
+ check_write_progress_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kThrottleTimeMs), |
+ this, |
+ &DownloadResourceHandler::CheckWriteProgress); |
+ } |
+} |
+ |
+void DownloadResourceHandler::MaybeResumeRequest() { |
+ if (!was_deferred_) |
+ return; |
+ |
+ if (pause_count_ > 0) |
+ return; |
+ if (download_id_ == DownloadId::Invalid()) |
+ return; |
+ if (buffer_.get() && (buffer_->size() > kLoadsToWrite)) |
+ return; |
+ |
+ was_deferred_ = false; |
+ ResourceDispatcherHostImpl::Get()->ResumeDeferredRequest( |
+ global_id_.child_id, |
+ global_id_.request_id); |
+} |