Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(456)

Unified Diff: content/browser/download/download_item_impl.cc

Issue 10831302: Download resumption - Preliminary (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fixed content unit tests. Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 d0984add840f7819230ee1b52fbbf8977952da43..582aa8517a045ba4ad266134102b32793cea03db 100644
--- a/content/browser/download/download_item_impl.cc
+++ b/content/browser/download/download_item_impl.cc
@@ -46,6 +46,7 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/download_persistent_store_info.h"
+#include "content/public/browser/download_url_parameters.h"
#include "net/base/net_util.h"
using content::BrowserThread;
@@ -58,7 +59,7 @@ using content::WebContents;
namespace {
-static void DeleteDownloadedFile(const FilePath& path) {
+void DeleteDownloadedFile(const FilePath& path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
// Make sure we only delete files.
@@ -99,6 +100,8 @@ class NullDownloadRequestHandle : public DownloadRequestHandleInterface {
virtual void PauseRequest() const OVERRIDE {}
virtual void ResumeRequest() const OVERRIDE {}
virtual void CancelRequest() const OVERRIDE {}
+ virtual void SetRequestId(int new_request_id) OVERRIDE {}
+ virtual int RequestId() const OVERRIDE { return -1; }
virtual std::string DebugString() const OVERRIDE {
return "Null DownloadRequestHandle";
}
@@ -119,6 +122,9 @@ const char DownloadItem::kEmptyFileHash[] = "";
}
+// The maximum number of attempts we will make to resume automatically.
+const int DownloadItemImpl::kMaxAutoResumeAttempts = 5;
+
// Our download table ID starts at 1, so we use 0 to represent a download that
// has started, but has not yet had its data persisted in the table. We use fake
// database handles in incognito mode starting at -1 and progressively getting
@@ -149,6 +155,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
db_handle_(info.db_handle),
delegate_(delegate),
is_paused_(false),
+ is_resuming_(false),
+ auto_resume_count_(0),
open_when_complete_(false),
file_externally_removed_(false),
safety_state_(SAFE),
@@ -174,10 +182,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
DownloadItemImpl::DownloadItemImpl(
DownloadItemImplDelegate* delegate,
const DownloadCreateInfo& info,
- scoped_ptr<DownloadRequestHandleInterface> request_handle,
const net::BoundNetLog& bound_net_log)
- : request_handle_(request_handle.Pass()),
- download_id_(info.download_id),
+ : download_id_(info.download_id),
target_disposition_(
(info.prompt_user_for_save_location) ?
TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE),
@@ -203,6 +209,8 @@ DownloadItemImpl::DownloadItemImpl(
db_handle_(DownloadItem::kUninitializedHandle),
delegate_(delegate),
is_paused_(false),
+ is_resuming_(false),
+ auto_resume_count_(0),
open_when_complete_(false),
file_externally_removed_(false),
safety_state_(SAFE),
@@ -258,6 +266,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
db_handle_(DownloadItem::kUninitializedHandle),
delegate_(delegate),
is_paused_(false),
+ is_resuming_(false),
+ auto_resume_count_(0),
open_when_complete_(false),
file_externally_removed_(false),
safety_state_(SAFE),
@@ -321,35 +331,42 @@ void DownloadItemImpl::DangerousDownloadValidated() {
UpdateObservers();
- delegate_->MaybeCompleteDownload(this);
+ delegate_->MaybeCompleteDownload(download_id_.local());
}
void DownloadItemImpl::TogglePause() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(state_ == IN_PROGRESS_INTERNAL || state_ == COMPLETING_INTERNAL);
- if (is_paused_)
- request_handle_->ResumeRequest();
- else
- request_handle_->PauseRequest();
- is_paused_ = !is_paused_;
+ DCHECK(IsPartialDownload());
+ if (IsInProgress()) {
+ if (is_paused_)
+ request_handle_->ResumeRequest();
+ else
+ request_handle_->PauseRequest();
+ is_paused_ = !is_paused_;
+ } else if (IsInterrupted()) {
+ auto_resume_count_ = 0; // User input resets the counter.
+ delegate_->RestartInterruptedDownload(
+ this, content::DownloadUrlParameters::OnStartedCallback());
+ }
+
UpdateObservers();
}
void DownloadItemImpl::Cancel(bool user_cancel) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- last_reason_ = user_cancel ?
- content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED :
- content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN;
-
VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
- if (state_ != IN_PROGRESS_INTERNAL) {
+ if (state_ != IN_PROGRESS_INTERNAL && state_ != INTERRUPTED_INTERNAL) {
// Small downloads might be complete before this method has
// a chance to run.
return;
}
+ last_reason_ = user_cancel ?
+ content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED :
+ content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN;
+
download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT);
TransitionTo(CANCELLED_INTERNAL);
@@ -456,6 +473,87 @@ bool DownloadItemImpl::IsPaused() const {
return is_paused_;
}
+bool DownloadItemImpl::IsResuming() const {
+ return is_resuming_;
+}
+
+void DownloadItemImpl::SetRequest(
+ scoped_ptr<DownloadRequestHandleInterface> request_handle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ request_handle_ = request_handle.Pass();
+}
+
+DownloadItem::ResumeMode DownloadItemImpl::CanResumeInterrupted() const {
+ if (!IsInterrupted() || !is_persisted_)
+ return RESUME_MODE_INVALID;
+
+ ResumeMode mode = RESUME_MODE_INVALID;
+
+ switch(last_reason_) {
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR:
+ case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT:
+ mode = RESUME_MODE_IMMEDIATE_CONTINUE; // Continue immediately.
+ break;
+
+ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION:
+ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE:
+ mode = RESUME_MODE_IMMEDIATE_RESTART; // Restart immediately.
+ break;
+
+ case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED:
+ case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED:
+ case content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN:
+ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED:
+ case content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN:
+ case content::DOWNLOAD_INTERRUPT_REASON_CRASH:
+ mode = RESUME_MODE_USER_CONTINUE; // Continue via user input.
+ break;
+
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
+ mode = RESUME_MODE_USER_RESTART; // Restart via user input.
+ break;
+
+ case content::DOWNLOAD_INTERRUPT_REASON_NONE:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED:
+ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT:
+ case content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED:
+ break;
+ }
+
+ // Check if we have exhausted the number of automatic retry attempts.
+ if (auto_resume_count_ >= kMaxAutoResumeAttempts) {
+ if (mode == RESUME_MODE_IMMEDIATE_CONTINUE)
+ mode = RESUME_MODE_USER_CONTINUE;
+ else if (mode == RESUME_MODE_IMMEDIATE_RESTART)
+ mode = RESUME_MODE_USER_RESTART;
+ }
+
+ return mode;
+}
+
+void DownloadItemImpl::AutoResumeIfValid() {
+ ResumeMode mode = CanResumeInterrupted();
+
+ if ((mode == RESUME_MODE_IMMEDIATE_RESTART) ||
+ (mode == RESUME_MODE_IMMEDIATE_CONTINUE)) {
+ if (mode == RESUME_MODE_IMMEDIATE_RESTART) {
+ received_bytes_ = 0; // Restart instead of continuing.
+ hash_state_ = "";
+ last_modified_time_ = "";
+ etag_ = "";
+ }
+
+ auto_resume_count_++;
+
+ delegate_->RestartInterruptedDownload(
+ this, content::DownloadUrlParameters::OnStartedCallback());
+ }
+}
+
bool DownloadItemImpl::IsTemporary() const {
return is_temporary_;
}
@@ -464,10 +562,9 @@ bool DownloadItemImpl::IsPersisted() const {
return is_persisted_;
}
-// TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to
-// |IsPartialDownload()|, when resuming interrupted downloads is implemented.
bool DownloadItemImpl::IsPartialDownload() const {
- return InternalToExternalState(state_) == IN_PROGRESS;
+ DownloadState state = InternalToExternalState(state_);
+ return (state == IN_PROGRESS) || (state == INTERRUPTED);
benjhayden 2012/10/15 17:59:37 Have you audited all the callers of IsPartialDownl
}
bool DownloadItemImpl::IsInProgress() const {
@@ -475,8 +572,7 @@ bool DownloadItemImpl::IsInProgress() const {
}
bool DownloadItemImpl::IsCancelled() const {
- DownloadState external_state = InternalToExternalState(state_);
- return external_state == CANCELLED || external_state == INTERRUPTED;
benjhayden 2012/10/15 17:59:37 Have you audited all the callers of IsCancelled()
+ return InternalToExternalState(state_) == CANCELLED;
}
bool DownloadItemImpl::IsInterrupted() const {
@@ -664,6 +760,21 @@ bool DownloadItemImpl::CanOpenDownload() {
return !file_externally_removed_;
}
+bool DownloadItemImpl::CanResumeDownload() const {
+ // Do not allow interrupted downloads to be resumed until the target name
+ // has been determined, and the user has validated any dangerous items.
+ // TODO(rdsmith) -- Add a state indicating that filename determination has
+ // occurred.
+ if (GetTargetFilePath().empty())
+ return false;
+
+ if (GetSafetyState() == DANGEROUS)
+ return false;
+
+ return IsInProgress() ||
+ (CanResumeInterrupted() != DownloadItem::RESUME_MODE_INVALID);
+}
+
bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() {
return delegate_->ShouldOpenFileBasedOnExtension(GetUserVerifiedFilePath());
}
@@ -760,7 +871,11 @@ std::string DownloadItemImpl::DebugString(bool verbose) const {
" received = %" PRId64
" reason = %s"
" paused = %c"
+ " resuming = %c"
+ " resume_mode = %s"
" safety = %s"
+ " all_data_saved = %c"
+ " persisted = %c"
" last_modified = '%s'"
" etag = '%s'"
" url_chain = \n\t\"%s\"\n\t"
@@ -771,7 +886,11 @@ std::string DownloadItemImpl::DebugString(bool verbose) const {
GetReceivedBytes(),
InterruptReasonDebugString(last_reason_).c_str(),
IsPaused() ? 'T' : 'F',
+ IsResuming() ? 'T' : 'F',
+ DebugResumeModeString(CanResumeInterrupted()),
DebugSafetyStateString(GetSafetyState()),
+ AllDataSaved() ? 'T' : 'F',
+ IsPersisted() ? 'T' : 'F',
GetLastModifiedTime().c_str(),
GetETag().c_str(),
url_list.c_str(),
@@ -797,6 +916,7 @@ void DownloadItemImpl::NotifyRemoved() {
void DownloadItemImpl::OnDownloadedFileRemoved() {
file_externally_removed_ = true;
UpdateObservers();
+ delegate_->MaybeCompleteDownload(download_id_.local());
benjhayden 2012/10/15 17:59:37 Sorry, what's this doing? I looked in download_man
}
void DownloadItemImpl::OffThreadCancel() {
@@ -809,6 +929,19 @@ void DownloadItemImpl::OffThreadCancel() {
delegate_->GetDownloadFileManager(), download_id_));
}
+void DownloadItemImpl::OffThreadInterrupt() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ request_handle_->CancelRequest();
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&DownloadFileManager::InterruptDownload,
+ delegate_->GetDownloadFileManager(),
+ download_id_,
+ base::Closure()));
+}
+
// An error occurred somewhere.
void DownloadItemImpl::Interrupt(content::DownloadInterruptReason reason) {
// Somewhat counter-intuitively, it is possible for us to receive an
@@ -825,10 +958,25 @@ void DownloadItemImpl::Interrupt(content::DownloadInterruptReason reason) {
return;
last_reason_ = reason;
+ is_resuming_ = false;
TransitionTo(INTERRUPTED_INTERNAL);
download_stats::RecordDownloadInterrupted(
reason, received_bytes_, total_bytes_);
delegate_->DownloadStopped(this);
+
+ AutoResumeIfValid();
+}
+
+void DownloadItemImpl::Resume(
+ scoped_ptr<DownloadRequestHandleInterface> req_handle) {
+ DVLOG(20) << __FUNCTION__ << "() " << DebugString(true);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (state_ != INTERRUPTED_INTERNAL)
+ return;
+ request_handle_ = req_handle.Pass();
+ is_resuming_ = true;
+ TransitionTo(IN_PROGRESS_INTERNAL);
}
void DownloadItemImpl::SetTotalBytes(int64 total_bytes) {
@@ -1014,7 +1162,7 @@ void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
void DownloadItemImpl::MaybeCompleteDownload() {
// TODO(rdsmith): Move logic for this function here.
- delegate_->MaybeCompleteDownload(this);
+ delegate_->MaybeCompleteDownload(download_id_.local());
}
// Called by DownloadManagerImpl::MaybeCompleteDownload() when it has
@@ -1269,6 +1417,10 @@ DownloadItemImpl::ExternalToInternalState(
return MAX_DOWNLOAD_INTERNAL_STATE;
}
+const net::BoundNetLog& DownloadItemImpl::GetBoundNetLog() const {
+ return bound_net_log_;
+}
+
const char* DownloadItemImpl::DebugDownloadStateString(
DownloadInternalState state) {
switch (state) {
@@ -1287,3 +1439,22 @@ const char* DownloadItemImpl::DebugDownloadStateString(
return "unknown";
};
}
+
+const char* DownloadItemImpl::DebugResumeModeString(
+ DownloadItem::ResumeMode mode) {
+ switch (mode) {
+ case DownloadItem::RESUME_MODE_INVALID:
+ return "INVALID";
+ case DownloadItem::RESUME_MODE_IMMEDIATE_CONTINUE:
+ return "IMMEDIATE_CONTINUE";
+ case DownloadItem::RESUME_MODE_IMMEDIATE_RESTART:
+ return "IMMEDIATE_RESTART";
+ case DownloadItem::RESUME_MODE_USER_CONTINUE:
+ return "USER_CONTINUE";
+ case DownloadItem::RESUME_MODE_USER_RESTART:
+ return "USER_RESTART";
+ default:
+ NOTREACHED() << "Unknown resume mode " << mode;
+ return "unknown";
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698