| 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 aeb7fc3fffc4837787c4705f7aadd9af2d8060d5..bea70fe8164805998a3a6bd944d75eabfbdf84c2 100644
|
| --- a/content/browser/download/download_item_impl.cc
|
| +++ b/content/browser/download/download_item_impl.cc
|
| @@ -4,11 +4,11 @@
|
|
|
| // 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,
|
| -// MaybeCompleteDownload, 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 this file.
|
| +// interfaces Start, DelayedDownloadOpened, MaybeCompleteDownload, 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
|
| +// this file.
|
|
|
| // A regular DownloadItem (created for a download in this session of the
|
| // browser) normally goes through the following states:
|
| @@ -37,7 +37,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"
|
| @@ -104,6 +103,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 {
|
| @@ -129,7 +142,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),
|
| @@ -176,7 +190,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) ?
|
| @@ -236,7 +251,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),
|
| @@ -277,6 +293,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();
|
| @@ -328,6 +349,11 @@ void DownloadItemImpl::TogglePause() {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| DCHECK(state_ == IN_PROGRESS_INTERNAL || state_ == COMPLETING_INTERNAL);
|
| +
|
| + // Ignore pauses when we've passed the commit point.
|
| + if (state_ == COMPLETING_INTERNAL)
|
| + return;
|
| +
|
| if (is_paused_)
|
| request_handle_->ResumeRequest();
|
| else
|
| @@ -353,6 +379,12 @@ void DownloadItemImpl::Cancel(bool user_cancel) {
|
| download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT);
|
|
|
| TransitionTo(CANCELLED_INTERNAL);
|
| +
|
| + CancelDownloadFile();
|
| +
|
| + // Cancel the originating URL request.
|
| + request_handle_->CancelRequest();
|
| +
|
| delegate_->DownloadStopped(this);
|
| }
|
|
|
| @@ -374,10 +406,12 @@ void DownloadItemImpl::Delete(DeleteReason reason) {
|
| NOTREACHED();
|
| }
|
|
|
| - // TODO(asanka): Avoid deleting a file that is still owned by DownloadFile.
|
| - if (!current_path_.empty())
|
| + // Delete the file if it exists and is not owned by a DownloadFile object.
|
| + // (In the latter case the DownloadFile object will delete it on cancel.)
|
| + if (!current_path_.empty() && download_file_.get() == NULL) {
|
| BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
|
| base::Bind(&DeleteDownloadedFile, current_path_));
|
| + }
|
| Remove();
|
| // We have now been deleted.
|
| }
|
| @@ -765,7 +799,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(),
|
| @@ -776,7 +811,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());
|
| }
|
| @@ -799,16 +835,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
|
| @@ -826,11 +852,26 @@ void DownloadItemImpl::Interrupt(content::DownloadInterruptReason reason) {
|
|
|
| last_reason_ = reason;
|
| TransitionTo(INTERRUPTED_INTERNAL);
|
| +
|
| + CancelDownloadFile();
|
| +
|
| + // 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 weak_ptr_factory_.GetWeakPtr();
|
| +}
|
| +
|
| +bool DownloadItemImpl::IsSavePackageDownload() const {
|
| + return is_save_package_download_;
|
| +}
|
| +
|
| void DownloadItemImpl::SetTotalBytes(int64 total_bytes) {
|
| total_bytes_ = total_bytes;
|
| }
|
| @@ -873,13 +914,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_INTERNAL, 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();
|
| }
|
|
|
| @@ -904,6 +949,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);
|
| + MaybeCompleteDownload();
|
| +}
|
| +
|
| // **** Download progression cascade
|
|
|
| void DownloadItemImpl::Init(bool active,
|
| @@ -947,7 +1041,40 @@ 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> file) {
|
| + DCHECK(!download_file_.get());
|
| + DCHECK(file.get());
|
| + download_file_ = file.Pass();
|
| +
|
| + 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_->DetermineDownloadTarget(
|
| + this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +// Called by delegate_ when the download target path has been
|
| // determined.
|
| void DownloadItemImpl::OnDownloadTargetDetermined(
|
| const FilePath& target_path,
|
| @@ -988,13 +1115,16 @@ void DownloadItemImpl::OnDownloadTargetDetermined(
|
| // 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));
|
| }
|
|
|
| @@ -1067,24 +1197,26 @@ void DownloadItemImpl::ReadyForDownloadCompletionDone() {
|
| DCHECK(!GetTargetFilePath().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())));
|
| - TransitionTo(COMPLETING_INTERNAL);
|
| + ReleaseDownloadFile();
|
| }
|
| }
|
|
|
| @@ -1116,13 +1248,23 @@ 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(COMPLETING_INTERNAL);
|
| }
|
|
|
| @@ -1167,6 +1309,18 @@ void DownloadItemImpl::Completed() {
|
|
|
| // **** End of Download progression cascade
|
|
|
| +void DownloadItemImpl::CancelDownloadFile() {
|
| + // 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())));
|
| + }
|
| +}
|
| +
|
| bool DownloadItemImpl::IsDownloadReadyForCompletion() {
|
| // If we don't have all the data, the download is not ready for
|
| // completion.
|
| @@ -1197,21 +1351,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(DownloadInternalState new_state) {
|
| if (state_ == new_state)
|
| return;
|
|
|