| 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 a3e72ffa469e50f0efd18f92c63a50c2a99e0e16..c8a189baf76fc8a34518dc68d2d092098c9e8b47 100644
|
| --- a/content/browser/download/download_item_impl.cc
|
| +++ b/content/browser/download/download_item_impl.cc
|
| @@ -19,7 +19,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"
|
| @@ -121,6 +120,17 @@ class NullDownloadRequestHandle : public DownloadRequestHandleInterface {
|
| }
|
| };
|
|
|
| +// Detach the specified download file, then invoke the callback on the
|
| +// UI thread. Note that this will also delete the DownloadFile object,
|
| +// as the function accepts ownership and does not transfer it on.
|
| +static void ReleaseDownloadFile(scoped_ptr<DownloadFile> download_file,
|
| + const base::Closure& ui_callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| + download_file->Detach();
|
| +
|
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, ui_callback);
|
| +}
|
| +
|
| } // namespace
|
|
|
| namespace content {
|
| @@ -300,6 +310,10 @@ 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());
|
| +
|
| TransitionTo(REMOVING);
|
| STLDeleteContainerPairSecondPointers(
|
| external_data_map_.begin(), external_data_map_.end());
|
| @@ -307,6 +321,12 @@ DownloadItemImpl::~DownloadItemImpl() {
|
| delegate_->Detach();
|
| }
|
|
|
| +base::WeakPtr<content::DownloadDestinationController>
|
| +DownloadItemImpl::DestinationControllerAsWeakPtr() {
|
| + // Return does private downcast.
|
| + return weak_ptr_factory_.GetWeakPtr();
|
| +}
|
| +
|
| void DownloadItemImpl::AddObserver(Observer* observer) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| @@ -395,21 +415,6 @@ void DownloadItemImpl::DangerousDownloadValidated() {
|
| delegate_->MaybeCompleteDownload(this);
|
| }
|
|
|
| -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;
|
| -}
|
| -
|
| // Updates from the download thread may have been posted while this download
|
| // was being cancelled in the UI thread, so we'll accept them unless we're
|
| // complete.
|
| @@ -470,6 +475,20 @@ void DownloadItemImpl::Cancel(bool user_cancel) {
|
| delegate_->DownloadStopped(this);
|
| }
|
|
|
| +// We're starting the download.
|
| +void DownloadItemImpl::Start(scoped_ptr<content::DownloadFile> download_file) {
|
| + DCHECK(!download_file_.get());
|
| + download_file_ = download_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())));
|
| +}
|
| +
|
| // An error occurred somewhere.
|
| void DownloadItemImpl::Interrupt(content::DownloadInterruptReason reason) {
|
| // Somewhat counter-intuitively, it is possible for us to receive an
|
| @@ -506,12 +525,16 @@ void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) {
|
| }
|
|
|
| void DownloadItemImpl::OnAllDataSaved(
|
| - int64 size, const std::string& final_hash) {
|
| + const std::string& final_hash) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| 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();
|
| }
|
|
|
| @@ -700,43 +723,47 @@ void DownloadItemImpl::TogglePause() {
|
| UpdateObservers();
|
| }
|
|
|
| -void DownloadItemImpl::OnDownloadCompleting(DownloadFileManager* file_manager) {
|
| +void DownloadItemImpl::OnDownloadCompleting() {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| + // Lost Cancel race.
|
| + if (!IsInProgress())
|
| + return;
|
| +
|
| VLOG(20) << __FUNCTION__ << "()"
|
| << " needs rename = " << NeedsRename()
|
| << " " << DebugString(true);
|
| DCHECK(!GetTargetName().empty());
|
| DCHECK_NE(DANGEROUS, GetSafetyState());
|
| - DCHECK(file_manager);
|
|
|
| if (NeedsRename()) {
|
| - DownloadFileManager::RenameCompletionCallback callback =
|
| + content::DownloadFile::RenameCompletionCallback callback =
|
| base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - base::Unretained(file_manager));
|
| + weak_ptr_factory_.GetWeakPtr());
|
| BrowserThread::PostTask(
|
| BrowserThread::FILE, FROM_HERE,
|
| - base::Bind(&DownloadFileManager::RenameDownloadFile,
|
| - file_manager, GetGlobalId(), GetTargetFilePath(),
|
| - true, callback));
|
| + 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,
|
| - file_manager, GetGlobalId(),
|
| + base::Bind(&ReleaseDownloadFile, base::Passed(download_file_.Pass()),
|
| base::Bind(&DownloadItemImpl::OnDownloadFileReleased,
|
| weak_ptr_factory_.GetWeakPtr())));
|
| }
|
| }
|
|
|
| void DownloadItemImpl::OnDownloadRenamedToFinalName(
|
| - DownloadFileManager* file_manager,
|
| content::DownloadInterruptReason reason,
|
| const FilePath& full_path) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| + // Lost Cancel race.
|
| + if (!IsInProgress())
|
| + return;
|
| +
|
| VLOG(20) << __FUNCTION__ << "()"
|
| << " full_path = \"" << full_path.value() << "\""
|
| << " needed rename = " << NeedsRename()
|
| @@ -755,14 +782,30 @@ void DownloadItemImpl::OnDownloadRenamedToFinalName(
|
| delegate_->DownloadRenamedToFinalName(this);
|
|
|
| // Complete the download and release the DownloadFile.
|
| + DCHECK(download_file_.get());
|
| BrowserThread::PostTask(
|
| BrowserThread::FILE, FROM_HERE,
|
| - base::Bind(&DownloadFileManager::CompleteDownload,
|
| - file_manager, GetGlobalId(),
|
| + base::Bind(&ReleaseDownloadFile, base::Passed(download_file_.Pass()),
|
| base::Bind(&DownloadItemImpl::OnDownloadFileReleased,
|
| 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);
|
| +}
|
| +
|
| void DownloadItemImpl::OnDownloadFileReleased() {
|
| if (delegate_->ShouldOpenDownload(this))
|
| Completed();
|
| @@ -893,16 +936,31 @@ void DownloadItemImpl::OnContentCheckCompleted(
|
| }
|
|
|
| void DownloadItemImpl::OnIntermediatePathDetermined(
|
| - DownloadFileManager* file_manager,
|
| const FilePath& intermediate_path) {
|
| - DownloadFileManager::RenameCompletionCallback callback =
|
| + if (!IsInProgress()) {
|
| + // Lost interrupt/cancel race. We need to continue the cascade
|
| + // anyway, so that we get this entry persisted and made visible
|
| + // to observers. Actual error code doesn't matter as we've
|
| + // already stored the original reason for failure.
|
| + //
|
| + // TODO(rdsmith): Remove this code when we make downloads visible to
|
| + // observers earlier in the cascade so they can see immediate transitions
|
| + // to INTERRUPTED.
|
| + OnDownloadRenamedToIntermediateName(
|
| + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, FilePath());
|
| + return;
|
| + }
|
| +
|
| + content::DownloadFile::RenameCompletionCallback callback =
|
| base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName,
|
| weak_ptr_factory_.GetWeakPtr());
|
| +
|
| + DCHECK(download_file_.get());
|
| BrowserThread::PostTask(
|
| BrowserThread::FILE, FROM_HERE,
|
| - base::Bind(&DownloadFileManager::RenameDownloadFile,
|
| - file_manager, GetGlobalId(), intermediate_path,
|
| - false, callback));
|
| + base::Bind(&DownloadFile::Rename,
|
| + base::Unretained(download_file_.get()),
|
| + intermediate_path, false, callback));
|
| }
|
|
|
| const FilePath& DownloadItemImpl::GetFullPath() const {
|
| @@ -924,14 +982,62 @@ FilePath DownloadItemImpl::GetUserVerifiedFilePath() const {
|
| GetTargetFilePath() : GetFullPath();
|
| }
|
|
|
| -void DownloadItemImpl::OffThreadCancel(DownloadFileManager* file_manager) {
|
| +void DownloadItemImpl::OffThreadCancel() {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| request_handle_->CancelRequest();
|
|
|
| + DCHECK(download_file_.get());
|
| BrowserThread::PostTask(
|
| BrowserThread::FILE, FROM_HERE,
|
| - base::Bind(&DownloadFileManager::CancelDownload,
|
| - file_manager, download_id_));
|
| + // Will be deleted at end of task execution.
|
| + base::Bind(&DownloadFile::Cancel, base::Owned(download_file_.release())));
|
| +}
|
| +
|
| +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) {
|
| + Interrupt(reason);
|
| +}
|
| +
|
| +void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) {
|
| + if (!IsInProgress())
|
| + return;
|
| + OnAllDataSaved(final_hash);
|
| + delegate_->MaybeCompleteDownload(this);
|
| }
|
|
|
| void DownloadItemImpl::Init(bool active,
|
| @@ -1042,7 +1148,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(),
|
| @@ -1054,7 +1161,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());
|
| }
|
|
|