Chromium Code Reviews| Index: content/browser/download/download_item_impl.cc |
| diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc |
| index ebef25349cd4f290f3437aae2158e8daa11d9bd5..285a40e3ab595a8854dc18956b06f1e15aa2039c 100644 |
| --- a/content/browser/download/download_item_impl.cc |
| +++ b/content/browser/download/download_item_impl.cc |
| @@ -170,9 +170,9 @@ DownloadItemImpl::DownloadItemImpl( |
| const net::BoundNetLog& bound_net_log) |
| : is_save_package_download_(false), |
| download_id_(download_id), |
| - target_disposition_( |
| - (info.save_info->prompt_for_save_location) ? |
| - TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE), |
| + target_disposition_((info.save_info->prompt_for_save_location) |
| + ? TARGET_DISPOSITION_PROMPT |
| + : TARGET_DISPOSITION_OVERWRITE), |
| url_chain_(info.url_chain), |
| referrer_url_(info.referrer_url), |
| suggested_filename_(base::UTF16ToUTF8(info.save_info->suggested_name)), |
| @@ -201,7 +201,7 @@ DownloadItemImpl::DownloadItemImpl( |
| auto_opened_(false), |
| is_temporary_(!info.save_info->file_path.empty()), |
| all_data_saved_(false), |
| - destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE), |
| + destination_error_(DOWNLOAD_INTERRUPT_REASON_NONE), |
| opened_(false), |
| delegate_delayed_complete_(false), |
| bound_net_log_(bound_net_log), |
| @@ -341,8 +341,9 @@ void DownloadItemImpl::StealDangerousDownload( |
| void DownloadItemImpl::Pause() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - // Ignore irrelevant states. |
| - if (state_ != IN_PROGRESS_INTERNAL || is_paused_) |
| + // Ignore irrelevant states. Also ignore the call if there's no request |
| + // handle. |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
This sounds like it's trying to handle Pause() req
asanka
2014/03/19 20:37:06
Hmm. Good point. We could set is_paused_ and respe
Randy Smith (Not in Mondays)
2014/03/20 15:24:54
That's effectively accepting that we have a race a
asanka
2016/02/11 23:57:24
But that's not correct. We are no longer in a plac
Randy Smith (Not in Mondays)
2016/02/12 18:58:10
Ok. I'm good with your race handling suggestion e
asanka
2016/02/12 20:52:28
Time Of Check vs. Time Of Use. :-) Basically, the
|
| + if (state_ != IN_PROGRESS_INTERNAL || is_paused_ || !request_handle_) |
| return; |
| request_handle_->PauseRequest(); |
| @@ -408,7 +409,7 @@ void DownloadItemImpl::Cancel(bool user_cancel) { |
| if (!is_save_package_download_ && download_file_) |
| ReleaseDownloadFile(true); |
| - if (state_ == IN_PROGRESS_INTERNAL) { |
| + if (state_ == IN_PROGRESS_INTERNAL && request_handle_) { |
| // Cancel the originating URL request unless it's already been cancelled |
| // by interrupt. |
| request_handle_->CancelRequest(); |
| @@ -926,6 +927,7 @@ void DownloadItemImpl::MergeOriginInfoOnResume( |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK_EQ(RESUMING_INTERNAL, state_); |
| DCHECK(!new_create_info.url_chain.empty()); |
| + DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, new_create_info.interrupt_reason); |
| // We are going to tack on any new redirects to our list of redirects. |
| // When a download is resumed, the URL used for the resumption request is the |
| @@ -1106,11 +1108,10 @@ void DownloadItemImpl::Init(bool active, |
| // We're starting the download. |
| void DownloadItemImpl::Start( |
| scoped_ptr<DownloadFile> file, |
| - scoped_ptr<DownloadRequestHandleInterface> req_handle) { |
| + scoped_ptr<DownloadRequestHandleInterface> req_handle, |
| + const DownloadCreateInfo& new_create_info) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(!download_file_.get()); |
| - DCHECK(file.get()); |
| - DCHECK(req_handle.get()); |
| download_file_ = file.Pass(); |
| request_handle_ = req_handle.Pass(); |
| @@ -1123,6 +1124,24 @@ void DownloadItemImpl::Start( |
| return; |
| } |
| + DCHECK(state_ == IN_PROGRESS_INTERNAL || state_ == RESUMING_INTERNAL); |
| + if (new_create_info.interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE) { |
| + DCHECK(!download_file_.get()); |
| + // If the download failed, then skip the download_file_ initialization. It |
| + // isn't going to be used anyway. OnDownloadFileInitialized() handles all |
| + // interrupts that happen up until after download file initialization. Allow |
| + // it to handle the new interrupt as well. |
| + OnDownloadFileInitialized(new_create_info.interrupt_reason); |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
nit: Might want to change the name if we're expand
Randy Smith (Not in Mondays)
2014/03/20 15:24:54
Just noting you didn't respond to this nit (I don'
asanka
2016/02/11 23:57:24
Whoops. Missed. I'm doing a pass through the previ
|
| + return; |
| + } |
| + |
| + // Successful download start. |
| + DCHECK(download_file_.get()); |
| + DCHECK(request_handle_.get()); |
| + |
| + if (state_ == RESUMING_INTERNAL) |
| + MergeOriginInfoOnResume(new_create_info); |
| + |
| TransitionTo(IN_PROGRESS_INTERNAL, UPDATE_OBSERVERS); |
| BrowserThread::PostTask( |
| @@ -1138,16 +1157,25 @@ void DownloadItemImpl::OnDownloadFileInitialized( |
| DownloadInterruptReason result) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { |
| + // Either the download was interrupted at Start() or the download file |
| + // failed to initialize. Either way, the download will not transition to an |
| + // interrupted state. |
| Interrupt(result); |
| - // TODO(rdsmith/asanka): Arguably we should show this in the UI, but |
| - // it's not at all clear what to show--we haven't done filename |
| - // determination, so we don't know what name to display. OTOH, |
| - // the failure mode of not showing the DI if the file initialization |
| - // fails isn't a good one. Can we hack up a name based on the |
| - // URLRequest? We'll need to make sure that initialization happens |
| - // properly. Possibly the right thing is to have the UI handle |
| - // this case specially. |
| - return; |
| + |
| + // The download may transition to RESUMING_INTERNAL as a result of the |
| + // Interrupt() call above if it was resumed automatically. If so, then |
| + // Start() is expected to be called again and the download progression |
| + // cascade will be redone. Therefore we are done for now. |
| + // |
| + // NOTE: The check below assumes that resumption doesn't call Start() |
| + // synchronously. If it did, then state_ may not be RESUMING_INTERNAL when |
| + // we get here. This constraint is currently enforced via a DCHECK in |
| + // Start() which requires that the download be in RESUMING_INTERNAL or |
| + // IN_PROGRESS_INTERNAL state when Start() is called. If Start() was called |
| + // synchronously, then the state of the DownloadItem would be |
| + // INTERRUPTED_INTERNAL. |
| + if (state_ == RESUMING_INTERNAL) |
| + return; |
| } |
| delegate_->DetermineDownloadTarget( |
| @@ -1171,20 +1199,19 @@ void DownloadItemImpl::OnDownloadTargetDetermined( |
| return; |
| } |
| - // TODO(rdsmith,asanka): We are ignoring the possibility that the download |
| - // has been interrupted at this point until we finish the intermediate |
| - // rename and set the full path. That's dangerous, because we might race |
| - // with resumption, either manual (because the interrupt is visible to the |
| - // UI) or automatic. If we keep the "ignore an error on download until file |
| - // name determination complete" semantics, we need to make sure that the |
| - // error is kept completely invisible until that point. |
| - |
| VLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition |
| << " " << danger_type << " " << DebugString(true); |
| target_path_ = target_path; |
| target_disposition_ = disposition; |
| SetDangerType(danger_type); |
| + if (state_ != IN_PROGRESS_INTERNAL) { |
| + // The download could be already interrupted, in which case there's nothing |
| + // more we can do. Notify observers and quit the download progression |
| + // cascade. |
| + UpdateObservers(); |
| + return; |
| + } |
| // We want the intermediate and target paths to refer to the same directory so |
| // that they are both on the same device and subject to same |
| @@ -1210,7 +1237,6 @@ void DownloadItemImpl::OnDownloadTargetDetermined( |
| // filename. Unnecessary renames may cause bugs like |
| // http://crbug.com/74187. |
| DCHECK(!is_save_package_download_); |
| - DCHECK(download_file_.get()); |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
Ok, I'm confused. It seems to me that if we're no
asanka
2014/03/19 20:37:06
Ah. The DCHECK removal was due to an interim chang
|
| DownloadFile::RenameCompletionCallback callback = |
| base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName, |
| weak_ptr_factory_.GetWeakPtr()); |
| @@ -1403,22 +1429,6 @@ void DownloadItemImpl::Completed() { |
| } |
| } |
| -void DownloadItemImpl::OnResumeRequestStarted( |
| - DownloadItem* item, |
| - DownloadInterruptReason interrupt_reason) { |
| - // If |item| is not NULL, then Start() has been called already, and nothing |
| - // more needs to be done here. |
| - if (item) { |
| - DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); |
| - DCHECK_EQ(static_cast<DownloadItem*>(this), item); |
| - return; |
| - } |
| - // Otherwise, the request failed without passing through |
| - // DownloadResourceHandler::OnResponseStarted. |
| - DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); |
| - Interrupt(interrupt_reason); |
| -} |
| - |
| // **** End of Download progression cascade |
| // An error occurred somewhere. |
| @@ -1443,34 +1453,31 @@ void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) { |
| ResumeMode resume_mode = GetResumeMode(); |
| - if (state_ == IN_PROGRESS_INTERNAL) { |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
So this change is removing a bit of redundancy fro
asanka
2014/03/19 20:37:06
The problem was the handling of downloads that wer
Randy Smith (Not in Mondays)
2014/03/20 15:24:54
We could do this, but my instinct is that we shoul
asanka
2016/02/11 23:57:24
New downloads that start in a failed state need to
Randy Smith (Not in Mondays)
2016/02/12 18:58:10
I feel absolutely no need to resolve this old phil
asanka
2016/02/12 20:52:28
Acknowledged.
|
| - // Cancel (delete file) if: |
| - // 1) we're going to restart. |
| - // 2) Resumption isn't possible (download was cancelled or blocked due to |
| - // security restrictions). |
| - // 3) Resumption isn't enabled. |
| - // No point in leaving data around we aren't going to use. |
| + // Cancel (delete file) if: |
| + // 1) we're going to restart. |
| + // 2) Resumption isn't possible (download was cancelled or blocked due to |
| + // security restrictions). |
| + // 3) Resumption isn't enabled. |
| + // No point in leaving data around we aren't going to use. |
| + if (download_file_.get()) |
| ReleaseDownloadFile(resume_mode == RESUME_MODE_IMMEDIATE_RESTART || |
| resume_mode == RESUME_MODE_USER_RESTART || |
| resume_mode == RESUME_MODE_INVALID || |
| !IsDownloadResumptionEnabled()); |
| - // Cancel the originating URL request. |
| + // Cancel the originating URL request. |
| + if (request_handle_.get()) |
| request_handle_->CancelRequest(); |
| - } else { |
| - DCHECK(!download_file_.get()); |
| - } |
| - // Reset all data saved, as even if we did save all the data we're going |
| - // to go through another round of downloading when we resume. |
| - // There's a potential problem here in the abstract, as if we did download |
| - // all the data and then run into a continuable error, on resumption we |
| - // won't download any more data. However, a) there are currently no |
| - // continuable errors that can occur after we download all the data, and |
| - // b) if there were, that would probably simply result in a null range |
| - // request, which would generate a DestinationCompleted() notification |
| - // from the DownloadFile, which would behave properly with setting |
| - // all_data_saved_ to false here. |
| + // Reset all data saved, as even if we did save all the data we're going to go |
| + // through another round of downloading when we resume. There's a potential |
| + // problem here in the abstract, as if we did download all the data and then |
| + // run into a continuable error, on resumption we won't download any more |
| + // data. However, a) there are currently no continuable errors that can occur |
| + // after we download all the data, and b) if there were, that would probably |
| + // simply result in a null range request, which would generate a |
| + // DestinationCompleted() notification from the DownloadFile, which would |
| + // behave properly with setting all_data_saved_ to false here. |
| all_data_saved_ = false; |
| TransitionTo(INTERRUPTED_INTERNAL, DONT_UPDATE_OBSERVERS); |
| @@ -1701,9 +1708,6 @@ void DownloadItemImpl::ResumeInterruptedDownload() { |
| download_params->set_hash_state(GetHashState()); |
| download_params->set_last_modified(GetLastModifiedTime()); |
| download_params->set_etag(GetETag()); |
| - download_params->set_callback( |
| - base::Bind(&DownloadItemImpl::OnResumeRequestStarted, |
| - weak_ptr_factory_.GetWeakPtr())); |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
Isn't this assuming that neither of the fall-throu
asanka
2014/03/19 20:37:06
I verified that ResourceHandler::OnResponseComplet
Randy Smith (Not in Mondays)
2014/03/20 18:04:46
What about the places outside DownloadResourceHand
asanka
2016/02/11 23:57:24
RDH::BeginDownload doesn't call the started callba
|
| delegate_->ResumeInterruptedDownload(download_params.Pass(), GetId()); |
| // Just in case we were interrupted while paused. |