Index: chrome/browser/download/download_manager.cc |
diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc |
index dd350225a32c87378afe68dcc791e724dcbe8a98..4f404dd27ed4c7fbae337ada118271fb305572fe 100644 |
--- a/chrome/browser/download/download_manager.cc |
+++ b/chrome/browser/download/download_manager.cc |
@@ -75,6 +75,7 @@ void DownloadManager::Shutdown() { |
// 'in_progress_' may contain DownloadItems that have not finished the start |
// complete (from the history service) and thus aren't in downloads_. |
+ // It may also contain DownloadItems that have been interrupted. |
DownloadMap::iterator it = in_progress_.begin(); |
std::set<DownloadItem*> to_remove; |
for (; it != in_progress_.end(); ++it) { |
@@ -86,7 +87,7 @@ void DownloadManager::Shutdown() { |
to_remove.insert(download); |
continue; |
} |
- DCHECK_EQ(DownloadItem::IN_PROGRESS, download->state()); |
+ DCHECK(download->IsPartialDownload()); |
download->Cancel(false); |
download_history_->UpdateEntry(download); |
if (download->db_handle() == DownloadHistory::kUninitializedHandle) { |
@@ -168,10 +169,12 @@ void DownloadManager::GetCurrentDownloads( |
for (DownloadMap::iterator it = downloads_.begin(); |
it != downloads_.end(); ++it) { |
if (!it->second->is_temporary() && |
- (it->second->state() == DownloadItem::IN_PROGRESS || |
- it->second->safety_state() == DownloadItem::DANGEROUS) && |
- (dir_path.empty() || it->second->full_path().DirName() == dir_path)) |
+ (it->second->IsPartialDownload() || |
+ (it->second->safety_state() == DownloadItem::DANGEROUS)) && |
+ (dir_path.empty() || |
+ (it->second->full_path().DirName() == dir_path))) { |
result->push_back(it->second); |
+ } |
} |
} |
@@ -301,7 +304,8 @@ void DownloadManager::StartDownload(DownloadCreateInfo* info) { |
// We need to move over to the download thread because we don't want to stat |
// the suggested path on the UI thread. |
ChromeThread::PostTask( |
- ChromeThread::FILE, FROM_HERE, |
+ ChromeThread::FILE, |
+ FROM_HERE, |
NewRunnableMethod( |
this, &DownloadManager::CheckIfSuggestedPathExists, info)); |
} |
@@ -368,7 +372,8 @@ void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) { |
} |
ChromeThread::PostTask( |
- ChromeThread::UI, FROM_HERE, |
+ ChromeThread::UI, |
+ FROM_HERE, |
NewRunnableMethod(this, |
&DownloadManager::OnPathExistenceAvailable, |
info)); |
@@ -411,10 +416,20 @@ void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info, |
scoped_ptr<DownloadCreateInfo> infop(info); |
info->path = target_path; |
- DownloadItem* download = new DownloadItem(this, *info, |
- profile_->IsOffTheRecord()); |
- DCHECK(!ContainsKey(in_progress_, info->download_id)); |
- in_progress_[info->download_id] = download; |
+ DownloadItem* download = NULL; |
+ // For partial downloads, reuse the existing DownloadItem. |
+ if (info->is_partial_download && (info->download_id != -1)) { |
+ DownloadMap::iterator it = in_progress_.find(info->download_id); |
+ if (it != in_progress_.end()) { |
+ download = it->second; |
+ download->Resumed(info->request_id); |
+ } |
+ } |
+ if (!download) { |
+ download = new DownloadItem(this, *info, profile_->IsOffTheRecord()); |
+ DCHECK(!ContainsKey(in_progress_, info->download_id)); |
+ in_progress_[info->download_id] = download; |
+ } |
bool download_finished = ContainsKey(pending_finished_downloads_, |
info->download_id); |
@@ -424,7 +439,8 @@ void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info, |
// We can now rename the file to its final name (or its tentative name |
// in dangerous download cases). |
ChromeThread::PostTask( |
- ChromeThread::FILE, FROM_HERE, |
+ ChromeThread::FILE, |
+ FROM_HERE, |
NewRunnableMethod( |
file_manager_, &DownloadFileManager::OnFinalDownloadName, |
download->id(), target_path, !info->is_dangerous, this)); |
@@ -433,7 +449,8 @@ void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info, |
// rename it to its intermediate '.crdownload' path. |
FilePath download_path = download_util::GetCrDownloadPath(target_path); |
ChromeThread::PostTask( |
- ChromeThread::FILE, FROM_HERE, |
+ ChromeThread::FILE, |
+ FROM_HERE, |
NewRunnableMethod( |
file_manager_, &DownloadFileManager::OnIntermediateDownloadName, |
download->id(), download_path, this)); |
@@ -465,6 +482,17 @@ void DownloadManager::UpdateDownload(int32 download_id, int64 size) { |
UpdateAppIcon(); |
} |
+void DownloadManager::OnResponseCompleted(int32 download_id, |
+ int64 size, |
+ int os_error) { |
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
+ if (os_error == 0) { |
+ OnAllDataSaved(download_id, size); |
+ } else { |
+ DownloadError(download_id, size, os_error); |
+ } |
+} |
+ |
void DownloadManager::OnAllDataSaved(int32 download_id, int64 size) { |
DownloadMap::iterator it = in_progress_.find(download_id); |
if (it == in_progress_.end()) { |
@@ -509,7 +537,8 @@ void DownloadManager::OnAllDataSaved(int32 download_id, int64 size) { |
// We first need to rename the downloaded file from its temporary name to |
// its final name before we can continue. |
ChromeThread::PostTask( |
- ChromeThread::FILE, FROM_HERE, |
+ ChromeThread::FILE, |
+ FROM_HERE, |
NewRunnableMethod( |
this, &DownloadManager::ProceedWithFinishedDangerousDownload, |
download->db_handle(), |
@@ -519,7 +548,8 @@ void DownloadManager::OnAllDataSaved(int32 download_id, int64 size) { |
if (download->need_final_rename()) { |
ChromeThread::PostTask( |
- ChromeThread::FILE, FROM_HERE, |
+ ChromeThread::FILE, |
+ FROM_HERE, |
NewRunnableMethod( |
file_manager_, &DownloadFileManager::OnFinalDownloadName, |
download->id(), download->full_path(), false, this)); |
@@ -529,6 +559,33 @@ void DownloadManager::OnAllDataSaved(int32 download_id, int64 size) { |
ContinueDownloadFinished(download); |
} |
+void DownloadManager::DownloadError(int32 download_id, |
+ int64 size, |
+ int os_error) { |
+ DownloadMap::iterator it = in_progress_.find(download_id); |
+ // If enough time passes, the download item could be |
+ // removed from the |in_progress_| map before we get here. |
+ if (it == in_progress_.end()) |
+ return; |
+ |
+ DownloadItem* download = it->second; |
+ download->Interrupted(size, os_error); |
+ |
+ LOG(INFO) << "Error " << download->last_os_error() << " at offset " |
+ << download->received_bytes() << " for file at \"" |
+ << download->url().spec() << "\""; |
+ |
+ // Clean up will happen when the history system create callback runs if we |
+ // don't have a valid db_handle yet, and otherwise when we restart. |
+ if (download->db_handle() != DownloadHistory::kUninitializedHandle) |
+ download_history_->UpdateEntry(download); |
+ |
+ UpdateAppIcon(); |
+ |
+ // Notify our observers that we are interrupted. |
+ download->UpdateObservers(); |
+} |
+ |
void DownloadManager::DownloadRenamedToFinalName(int download_id, |
const FilePath& full_path) { |
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
@@ -580,7 +637,8 @@ void DownloadManager::ProceedWithFinishedDangerousDownload( |
} |
ChromeThread::PostTask( |
- ChromeThread::UI, FROM_HERE, |
+ ChromeThread::UI, |
+ FROM_HERE, |
NewRunnableMethod(this, &DownloadManager::DangerousDownloadRenamed, |
download_handle, success, new_path, uniquifier)); |
} |
@@ -633,14 +691,16 @@ void DownloadManager::DownloadCancelledInternal(int download_id, |
int request_id) { |
// Cancel the network request. RDH is guaranteed to outlive the IO thread. |
ChromeThread::PostTask( |
- ChromeThread::IO, FROM_HERE, |
+ ChromeThread::IO, |
+ FROM_HERE, |
NewRunnableFunction(&download_util::CancelDownloadRequest, |
g_browser_process->resource_dispatcher_host(), |
render_process_id, |
request_id)); |
ChromeThread::PostTask( |
- ChromeThread::FILE, FROM_HERE, |
+ ChromeThread::FILE, |
+ FROM_HERE, |
NewRunnableMethod( |
file_manager_, &DownloadFileManager::CancelDownload, download_id)); |
} |
@@ -655,7 +715,8 @@ void DownloadManager::PauseDownload(int32 download_id, bool pause) { |
return; |
ChromeThread::PostTask( |
- ChromeThread::IO, FROM_HERE, |
+ ChromeThread::IO, |
+ FROM_HERE, |
NewRunnableMethod(this, |
&DownloadManager::PauseDownloadRequest, |
g_browser_process->resource_dispatcher_host(), |
@@ -664,6 +725,40 @@ void DownloadManager::PauseDownload(int32 download_id, bool pause) { |
pause)); |
} |
+void DownloadManager::RestartDownload(int32 download_id) { |
+ // Handle the case of clicking 'Resume' in the download shelf. |
+ DownloadMap::iterator it = in_progress_.find(download_id); |
+ if (it == in_progress_.end()) |
+ return; |
+ |
+ DownloadItem* download = it->second; |
+ if (!download->IsInterruptedDownload()) |
+ return; |
+ |
+ // Restart the download. |
+ // |info| is cleaned up in ResourceDispatcherHost::RestartRequestAsync(). |
+ DownloadCreateInfo* info = new DownloadCreateInfo( |
+ download->full_path(), |
+ download->url(), |
+ download->start_time(), |
+ download->received_bytes(), |
+ download->total_bytes(), |
+ DownloadItem::IN_PROGRESS, // Don't want this to show INTERRUPTED. |
+ -1); |
+ info->referrer_url = download->referrer_url(); |
+ |
+ // Inform the ResourceDispatcherHost that we're restarting the download. |
+ ChromeThread::PostTask( |
+ ChromeThread::IO, |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &DownloadManager::RestartDownloadRequest, |
+ g_browser_process->resource_dispatcher_host(), |
+ download->render_process_id(), |
+ download->request_id(), |
+ info)); |
+} |
+ |
void DownloadManager::UpdateAppIcon() { |
int64 total_bytes = 0; |
int64 received_bytes = 0; |
@@ -708,6 +803,14 @@ void DownloadManager::PauseDownloadRequest(ResourceDispatcherHost* rdh, |
rdh->PauseRequest(render_process_id, request_id, pause); |
} |
+void DownloadManager::RestartDownloadRequest(ResourceDispatcherHost* rdh, |
+ int render_process_id, |
+ int request_id, |
+ DownloadCreateInfo* info) { |
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
+ rdh->RestartRequest(render_process_id, request_id, info); |
+} |
+ |
void DownloadManager::RemoveDownload(int64 download_handle) { |
DownloadMap::iterator it = downloads_.find(download_handle); |
if (it == downloads_.end()) |
@@ -805,7 +908,9 @@ void DownloadManager::DownloadUrlToFile(const GURL& url, |
const DownloadSaveInfo& save_info, |
TabContents* tab_contents) { |
DCHECK(tab_contents); |
- ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, |
+ ChromeThread::PostTask( |
+ ChromeThread::IO, |
+ FROM_HERE, |
NewRunnableFunction(&download_util::DownloadUrl, |
url, |
referrer, |
@@ -868,7 +973,8 @@ void DownloadManager::DangerousDownloadValidated(DownloadItem* download) { |
return; |
ChromeThread::PostTask( |
- ChromeThread::FILE, FROM_HERE, |
+ ChromeThread::FILE, |
+ FROM_HERE, |
NewRunnableMethod( |
this, &DownloadManager::ProceedWithFinishedDangerousDownload, |
download->db_handle(), download->full_path(), |
@@ -899,20 +1005,24 @@ void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info, |
DownloadItem* download = it->second; |
- // It's not immediately obvious, but HistoryBackend::CreateDownload() can |
- // call this function with an invalid |db_handle|. For instance, this can |
- // happen when the history database is offline. We cannot have multiple |
- // DownloadItems with the same invalid db_handle, so we need to assign a |
- // unique |db_handle| here. |
- if (db_handle == DownloadHistory::kUninitializedHandle) |
- db_handle = download_history_->GetNextFakeDbHandle(); |
- |
- DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle); |
- download->set_db_handle(db_handle); |
- |
- // Insert into our full map. |
- DCHECK(downloads_.find(download->db_handle()) == downloads_.end()); |
- downloads_[download->db_handle()] = download; |
+ if (download->db_handle() != DownloadHistory::kUninitializedHandle) { |
+ // We're reusing the download. |
+ } else { |
+ // It's not immediately obvious, but HistoryBackend::CreateDownload() can |
+ // call this function with an invalid |db_handle|. For instance, this can |
+ // happen when the history database is offline. We cannot have multiple |
+ // DownloadItems with the same invalid db_handle, so we need to assign a |
+ // unique |db_handle| here. |
+ if (db_handle == DownloadHistory::kUninitializedHandle) |
+ db_handle = download_history_->GetNextFakeDbHandle(); |
+ |
+ DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle); |
+ download->set_db_handle(db_handle); |
+ |
+ // Insert into our full map. |
+ DCHECK(downloads_.find(download->db_handle()) == downloads_.end()); |
+ downloads_[download->db_handle()] = download; |
+ } |
// Show in the appropropriate browser UI. |
ShowDownloadInBrowser(info, download); |
@@ -925,7 +1035,8 @@ void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info, |
// in sync with the DownloadItem's completion status, and also inform any |
// observers so that they get more than just the start notification. |
if (download->state() != DownloadItem::IN_PROGRESS) { |
- in_progress_.erase(it); |
+ if (!download->IsInterruptedDownload()) |
+ in_progress_.erase(it); |
download_history_->UpdateEntry(download); |
download->UpdateObservers(); |
} |