Chromium Code Reviews| Index: content/browser/download/download_manager_impl.cc |
| diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc |
| index 77b4a20a0bfb1ebb9fbb821dcec9c46f1a96da7b..0d7507037f02a336fa46e2f076a1dd3dbdaf0603 100644 |
| --- a/content/browser/download/download_manager_impl.cc |
| +++ b/content/browser/download/download_manager_impl.cc |
| @@ -39,6 +39,7 @@ |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/resource_context.h" |
| #include "content/public/browser/web_contents_delegate.h" |
| +#include "content/public/common/referrer.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/upload_data.h" |
| #include "net/url_request/url_request_context.h" |
| @@ -75,7 +76,8 @@ class SavePageData : public base::SupportsUserData::Data { |
| const char SavePageData::kKey[] = "DownloadItem SavePageData"; |
| -void BeginDownload(content::DownloadUrlParameters* params) { |
| +void BeginDownload(content::DownloadUrlParameters* params, |
| + DownloadId download_id) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and |
| // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so |
| @@ -117,6 +119,9 @@ void BeginDownload(content::DownloadUrlParameters* params) { |
| params->render_view_host_routing_id(), |
| params->prefer_cache(), |
| params->save_info(), |
| + params->last_modified(), |
| + params->etag(), |
| + download_id, |
| params->callback()); |
| } |
| @@ -177,10 +182,8 @@ class DownloadItemFactoryImpl : public content::DownloadItemFactory { |
| virtual DownloadItemImpl* CreateActiveItem( |
| DownloadItemImplDelegate* delegate, |
| const DownloadCreateInfo& info, |
| - scoped_ptr<DownloadRequestHandleInterface> request_handle, |
| const net::BoundNetLog& bound_net_log) OVERRIDE { |
| - return new DownloadItemImpl(delegate, info, request_handle.Pass(), |
| - bound_net_log); |
| + return new DownloadItemImpl(delegate, info, bound_net_log); |
| } |
| virtual DownloadItemImpl* CreateSavePageItem( |
| @@ -353,6 +356,9 @@ DownloadItem* DownloadManagerImpl::StartDownload( |
| scoped_ptr<content::ByteStreamReader> stream) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + bool resuming = |
| + (downloads_.find(info->download_id.local()) != downloads_.end()); |
| + |
| // |bound_net_log| will be used for logging both the download item's and |
| // the download file's events. |
| net::BoundNetLog bound_net_log = CreateDownloadItem(info.get()); |
| @@ -369,7 +375,9 @@ DownloadItem* DownloadManagerImpl::StartDownload( |
| } |
| DownloadFileManager::CreateDownloadFileCallback callback( |
| - base::Bind(&DownloadManagerImpl::OnDownloadFileCreated, |
| + base::Bind(resuming ? |
| + &DownloadManagerImpl::ContinueStartingDownload : |
| + &DownloadManagerImpl::OnDownloadFileCreated, |
| this, download_id.local())); |
| BrowserThread::PostTask( |
| @@ -383,8 +391,32 @@ DownloadItem* DownloadManagerImpl::StartDownload( |
| return GetDownload(download_id.local()); |
| } |
| +// We have received a message from DownloadFileManager about a new download. |
| +void DownloadManagerImpl::ContinueStartingDownload( |
| + int32 download_id, content::DownloadInterruptReason reason) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + if (reason != content::DOWNLOAD_INTERRUPT_REASON_NONE) { |
| + OnDownloadInterrupted(download_id, reason); |
| + // 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;". |
| + } |
| + |
| + DownloadItem* download = GetActiveDownload(download_id); |
| + if (!download) |
| + return; |
| + |
| + MaybeCompleteDownload(download_id); |
| +} |
| + |
| void DownloadManagerImpl::OnDownloadFileCreated( |
| int32 download_id, content::DownloadInterruptReason reason) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (reason != content::DOWNLOAD_INTERRUPT_REASON_NONE) { |
| OnDownloadInterrupted(download_id, reason); |
| // TODO(rdsmith): It makes no sense to continue along the |
| @@ -479,18 +511,44 @@ net::BoundNetLog DownloadManagerImpl::CreateDownloadItem( |
| DownloadCreateInfo* info) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - net::BoundNetLog bound_net_log = |
| - net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD); |
| + // |bound_net_log| will be used for logging the both the download item's and |
| + // the download file's events. |
| + net::BoundNetLog bound_net_log; |
| + |
| if (!info->download_id.IsValid()) |
| info->download_id = GetNextId(); |
| - DownloadItemImpl* download = factory_->CreateActiveItem( |
| - this, *info, |
| - scoped_ptr<DownloadRequestHandleInterface>( |
| - new DownloadRequestHandle(info->request_handle)).Pass(), |
| - bound_net_log); |
| - DCHECK(!ContainsKey(downloads_, download->GetId())); |
| - downloads_[download->GetId()] = download; |
| + int32 download_id = info->download_id.local(); |
| + DownloadItemImpl* download = NULL; |
| + scoped_ptr<DownloadRequestHandleInterface> req_handle( |
| + new DownloadRequestHandle(info->request_handle)); |
| + bool resuming = (downloads_.find(download_id) != downloads_.end()); |
| + if (resuming) { |
| + download = downloads_[download_id]; |
| + DCHECK(download != NULL); |
| + } |
| + |
| + if (download) { |
| + // Reuse an interrupted |DownloadItem|. |
| + DCHECK(download->IsInterrupted()); |
| + bound_net_log = download->GetBoundNetLog(); // Connect to its net log. |
| + download->Resume(req_handle.Pass()); |
| + } else { |
| + // Create a new |DownloadItem|. |
| + bound_net_log = |
| + net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD); |
| + download = factory_->CreateActiveItem(this, *info, bound_net_log); |
| + |
| + download->SetRequest(req_handle.Pass()); |
| + |
| + DCHECK(!ContainsKey(downloads_, download->GetId())); |
| + downloads_[download->GetId()] = download; |
| + } |
| + |
| + DVLOG(20) << __FUNCTION__ << "()" |
| + << " info " << info->DebugString() |
| + << " download " << download->DebugString(true); |
| + |
| DCHECK(!ContainsKey(active_downloads_, download->GetId())); |
| active_downloads_[download->GetId()] = download; |
| FOR_EACH_OBSERVER(Observer, observers_, OnDownloadCreated(this, download)); |
| @@ -561,7 +619,7 @@ void DownloadManagerImpl::OnResponseCompleted(int32 download_id, |
| DownloadItemImpl* download = active_downloads_[download_id]; |
| download->OnAllDataSaved(size, hash); |
| - MaybeCompleteDownload(download); |
| + MaybeCompleteDownload(download_id); |
| } |
| void DownloadManagerImpl::AssertStateConsistent( |
| @@ -616,11 +674,18 @@ bool DownloadManagerImpl::IsDownloadReadyForCompletion( |
| // downloads. SavePackage always uses its own Finish() to mark downloads |
| // complete. |
| -void DownloadManagerImpl::MaybeCompleteDownload( |
| - DownloadItemImpl* download) { |
| +void DownloadManagerImpl::MaybeCompleteDownload(int32 download_id) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + // This can be called from |DownloadItemImpl::OnDownloadedFileRemoved()|, |
| + // without the download being active. |
| + DCHECK(ContainsKey(downloads_, download_id)); |
| + if (!ContainsKey(active_downloads_, download_id)) |
| + return; |
| + DownloadItemImpl* download = active_downloads_[download_id]; |
| + |
| VLOG(20) << __FUNCTION__ << "()" << " download = " |
| - << download->DebugString(false); |
| + << download->DebugString(true); |
| if (!IsDownloadReadyForCompletion(download)) |
| return; |
| @@ -656,7 +721,7 @@ void DownloadManagerImpl::MaybeCompleteDownload( |
| void DownloadManagerImpl::MaybeCompleteDownloadById(int download_id) { |
| if (ContainsKey(active_downloads_, download_id)) |
| - MaybeCompleteDownload(active_downloads_[download_id]); |
| + MaybeCompleteDownload(download_id); |
| } |
| void DownloadManagerImpl::DownloadCompleted(DownloadItemImpl* download) { |
| @@ -669,10 +734,13 @@ void DownloadManagerImpl::DownloadCompleted(DownloadItemImpl* download) { |
| } |
| void DownloadManagerImpl::CancelDownload(int32 download_id) { |
| + DownloadItem* download = GetActiveDownload(download_id); |
| // A cancel at the right time could remove the download from the |
| // |active_downloads_| map before we get here. |
| - if (ContainsKey(active_downloads_, download_id)) |
| - active_downloads_[download_id]->Cancel(true); |
| + if (!download) |
| + return; |
| + |
| + download->Cancel(true); |
| } |
| void DownloadManagerImpl::DownloadStopped(DownloadItemImpl* download) { |
| @@ -687,7 +755,11 @@ void DownloadManagerImpl::DownloadStopped(DownloadItemImpl* download) { |
| AssertStateConsistent(download); |
| DCHECK(file_manager_); |
| - download->OffThreadCancel(); |
| + |
| + if (download->IsInterrupted()) |
| + download->OffThreadInterrupt(); |
| + else |
| + download->OffThreadCancel(); |
| } |
| void DownloadManagerImpl::OnDownloadInterrupted( |
| @@ -697,9 +769,80 @@ void DownloadManagerImpl::OnDownloadInterrupted( |
| if (!ContainsKey(active_downloads_, download_id)) |
| return; |
| + |
| + VLOG(20) << __FUNCTION__ << "()" |
| + << " reason " << InterruptReasonDebugString(reason) |
| + << " download = " |
| + << active_downloads_[download_id]->DebugString(true); |
| + |
| active_downloads_[download_id]->Interrupt(reason); |
| } |
| +// Resume a download of a specific URL. We send the request to the |
| +// ResourceDispatcherHost, and let it send us responses like a regular |
| +// download. |
| +void DownloadManagerImpl::RestartInterruptedDownload( |
| + DownloadItemImpl* download, |
| + const content::DownloadUrlParameters::OnStartedCallback& callback) { |
|
Randy Smith (Not in Mondays)
2012/10/15 00:36:08
If we've already got a download item, let's not us
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + // Handle the case of clicking 'Resume' in the download shelf. |
| + DCHECK(download); |
| + DCHECK(download->IsInterrupted()); |
| + |
| + DVLOG(20) << __FUNCTION__ << "()" |
| + << " download = " << download->DebugString(true); |
| + |
| + // Restart the download. |
| + WebContents* contents = GetWebContents(download); |
| + if (!contents) { |
| + if (!callback.is_null()) |
| + callback.Run(download, net::ERR_ACCESS_DENIED); |
| + return; |
| + } |
| + |
| + content::DownloadSaveInfo save_info; |
| + save_info.file_path = download->GetFullPath(); |
| + save_info.offset = download->GetReceivedBytes(); |
| + save_info.hash_state = download->GetHashState(); |
| + |
| + content::DownloadUrlParameters* download_params = |
| + new content::DownloadUrlParameters( |
| + download->GetOriginalUrl(), |
| + contents->GetRenderProcessHost()->GetID(), |
| + contents->GetRenderViewHost()->GetRoutingID(), |
| + contents->GetBrowserContext()->GetResourceContext(), |
| + save_info); |
| + |
| + content::Referrer referrer(download->GetReferrerUrl(), |
| + WebKit::WebReferrerPolicyDefault); |
| + download_params->set_referrer(referrer); |
| + download_params->set_last_modified(download->GetLastModifiedTime()); |
| + download_params->set_etag(download->GetETag()); |
| + download_params->set_callback(callback); |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind( |
| + &BeginDownload, |
| + base::Owned(download_params), |
| + download->GetGlobalId())); |
| +} |
| + |
| +DownloadItem* DownloadManagerImpl::GetActiveDownload(int32 download_id) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + DownloadMap::iterator it = active_downloads_.find(download_id); |
| + if (it == active_downloads_.end()) |
| + return NULL; |
| + |
| + DownloadItem* download = it->second; |
| + |
| + DCHECK(download); |
| + DCHECK_EQ(download_id, download->GetId()); |
| + |
| + return download; |
| +} |
| + |
| void DownloadManagerImpl::RemoveFromActiveList(DownloadItemImpl* download) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(download); |
| @@ -791,8 +934,12 @@ void DownloadManagerImpl::DownloadUrl( |
| DCHECK(params->prefer_cache()); |
| DCHECK(params->method() == "POST"); |
| } |
| - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( |
| - &BeginDownload, base::Owned(params.release()))); |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind( |
| + &BeginDownload, |
| + base::Owned(params.release()), |
| + DownloadId())); |
| } |
| void DownloadManagerImpl::AddObserver(Observer* observer) { |
| @@ -889,9 +1036,10 @@ void DownloadManagerImpl::OnDownloadItemAddedToPersistentStore( |
| // completion status, and also inform any observers so that they get |
| // more than just the start notification. |
| if (item->IsInProgress()) { |
| - MaybeCompleteDownload(item); |
| + MaybeCompleteDownload(item->GetId()); |
| } else { |
| - DCHECK(item->IsCancelled()); |
| + DCHECK(item->IsCancelled() || item->IsInterrupted()) |
| + << " download = " << item->DebugString(true); |
| active_downloads_.erase(item->GetId()); |
| if (delegate_) |
| delegate_->UpdateItemInPersistentStore(item); |
| @@ -902,12 +1050,7 @@ void DownloadManagerImpl::OnDownloadItemAddedToPersistentStore( |
| void DownloadManagerImpl::ShowDownloadInBrowser(DownloadItemImpl* download) { |
| // The 'contents' may no longer exist if the user closed the contents before |
| // we get this start completion event. |
| - WebContents* content = download->GetWebContents(); |
| - |
| - // If the contents no longer exists, we ask the embedder to suggest another |
| - // contents. |
| - if (!content && delegate_) |
| - content = delegate_->GetAlternativeWebContentsToNotifyForDownload(); |
| + WebContents* content = GetWebContents(download); |
| if (content && content->GetDelegate()) |
| content->GetDelegate()->OnStartDownload(content, download); |
| @@ -1046,3 +1189,14 @@ void DownloadManagerImpl::DownloadRenamedToFinalName( |
| download, download->GetFullPath()); |
| } |
| } |
| + |
| +WebContents* DownloadManagerImpl::GetWebContents(const DownloadItem* download) { |
| + WebContents* web_contents = download->GetWebContents(); |
| + // If the original download_id was created from the history DB, its |
| + // |DownloadRequestHandle| will not be valid, and we won't get a proper |
| + // |WebContents| pointer from it. |
| + if (!web_contents) |
| + web_contents = delegate_->GetAlternativeWebContentsToNotifyForDownload(); |
| + |
| + return web_contents; |
| +} |