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 ecddd9458c921bdb15e1acbb776e7e5a26baa447..5643698d0e969439f8cfdde8b0663b1b60607b97 100644 |
--- a/content/browser/download/download_item_impl.cc |
+++ b/content/browser/download/download_item_impl.cc |
@@ -41,15 +41,21 @@ |
#include "content/browser/download/download_item_impl_delegate.h" |
#include "content/browser/download/download_request_handle.h" |
#include "content/browser/download/download_stats.h" |
+#include "content/browser/renderer_host/render_view_host_impl.h" |
#include "content/browser/web_contents/web_contents_impl.h" |
+#include "content/public/browser/browser_context.h" |
#include "content/public/browser/browser_thread.h" |
#include "content/public/browser/content_browser_client.h" |
+#include "content/public/browser/download_interrupt_reasons.h" |
+#include "content/public/browser/download_url_parameters.h" |
+#include "content/public/common/referrer.h" |
#include "net/base/net_util.h" |
namespace content { |
+ |
namespace { |
-static void DeleteDownloadedFile(const FilePath& path) { |
+void DeleteDownloadedFile(const FilePath& path) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
// Make sure we only delete files. |
@@ -90,6 +96,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"; |
} |
@@ -112,6 +120,9 @@ static void DownloadFileCancel(scoped_ptr<DownloadFile> download_file) { |
const char DownloadItem::kEmptyFileHash[] = ""; |
+// The maximum number of attempts we will make to resume automatically. |
+const int DownloadItemImpl::kMaxAutoResumeAttempts = 5; |
+ |
// Constructor for reading from the history service. |
DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, |
DownloadId download_id, |
@@ -145,6 +156,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, |
end_time_(end_time), |
delegate_(delegate), |
is_paused_(false), |
+ is_resuming_(false), |
+ auto_resume_count_(0), |
open_when_complete_(false), |
file_externally_removed_(false), |
safety_state_(SAFE), |
@@ -168,10 +181,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, |
DownloadItemImpl::DownloadItemImpl( |
DownloadItemImplDelegate* delegate, |
const DownloadCreateInfo& info, |
- scoped_ptr<DownloadRequestHandleInterface> request_handle, |
const net::BoundNetLog& bound_net_log) |
: is_save_package_download_(false), |
- request_handle_(request_handle.Pass()), |
download_id_(info.download_id), |
target_disposition_( |
(info.save_info->prompt_for_save_location) ? |
@@ -196,6 +207,8 @@ DownloadItemImpl::DownloadItemImpl( |
start_time_(info.start_time), |
delegate_(delegate), |
is_paused_(false), |
+ is_resuming_(false), |
+ auto_resume_count_(0), |
open_when_complete_(false), |
file_externally_removed_(false), |
safety_state_(SAFE), |
@@ -249,6 +262,8 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, |
start_time_(base::Time::Now()), |
delegate_(delegate), |
is_paused_(false), |
+ is_resuming_(false), |
+ auto_resume_count_(0), |
open_when_complete_(false), |
file_externally_removed_(false), |
safety_state_(SAFE), |
@@ -322,35 +337,41 @@ void DownloadItemImpl::DangerousDownloadValidated() { |
void DownloadItemImpl::TogglePause() { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- DCHECK(state_ == IN_PROGRESS_INTERNAL || state_ == COMPLETING_INTERNAL); |
+ DCHECK(IsPartialDownload()); |
VLOG(20) << __FUNCTION__ << " download=" << DebugString(true); |
// Ignore pauses when we've passed the commit point. |
if (state_ == COMPLETING_INTERNAL) |
return; |
- if (is_paused_) |
- request_handle_->ResumeRequest(); |
- else |
- request_handle_->PauseRequest(); |
- is_paused_ = !is_paused_; |
+ 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. |
+ ResumeInterruptedDownload(); |
+ } |
+ |
UpdateObservers(); |
} |
void DownloadItemImpl::Cancel(bool user_cancel) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- last_reason_ = user_cancel ? |
- DOWNLOAD_INTERRUPT_REASON_USER_CANCELED : |
- 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 ? |
+ DOWNLOAD_INTERRUPT_REASON_USER_CANCELED : |
+ DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN; |
+ |
RecordDownloadCount(CANCELLED_COUNT); |
TransitionTo(CANCELLED_INTERNAL); |
@@ -364,6 +385,7 @@ void DownloadItemImpl::Cancel(bool user_cancel) { |
} |
void DownloadItemImpl::Delete(DeleteReason reason) { |
+ VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
switch (reason) { |
@@ -392,6 +414,7 @@ void DownloadItemImpl::Delete(DeleteReason reason) { |
} |
void DownloadItemImpl::Remove() { |
+ VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
delegate_->AssertStateConsistent(this); |
@@ -461,14 +484,98 @@ bool DownloadItemImpl::IsPaused() const { |
return is_paused_; |
} |
+bool DownloadItemImpl::IsResuming() const { |
+ return is_resuming_; |
+} |
+ |
+bool DownloadItemImpl::CanResumeInterrupted() const { |
+ return GetResumeMode() != RESUME_MODE_INVALID; |
+} |
+ |
+DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ if (!IsInterrupted()) |
+ return RESUME_MODE_INVALID; |
+ |
+ ResumeMode mode = RESUME_MODE_INVALID; |
+ |
+ switch(last_reason_) { |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: |
+ case DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: |
+ mode = RESUME_MODE_IMMEDIATE_CONTINUE; // Continue immediately. |
+ break; |
+ |
+ case DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION: |
+ case DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE: |
+ mode = RESUME_MODE_IMMEDIATE_RESTART; // Restart immediately. |
+ break; |
+ |
+ case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: |
+ case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: |
+ case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: |
+ case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: |
+ case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: |
+ case DOWNLOAD_INTERRUPT_REASON_CRASH: |
+ mode = RESUME_MODE_USER_CONTINUE; // Continue via user input. |
+ break; |
+ |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_FAILED: |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: |
+ mode = RESUME_MODE_USER_RESTART; // Restart via user input. |
+ break; |
+ |
+ case DOWNLOAD_INTERRUPT_REASON_NONE: |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: |
+ case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: |
+ case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: |
+ break; |
+ |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED: |
+ case DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED: |
+ 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() { |
+ DVLOG(20) << __FUNCTION__ << "() " << DebugString(true); |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ ResumeMode mode = GetResumeMode(); |
+ |
+ 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_++; |
+ |
+ ResumeInterruptedDownload(); |
+ } |
+} |
+ |
bool DownloadItemImpl::IsTemporary() const { |
return is_temporary_; |
} |
-// 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); |
} |
bool DownloadItemImpl::IsInProgress() const { |
@@ -476,8 +583,7 @@ bool DownloadItemImpl::IsInProgress() const { |
} |
bool DownloadItemImpl::IsCancelled() const { |
- DownloadState external_state = InternalToExternalState(state_); |
- return external_state == CANCELLED || external_state == INTERRUPTED; |
+ return InternalToExternalState(state_) == CANCELLED; |
} |
bool DownloadItemImpl::IsInterrupted() const { |
@@ -665,6 +771,22 @@ bool DownloadItemImpl::CanOpenDownload() { |
return !file_externally_removed_; |
} |
+bool DownloadItemImpl::CanResumeDownload() const { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ // 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(); |
+} |
+ |
bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() { |
return delegate_->ShouldOpenFileBasedOnExtension(GetUserVerifiedFilePath()); |
} |
@@ -747,24 +869,32 @@ std::string DownloadItemImpl::DebugString(bool verbose) const { |
" received = %" PRId64 |
" reason = %s" |
" paused = %c" |
+ " resuming = %c" |
+ " resume_mode = %s" |
+ " auto_resume_count = %d" |
" safety = %s" |
+ " all_data_saved = %c" |
" last_modified = '%s'" |
" etag = '%s'" |
+ " has_download_file = %s" |
" url_chain = \n\t\"%s\"\n\t" |
- " full_path = \"%" PRFilePath "\"" |
- " target_path = \"%" PRFilePath "\"" |
- " has download file = %s", |
+ " full_path = \"%" PRFilePath "\"\n\t" |
+ " target_path = \"%" PRFilePath "\"", |
GetTotalBytes(), |
GetReceivedBytes(), |
InterruptReasonDebugString(last_reason_).c_str(), |
IsPaused() ? 'T' : 'F', |
+ IsResuming() ? 'T' : 'F', |
+ DebugResumeModeString(GetResumeMode()), |
+ auto_resume_count_, |
DebugSafetyStateString(GetSafetyState()), |
+ AllDataSaved() ? 'T' : 'F', |
GetLastModifiedTime().c_str(), |
GetETag().c_str(), |
+ download_file_.get() ? "true" : "false", |
url_list.c_str(), |
GetFullPath().value().c_str(), |
- GetTargetFilePath().value().c_str(), |
- download_file_.get() ? "true" : "false"); |
+ GetTargetFilePath().value().c_str()); |
} else { |
description += base::StringPrintf(" url = \"%s\"", url_list.c_str()); |
} |
@@ -788,11 +918,6 @@ void DownloadItemImpl::OnDownloadedFileRemoved() { |
UpdateObservers(); |
} |
-base::WeakPtr<DownloadDestinationObserver> |
-DownloadItemImpl::DestinationObserverAsWeakPtr() { |
- return weak_ptr_factory_.GetWeakPtr(); |
-} |
- |
void DownloadItemImpl::SetTotalBytes(int64 total_bytes) { |
total_bytes_ = total_bytes; |
} |
@@ -949,10 +1074,26 @@ void DownloadItemImpl::Init(bool active, |
} |
// We're starting the download. |
-void DownloadItemImpl::Start(scoped_ptr<DownloadFile> file) { |
+void DownloadItemImpl::Start( |
+ scoped_ptr<DownloadFile> file, |
+ scoped_ptr<DownloadRequestHandleInterface> req_handle) { |
+ 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(); |
+ |
+ is_resuming_ = (state_ == INTERRUPTED_INTERNAL); |
+ |
+ TransitionTo(IN_PROGRESS_INTERNAL); |
+ |
+ last_reason_ = DOWNLOAD_INTERRUPT_REASON_NONE; |
+ |
+ if (is_resuming_) { |
+ FOR_EACH_OBSERVER(Observer, observers_, OnDownloadResumed(this)); |
+ } |
BrowserThread::PostTask( |
BrowserThread::FILE, FROM_HERE, |
@@ -965,6 +1106,7 @@ void DownloadItemImpl::Start(scoped_ptr<DownloadFile> file) { |
void DownloadItemImpl::OnDownloadFileInitialized( |
DownloadInterruptReason result) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { |
Interrupt(result); |
// TODO(rdsmith): It makes no sense to continue along the |
@@ -972,7 +1114,7 @@ void DownloadItemImpl::OnDownloadFileInitialized( |
// 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 |
+ // (including queue removal) immediately rather than waiting for |
// persistence we should replace this comment with a "return;". |
} |
@@ -1097,11 +1239,12 @@ void DownloadItemImpl::OnDownloadCompleting() { |
} |
void DownloadItemImpl::ReadyForDownloadCompletionDone() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
if (state_ != IN_PROGRESS_INTERNAL) |
return; |
- VLOG(20) << __FUNCTION__ << "()" |
- << " " << DebugString(true); |
+ VLOG(20) << __FUNCTION__ << "()" << " " << DebugString(true); |
DCHECK(!GetTargetFilePath().empty()); |
DCHECK_NE(DANGEROUS, GetSafetyState()); |
@@ -1180,6 +1323,8 @@ void DownloadItemImpl::OnDownloadRenamedToFinalName( |
} |
void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
auto_opened_ = auto_opened; |
Completed(); |
} |
@@ -1213,8 +1358,41 @@ void DownloadItemImpl::Completed() { |
// **** End of Download progression cascade |
+void DownloadItemImpl::ResumeInterruptedDownload() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ // Handle the case of clicking 'Resume' in the download shelf. |
+ DCHECK(IsInterrupted()); |
+ |
+ DVLOG(20) << __FUNCTION__ << "()" << DebugString(true); |
+ |
+ // Restart the download. |
+ if (!GetWebContents()) { |
+ Interrupt(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); |
+ return; |
+ } |
+ |
+ scoped_ptr<DownloadUrlParameters> download_params( |
+ DownloadUrlParameters::FromWebContents(GetWebContents(), |
+ GetOriginalUrl())); |
+ |
+ download_params->set_file_path(GetFullPath()); |
+ download_params->set_offset(GetReceivedBytes()); |
+ download_params->set_hash_state(GetHashState()); |
+ |
+ Referrer referrer(GetReferrerUrl(), WebKit::WebReferrerPolicyDefault); |
+ download_params->set_referrer(referrer); |
+ download_params->set_last_modified(GetLastModifiedTime()); |
+ download_params->set_etag(GetETag()); |
+ download_params->set_callback(DownloadUrlParameters::OnStartedCallback()); |
+ |
+ delegate_->ResumeInterruptedDownload(download_params.Pass(), GetGlobalId()); |
+} |
+ |
// An error occurred somewhere. |
void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
// Somewhat counter-intuitively, it is possible for us to receive an |
// interrupt after we've already been interrupted. The generation of |
// interrupts from the file thread Renames and the generation of |
@@ -1229,6 +1407,8 @@ void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) { |
return; |
last_reason_ = reason; |
+ is_resuming_ = false; |
+ |
TransitionTo(INTERRUPTED_INTERNAL); |
CancelDownloadFile(); |
@@ -1236,15 +1416,26 @@ void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) { |
// Cancel the originating URL request. |
request_handle_->CancelRequest(); |
+ FOR_EACH_OBSERVER(Observer, observers_, OnDownloadInterrupted(this)); |
+ |
RecordDownloadInterrupted(reason, received_bytes_, total_bytes_); |
delegate_->DownloadStopped(this); |
+ |
+ AutoResumeIfValid(); |
+} |
+ |
+base::WeakPtr<DownloadDestinationObserver> |
+DownloadItemImpl::DestinationObserverAsWeakPtr() { |
+ return weak_ptr_factory_.GetWeakPtr(); |
} |
void DownloadItemImpl::CancelDownloadFile() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
// TODO(rdsmith/benjhayden): Remove condition as part of |
- // SavePackage integration. |
- // download_file_ can be NULL if Interrupt() is called after the download file |
- // has been released. |
+ // |SavePackage| integration. |
+ // |download_file_| can be NULL if Interrupt() is called after the |
+ // download file has been released. |
if (!is_save_package_download_ && download_file_.get()) { |
BrowserThread::PostTask( |
BrowserThread::FILE, FROM_HERE, |
@@ -1259,6 +1450,8 @@ bool DownloadItemImpl::IsDownloadReadyForCompletion() { |
<< " " << (state_ == IN_PROGRESS_INTERNAL) |
<< " " << !GetTargetFilePath().empty() |
<< " " << (target_path_.DirName() == current_path_.DirName()); |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
// If we don't have all the data, the download is not ready for |
// completion. |
if (!AllDataSaved()) |
@@ -1288,6 +1481,8 @@ bool DownloadItemImpl::IsDownloadReadyForCompletion() { |
} |
void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
if (state_ == new_state) |
return; |
@@ -1311,6 +1506,14 @@ void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) { |
base::Bind(&ItemInterruptedNetLogCallback, last_reason_, |
received_bytes_, &hash_state_)); |
break; |
+ case IN_PROGRESS_INTERNAL: |
+ if (is_resuming_) { |
+ bound_net_log_.AddEvent( |
+ net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED, |
+ base::Bind(&ItemResumingNetLogCallback, |
+ false, last_reason_, received_bytes_, &hash_state_)); |
+ } |
+ break; |
case CANCELLED_INTERNAL: |
bound_net_log_.AddEvent( |
net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED, |
@@ -1326,13 +1529,14 @@ void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) { |
<< " " << InternalToExternalState(state_); |
// Only update observers on user visible state changes. |
- if (InternalToExternalState(old_state) != InternalToExternalState(state_)) |
+ DownloadState external_state = InternalToExternalState(state_); |
+ DownloadState old_external_state = InternalToExternalState(old_state); |
+ |
+ if (old_external_state != external_state) |
UpdateObservers(); |
- bool is_done = (state_ != IN_PROGRESS_INTERNAL && |
- state_ != COMPLETING_INTERNAL); |
- bool was_done = (old_state != IN_PROGRESS_INTERNAL && |
- old_state != COMPLETING_INTERNAL); |
+ bool is_done = external_state != IN_PROGRESS; |
+ bool was_done = old_external_state != IN_PROGRESS; |
if (is_done && !was_done) |
bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE); |
} |
@@ -1405,6 +1609,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) { |
@@ -1424,4 +1632,22 @@ const char* DownloadItemImpl::DebugDownloadStateString( |
}; |
} |
+const char* DownloadItemImpl::DebugResumeModeString(ResumeMode mode) { |
+ switch (mode) { |
+ case RESUME_MODE_INVALID: |
+ return "INVALID"; |
+ case RESUME_MODE_IMMEDIATE_CONTINUE: |
+ return "IMMEDIATE_CONTINUE"; |
+ case RESUME_MODE_IMMEDIATE_RESTART: |
+ return "IMMEDIATE_RESTART"; |
+ case RESUME_MODE_USER_CONTINUE: |
+ return "USER_CONTINUE"; |
+ case RESUME_MODE_USER_RESTART: |
+ return "USER_RESTART"; |
+ default: |
+ NOTREACHED() << "Unknown resume mode " << mode; |
+ return "unknown"; |
+ } |
+} |
+ |
} // namespace content |