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 053406902ba7e43d870827062f8ad9c0965bf599..7cc8db76c3fcbfbefa8087a4413f66c76e2293cd 100644 |
| --- a/content/browser/download/download_item_impl.cc |
| +++ b/content/browser/download/download_item_impl.cc |
| @@ -4,7 +4,7 @@ |
| // File method ordering: Methods in this file are in the same order |
| // as in download_item_impl.h, with the following exception: The public |
| -// interfaces DelayedDownloadOpened, OnDownloadTargetDetermined, and |
| +// interfaces Start, DelayedDownloadOpened, OnDownloadTargetDetermined, and |
| // OnDownloadCompleting are placed in chronological order with the other |
| // (private) routines that together define a DownloadItem's state transitions |
| // as the download progresses. See "Download progression cascade" later in |
| @@ -39,7 +39,6 @@ |
| #include "base/utf_string_conversions.h" |
| #include "content/browser/download/download_create_info.h" |
| #include "content/browser/download/download_file.h" |
| -#include "content/browser/download/download_file_manager.h" |
| #include "content/browser/download/download_interrupt_reasons_impl.h" |
| #include "content/browser/download/download_item_impl_delegate.h" |
| #include "content/browser/download/download_request_handle.h" |
| @@ -122,6 +121,20 @@ class NullDownloadRequestHandle : public DownloadRequestHandleInterface { |
| } |
| }; |
| +// Wrapper around DownloadFile::Detach and DownloadFile::Cancel that |
| +// takes ownership of the DownloadFile and hence implicitly destroys it |
| +// at the end of the function. |
| +static void DownloadFileDetach( |
| + scoped_ptr<DownloadFile> download_file, base::Closure callback) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + download_file->Detach(callback); |
| +} |
| + |
| +static void DownloadFileCancel(scoped_ptr<DownloadFile> download_file) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + download_file->Cancel(); |
| +} |
| + |
| } // namespace |
| namespace content { |
| @@ -147,7 +160,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, |
| DownloadId download_id, |
| const DownloadPersistentStoreInfo& info, |
| const net::BoundNetLog& bound_net_log) |
| - : download_id_(download_id), |
| + : is_save_package_download_(false), |
| + download_id_(download_id), |
| current_path_(info.path), |
| target_path_(info.path), |
| target_disposition_(TARGET_DISPOSITION_OVERWRITE), |
| @@ -194,7 +208,8 @@ DownloadItemImpl::DownloadItemImpl( |
| const DownloadCreateInfo& info, |
| scoped_ptr<DownloadRequestHandleInterface> request_handle, |
| const net::BoundNetLog& bound_net_log) |
| - : request_handle_(request_handle.Pass()), |
| + : is_save_package_download_(false), |
| + request_handle_(request_handle.Pass()), |
| download_id_(info.download_id), |
| target_disposition_( |
| (info.prompt_user_for_save_location) ? |
| @@ -254,7 +269,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, |
| DownloadId download_id, |
| const std::string& mime_type, |
| const net::BoundNetLog& bound_net_log) |
| - : request_handle_(new NullDownloadRequestHandle()), |
| + : is_save_package_download_(true), |
| + request_handle_(new NullDownloadRequestHandle()), |
| download_id_(download_id), |
| current_path_(path), |
| target_path_(path), |
| @@ -295,6 +311,11 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, |
| DownloadItemImpl::~DownloadItemImpl() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + // Should always have been nuked before now, at worst in |
| + // DownloadManager shutdown. |
| + DCHECK(!download_file_.get()); |
| + |
| FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this)); |
| delegate_->AssertStateConsistent(this); |
| delegate_->Detach(); |
| @@ -367,6 +388,21 @@ void DownloadItemImpl::Cancel(bool user_cancel) { |
| download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT); |
| TransitionTo(CANCELLED); |
| + |
| + // Cancel and remove the download file. |
| + // TODO(rdsmith/benjhayden): Remove condition as part of |
| + // SavePackage integration. |
| + if (!is_save_package_download_) { |
| + CHECK(download_file_.get()); |
| + BrowserThread::PostTask( |
| + BrowserThread::FILE, FROM_HERE, |
| + // Will be deleted at end of task execution. |
| + base::Bind(&DownloadFileCancel, base::Passed(download_file_.Pass()))); |
| + } |
| + |
| + // Cancel the originating URL request. |
| + request_handle_->CancelRequest(); |
| + |
| if (user_cancel) |
| delegate_->DownloadStopped(this); |
| } |
| @@ -817,7 +853,8 @@ std::string DownloadItemImpl::DebugString(bool verbose) const { |
| " etag = '%s'" |
| " url_chain = \n\t\"%s\"\n\t" |
| " full_path = \"%" PRFilePath "\"" |
| - " target_path = \"%" PRFilePath "\"", |
| + " target_path = \"%" PRFilePath "\"" |
| + " has download file = %s", |
| GetDbHandle(), |
| GetTotalBytes(), |
| GetReceivedBytes(), |
| @@ -828,7 +865,8 @@ std::string DownloadItemImpl::DebugString(bool verbose) const { |
| GetETag().c_str(), |
| url_list.c_str(), |
| GetFullPath().value().c_str(), |
| - GetTargetFilePath().value().c_str()); |
| + GetTargetFilePath().value().c_str(), |
| + download_file_.get() ? "true" : "false"); |
| } else { |
| description += base::StringPrintf(" url = \"%s\"", url_list.c_str()); |
| } |
| @@ -851,16 +889,6 @@ void DownloadItemImpl::OnDownloadedFileRemoved() { |
| UpdateObservers(); |
| } |
| -void DownloadItemImpl::OffThreadCancel() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - request_handle_->CancelRequest(); |
| - |
| - BrowserThread::PostTask( |
| - BrowserThread::FILE, FROM_HERE, |
| - base::Bind(&DownloadFileManager::CancelDownload, |
| - delegate_->GetDownloadFileManager(), download_id_)); |
| -} |
| - |
| // An error occurred somewhere. |
| void DownloadItemImpl::Interrupt(content::DownloadInterruptReason reason) { |
| // Somewhat counter-intuitively, it is possible for us to receive an |
| @@ -878,11 +906,36 @@ void DownloadItemImpl::Interrupt(content::DownloadInterruptReason reason) { |
| last_reason_ = reason; |
| TransitionTo(INTERRUPTED); |
| + |
| + // Cancel and remove the download file. |
| + // TODO(rdsmith/benjhayden): Remove condition as part of |
| + // SavePackage integration. |
| + if (!is_save_package_download_) { |
| + CHECK(download_file_.get()); |
| + BrowserThread::PostTask( |
| + BrowserThread::FILE, FROM_HERE, |
| + // Will be deleted at end of task execution. |
| + base::Bind(&DownloadFileCancel, base::Passed(download_file_.Pass()))); |
| + } |
| + |
| + // Cancel the originating URL request. |
| + request_handle_->CancelRequest(); |
| + |
| download_stats::RecordDownloadInterrupted( |
| reason, received_bytes_, total_bytes_); |
| delegate_->DownloadStopped(this); |
| } |
| +base::WeakPtr<content::DownloadDestinationObserver> |
| +DownloadItemImpl::DestinationObserverAsWeakPtr() { |
| + // Return does private downcast. |
|
benjhayden
2012/09/12 21:01:01
Actually, this is up-casting.
And also obvious.
Randy Smith (Not in Mondays)
2012/09/13 20:15:12
The fact that it's private wasn't completely obvio
|
| + return weak_ptr_factory_.GetWeakPtr(); |
| +} |
| + |
| +bool DownloadItemImpl::IsSavePackageDownload() const { |
| + return is_save_package_download_; |
| +} |
| + |
| void DownloadItemImpl::SetTotalBytes(int64 total_bytes) { |
| total_bytes_ = total_bytes; |
| } |
| @@ -925,13 +978,17 @@ void DownloadItemImpl::UpdateProgress(int64 bytes_so_far, |
| UpdateObservers(); |
| } |
| -void DownloadItemImpl::OnAllDataSaved( |
| - int64 size, const std::string& final_hash) { |
| +void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + DCHECK_EQ(IN_PROGRESS, state_); |
| DCHECK(!all_data_saved_); |
| all_data_saved_ = true; |
| - ProgressComplete(size, final_hash); |
| + |
| + // Store final hash and null out intermediate serialized hash state. |
| + hash_ = final_hash; |
| + hash_state_ = ""; |
| + |
| UpdateObservers(); |
| } |
| @@ -945,6 +1002,7 @@ void DownloadItemImpl::MarkAsComplete() { |
| void DownloadItemImpl::SetIsPersisted() { |
| is_persisted_ = true; |
| + UpdateObservers(); |
| } |
| void DownloadItemImpl::SetDbHandle(int64 handle) { |
| @@ -955,6 +1013,55 @@ void DownloadItemImpl::SetDbHandle(int64 handle) { |
| net::NetLog::Int64Callback("db_handle", db_handle_)); |
| } |
| +void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far, |
| + int64 bytes_per_sec, |
| + const std::string& hash_state) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + if (!IsInProgress()) { |
| + // Ignore if we're no longer in-progress. This can happen if we race a |
| + // Cancel on the UI thread with an update on the FILE thread. |
| + // |
| + // TODO(rdsmith): Arguably we should let this go through, as this means |
| + // the download really did get further than we know before it was |
| + // cancelled. But the gain isn't very large, and the code is more |
| + // fragile if it has to support in progress updates in a non-in-progress |
| + // state. This issue should be readdressed when we revamp performance |
| + // reporting. |
| + return; |
| + } |
| + bytes_per_sec_ = bytes_per_sec; |
| + hash_state_ = hash_state; |
| + received_bytes_ = bytes_so_far; |
| + |
| + // If we've received more data than we were expecting (bad server info?), |
| + // revert to 'unknown size mode'. |
| + if (received_bytes_ > total_bytes_) |
| + total_bytes_ = 0; |
| + |
| + if (bound_net_log_.IsLoggingAllEvents()) { |
| + bound_net_log_.AddEvent( |
| + net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED, |
| + net::NetLog::Int64Callback("bytes_so_far", received_bytes_)); |
| + } |
| + |
| + UpdateObservers(); |
| +} |
| + |
| +void DownloadItemImpl::DestinationError( |
| + content::DownloadInterruptReason reason) { |
| + // The DestinationError and Interrupt routines are being kept separate |
| + // to allow for a future merging of the Cancel and Interrupt routines.. |
| + Interrupt(reason); |
| +} |
| + |
| +void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) { |
| + if (!IsInProgress()) |
| + return; |
| + OnAllDataSaved(final_hash); |
| + delegate_->MaybeCompleteDownload(this); |
| +} |
| + |
| // **** Download progression cascade |
| void DownloadItemImpl::Init(bool active, |
| @@ -998,7 +1105,37 @@ void DownloadItemImpl::Init(bool active, |
| VLOG(20) << __FUNCTION__ << "() " << DebugString(true); |
| } |
| -// Called by DownloadManagerImpl when the download target path has been |
| +// We're starting the download. |
| +void DownloadItemImpl::Start(scoped_ptr<content::DownloadFile> download_file) { |
| + DCHECK(!download_file_.get()); |
| + download_file_ = download_file.Pass(); |
|
benjhayden
2012/09/12 21:01:01
DCHECK(download_file_.get());?
Randy Smith (Not in Mondays)
2012/09/13 20:15:12
Equivalent done.
|
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::FILE, FROM_HERE, |
| + base::Bind(&DownloadFile::Initialize, |
| + // Safe because we control download file lifetime. |
| + base::Unretained(download_file_.get()), |
| + base::Bind(&DownloadItemImpl::OnDownloadFileInitialized, |
| + weak_ptr_factory_.GetWeakPtr()))); |
| +} |
| + |
| +void DownloadItemImpl::OnDownloadFileInitialized( |
| + content::DownloadInterruptReason result) { |
| + if (result != content::DOWNLOAD_INTERRUPT_REASON_NONE) { |
| + Interrupt(result); |
| + // TODO(rdsmith): It makes no sense to continue along the |
| + // regular download path after we've gotten an error. But it's |
| + // the way the code has historically worked, and this allows us |
| + // to get the download persisted and observers of the download manager |
| + // notified, so tests work. When we execute all side effects of cancel |
| + // (including queue removal) immedately rather than waiting for |
| + // persistence we should replace this comment with a "return;". |
| + } |
| + |
| + delegate_->DelegateStart(this); |
| +} |
| + |
| +// Called by delegate_ when the download target path has been |
| // determined. |
| void DownloadItemImpl::OnDownloadTargetDetermined( |
| const FilePath& target_path, |
| @@ -1024,18 +1161,31 @@ void DownloadItemImpl::OnDownloadTargetDetermined( |
| // space/permission/availability constraints. |
| DCHECK(intermediate_path.DirName() == target_path.DirName()); |
| + if (!IsInProgress()) { |
| + // If we've been cancelled or interrupted while the target was being |
| + // determined, continue the cascade with a null name. |
| + // The error doesn't matter as the cause of download stoppaged |
| + // will already have been recorded. |
| + OnDownloadRenamedToIntermediateName( |
| + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, FilePath()); |
| + return; |
| + } |
| + |
| // Rename to intermediate name. |
| // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a |
| // spurious rename when we can just rename to the final |
| // filename. Unnecessary renames may cause bugs like |
| // http://crbug.com/74187. |
| - DownloadFileManager::RenameCompletionCallback callback = |
| + DCHECK(!is_save_package_download_); |
| + CHECK(download_file_.get()); |
| + DownloadFile::RenameCompletionCallback callback = |
| base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName, |
| weak_ptr_factory_.GetWeakPtr()); |
| BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| - base::Bind(&DownloadFileManager::RenameDownloadFile, |
| - delegate_->GetDownloadFileManager(), GetGlobalId(), |
| + base::Bind(&DownloadFile::Rename, |
| + // Safe because we control download file lifetime. |
| + base::Unretained(download_file_.get()), |
| intermediate_path, false, callback)); |
| } |
| @@ -1063,29 +1213,35 @@ void DownloadItemImpl::MaybeCompleteDownload() { |
| void DownloadItemImpl::OnDownloadCompleting() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (!IsInProgress()) |
| + return; |
| + |
| VLOG(20) << __FUNCTION__ << "()" |
| << " needs rename = " << NeedsRename() |
| << " " << DebugString(true); |
| DCHECK(!GetTargetName().empty()); |
| DCHECK_NE(DANGEROUS, GetSafetyState()); |
| + // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration. |
| + if (is_save_package_download_) { |
| + // Avoid doing anything on the file thread; there's nothing we control |
| + // there. |
| + OnDownloadFileReleased(); |
| + return; |
| + } |
| + |
| + CHECK(download_file_.get()); |
| if (NeedsRename()) { |
| - DownloadFileManager::RenameCompletionCallback callback = |
| + content::DownloadFile::RenameCompletionCallback callback = |
| base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName, |
| weak_ptr_factory_.GetWeakPtr()); |
| BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| - base::Bind(&DownloadFileManager::RenameDownloadFile, |
| - delegate_->GetDownloadFileManager(), GetGlobalId(), |
| + base::Bind(&DownloadFile::Rename, |
| + base::Unretained(download_file_.get()), |
| GetTargetFilePath(), true, callback)); |
| } else { |
| - // Complete the download and release the DownloadFile. |
| - BrowserThread::PostTask( |
| - BrowserThread::FILE, FROM_HERE, |
| - base::Bind(&DownloadFileManager::CompleteDownload, |
| - delegate_->GetDownloadFileManager(), GetGlobalId(), |
| - base::Bind(&DownloadItemImpl::OnDownloadFileReleased, |
| - weak_ptr_factory_.GetWeakPtr()))); |
| + ReleaseDownloadFile(); |
| } |
| } |
| @@ -1094,6 +1250,9 @@ void DownloadItemImpl::OnDownloadRenamedToFinalName( |
| const FilePath& full_path) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (!IsInProgress()) |
| + return; |
| + |
| VLOG(20) << __FUNCTION__ << "()" |
| << " full_path = \"" << full_path.value() << "\"" |
| << " needed rename = " << NeedsRename() |
| @@ -1111,13 +1270,25 @@ void DownloadItemImpl::OnDownloadRenamedToFinalName( |
| SetFullPath(full_path); |
| delegate_->DownloadRenamedToFinalName(this); |
| + ReleaseDownloadFile(); |
| +} |
| + |
| +void DownloadItemImpl::ReleaseDownloadFile() { |
| // Complete the download and release the DownloadFile. |
| + DCHECK(!is_save_package_download_); |
| + CHECK(download_file_.get()); |
| BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| - base::Bind(&DownloadFileManager::CompleteDownload, |
| - delegate_->GetDownloadFileManager(), GetGlobalId(), |
| + base::Bind(&DownloadFileDetach, base::Passed(download_file_.Pass()), |
| base::Bind(&DownloadItemImpl::OnDownloadFileReleased, |
| weak_ptr_factory_.GetWeakPtr()))); |
| + |
| + // We're not completely done with the download item yet, but at this |
| + // point we're committed to complete the download. Cancels (or Interrupts, |
| + // though it's not clear how they could happen) after this point will be |
| + // ignored. |
| + TransitionTo(COMPLETE); |
| + delegate_->DownloadCompleted(this); |
| } |
| void DownloadItemImpl::OnDownloadFileReleased() { |
| @@ -1130,6 +1301,7 @@ void DownloadItemImpl::OnDownloadFileReleased() { |
| void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) { |
| auto_opened_ = auto_opened; |
| Completed(); |
| + UpdateObservers(); |
| } |
| void DownloadItemImpl::Completed() { |
| @@ -1139,8 +1311,6 @@ void DownloadItemImpl::Completed() { |
| DCHECK(all_data_saved_); |
| end_time_ = base::Time::Now(); |
| - TransitionTo(COMPLETE); |
| - delegate_->DownloadCompleted(this); |
| download_stats::RecordDownloadCompleted(start_tick_, received_bytes_); |
| if (auto_opened_) { |
| @@ -1166,21 +1336,6 @@ bool DownloadItemImpl::NeedsRename() const { |
| return target_path_ != current_path_; |
| } |
| -void DownloadItemImpl::ProgressComplete(int64 bytes_so_far, |
| - const std::string& final_hash) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - |
| - hash_ = final_hash; |
| - hash_state_ = ""; |
| - |
| - received_bytes_ = bytes_so_far; |
| - |
| - // If we've received more data than we were expecting (bad server info?), |
| - // revert to 'unknown size mode'. |
| - if (received_bytes_ > total_bytes_) |
| - total_bytes_ = 0; |
| -} |
| - |
| void DownloadItemImpl::TransitionTo(DownloadState new_state) { |
| if (state_ == new_state) |
| return; |
| @@ -1249,7 +1404,3 @@ void DownloadItemImpl::SetFullPath(const FilePath& new_path) { |
| base::Bind(&download_net_logs::ItemRenamedCallback, |
| ¤t_path_, &new_path)); |
| } |
| - |
| - |
| - |
| - |