diff options
Diffstat (limited to 'chrome/browser/download/download_file_manager.cc')
-rw-r--r-- | chrome/browser/download/download_file_manager.cc | 288 |
1 files changed, 80 insertions, 208 deletions
diff --git a/chrome/browser/download/download_file_manager.cc b/chrome/browser/download/download_file_manager.cc index 2862742..3c1edf9 100644 --- a/chrome/browser/download/download_file_manager.cc +++ b/chrome/browser/download/download_file_manager.cc @@ -35,6 +35,21 @@ namespace { // cause it to become unresponsive (in milliseconds). const int kUpdatePeriodMs = 500; +DownloadManager* DownloadManagerForRenderViewHost(int render_process_id, + int render_view_id) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + TabContents* contents = tab_util::GetTabContentsByID(render_process_id, + render_view_id); + if (contents) { + Profile* profile = contents->profile(); + if (profile) + return profile->GetDownloadManager(); + } + + return NULL; +} + } // namespace DownloadFileManager::DownloadFileManager(ResourceDispatcherHost* rdh) @@ -43,37 +58,29 @@ DownloadFileManager::DownloadFileManager(ResourceDispatcherHost* rdh) } DownloadFileManager::~DownloadFileManager() { - // Check for clean shutdown. DCHECK(downloads_.empty()); } -// Called during the browser shutdown process to clean up any state (open files, -// timers) that live on the download_thread_. void DownloadFileManager::Shutdown() { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - StopUpdateTimer(); ChromeThread::PostTask( ChromeThread::FILE, FROM_HERE, NewRunnableMethod(this, &DownloadFileManager::OnShutdown)); } -// Cease download thread operations. void DownloadFileManager::OnShutdown() { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + StopUpdateTimer(); STLDeleteValues(&downloads_); } -// Notifications sent from the download thread and run on the UI thread. +void DownloadFileManager::CreateDownloadFile( + DownloadCreateInfo* info, DownloadManager* download_manager) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); -// Lookup the DownloadManager for this TabContents' profile and inform it of -// a new download. -// TODO(paulg): When implementing download restart via the Downloads tab, -// there will be no 'render_process_id' or 'render_view_id'. -void DownloadFileManager::OnStartDownload(DownloadCreateInfo* info) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - DownloadManager* manager = DownloadManagerFromRenderIds(info->child_id, - info->render_view_id); - if (!manager) { + scoped_ptr<DownloadFile> download_file( + new DownloadFile(info, download_manager)); + if (!download_file->Initialize()) { ChromeThread::PostTask( ChromeThread::IO, FROM_HERE, NewRunnableFunction(&download_util::CancelDownloadRequest, @@ -84,58 +91,26 @@ void DownloadFileManager::OnStartDownload(DownloadCreateInfo* info) { return; } - StartUpdateTimer(); + DCHECK(GetDownloadFile(info->download_id) == NULL); + downloads_[info->download_id] = download_file.release(); + // TODO(phajdan.jr): fix the duplication of path info below. + info->path = info->save_info.file_path; - // Add the download manager to our request maps for future updates. We want to - // be able to cancel all in progress downloads when a DownloadManager is - // deleted, such as when a profile is closed. We also want to be able to look - // up the DownloadManager associated with a given request without having to - // rely on using tab information, since a tab may be closed while a download - // initiated from that tab is still in progress. - DownloadRequests& downloads = requests_[manager]; - downloads.insert(info->download_id); - - // TODO(paulg): The manager will exist when restarts are implemented. - DownloadManagerMap::iterator dit = managers_.find(info->download_id); - if (dit == managers_.end()) - managers_[info->download_id] = manager; - else - NOTREACHED(); - - // StartDownload will clean up |info|. - manager->StartDownload(info); -} + StartUpdateTimer(); -// Update the Download Manager with the finish state, and remove the request -// tracking entries. -void DownloadFileManager::OnDownloadFinished(int id, - int64 bytes_so_far) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - DownloadManager* manager = GetDownloadManager(id); - if (manager) - manager->DownloadFinished(id, bytes_so_far); - RemoveDownload(id, manager); - RemoveDownloadFromUIProgress(id); + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(download_manager, + &DownloadManager::StartDownload, info)); } -// Lookup one in-progress download. DownloadFile* DownloadFileManager::GetDownloadFile(int id) { DownloadFileMap::iterator it = downloads_.find(id); return it == downloads_.end() ? NULL : it->second; } -// The UI progress is updated on the file thread and removed on the UI thread. -void DownloadFileManager::RemoveDownloadFromUIProgress(int id) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - AutoLock lock(progress_lock_); - if (ui_progress_.find(id) != ui_progress_.end()) - ui_progress_.erase(id); -} - -// Throttle updates to the UI thread by only posting update notifications at a -// regularly controlled interval. void DownloadFileManager::StartUpdateTimer() { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); if (!update_timer_.IsRunning()) { update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdatePeriodMs), this, &DownloadFileManager::UpdateInProgressDownloads); @@ -143,21 +118,22 @@ void DownloadFileManager::StartUpdateTimer() { } void DownloadFileManager::StopUpdateTimer() { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); update_timer_.Stop(); } -// Our periodic timer has fired so send the UI thread updates on all in progress -// downloads. void DownloadFileManager::UpdateInProgressDownloads() { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - AutoLock lock(progress_lock_); - ProgressMap::iterator it = ui_progress_.begin(); - for (; it != ui_progress_.end(); ++it) { - const int id = it->first; - DownloadManager* manager = GetDownloadManager(id); - if (manager) - manager->UpdateDownload(id, it->second); + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + for (DownloadFileMap::iterator i = downloads_.begin(); + i != downloads_.end(); ++i) { + int id = i->first; + DownloadFile* download_file = i->second; + DownloadManager* manager = download_file->GetDownloadManager(); + if (manager) { + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, + NewRunnableMethod(manager, &DownloadManager::UpdateDownload, + id, download_file->bytes_so_far())); + } } } @@ -168,21 +144,13 @@ int DownloadFileManager::GetNextId() { return next_id_++; } -// Notifications sent from the IO thread and run on the download thread: - -// The IO thread created 'info', but the download thread (this method) uses it -// to create a DownloadFile, then passes 'info' to the UI thread where it is -// finally consumed and deleted. void DownloadFileManager::StartDownload(DownloadCreateInfo* info) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); DCHECK(info); - DownloadFile* download = new DownloadFile(info); - if (!download->Initialize()) { - // Couldn't open, cancel the operation. The UI thread does not yet know - // about this download so we have to clean up 'info'. We need to get back - // to the IO thread to cancel the network request and CancelDownloadRequest - // on the UI thread is the safe way to do that. + DownloadManager* manager = DownloadManagerForRenderViewHost( + info->child_id, info->render_view_id); + if (!manager) { ChromeThread::PostTask( ChromeThread::IO, FROM_HERE, NewRunnableFunction(&download_util::CancelDownloadRequest, @@ -190,22 +158,12 @@ void DownloadFileManager::StartDownload(DownloadCreateInfo* info) { info->child_id, info->request_id)); delete info; - delete download; return; } - DCHECK(GetDownloadFile(info->download_id) == NULL); - downloads_[info->download_id] = download; - // TODO(phajdan.jr): fix the duplication of path info below. - info->path = info->save_info.file_path; - { - AutoLock lock(progress_lock_); - ui_progress_[info->download_id] = info->received_bytes; - } - - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &DownloadFileManager::OnStartDownload, info)); + ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(this, &DownloadFileManager::CreateDownloadFile, + info, manager)); } // We don't forward an update to the UI thread here, since we want to throttle @@ -221,25 +179,14 @@ void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { contents.swap(buffer->contents); } - // Keep track of how many bytes we have successfully saved to update - // our progress status in the UI. - int64 progress_bytes = 0; - DownloadFile* download = GetDownloadFile(id); for (size_t i = 0; i < contents.size(); ++i) { net::IOBuffer* data = contents[i].first; const int data_len = contents[i].second; - if (download) { - if (download->AppendDataToFile(data->data(), data_len)) - progress_bytes += data_len; - } + if (download) + download->AppendDataToFile(data->data(), data_len); data->Release(); } - - if (download) { - AutoLock lock(progress_lock_); - ui_progress_[download->id()] += progress_bytes; - } } void DownloadFileManager::DownloadFinished(int id, DownloadBuffer* buffer) { @@ -250,18 +197,15 @@ void DownloadFileManager::DownloadFinished(int id, DownloadBuffer* buffer) { DownloadFile* download = it->second; download->Finish(); - int64 download_size = -1; - { - AutoLock lock(progress_lock_); - download_size = ui_progress_[download->id()]; + DownloadManager* download_manager = download->GetDownloadManager(); + if (download_manager) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + download_manager, &DownloadManager::DownloadFinished, + id, download->bytes_so_far())); } - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod( - this, &DownloadFileManager::OnDownloadFinished, - id, download_size)); - // We need to keep the download around until the UI thread has finalized // the name. if (download->path_renamed()) { @@ -271,9 +215,7 @@ void DownloadFileManager::DownloadFinished(int id, DownloadBuffer* buffer) { } if (downloads_.empty()) - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &DownloadFileManager::StopUpdateTimer)); + StopUpdateTimer(); } // This method will be sent via a user action, or shutdown on the UI thread, and @@ -286,94 +228,26 @@ void DownloadFileManager::CancelDownload(int id) { DownloadFile* download = it->second; download->Cancel(); - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod( - this, &DownloadFileManager::RemoveDownloadFromUIProgress, - download->id())); - if (download->path_renamed()) { downloads_.erase(it); delete download; } } - if (downloads_.empty()) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &DownloadFileManager::StopUpdateTimer)); - } -} - -// Relate a download ID to its owning DownloadManager. -DownloadManager* DownloadFileManager::GetDownloadManager(int download_id) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - DownloadManagerMap::iterator it = managers_.find(download_id); - if (it != managers_.end()) - return it->second; - return NULL; -} - -// Utility function for look up table maintenance, called on the UI thread. -// A manager may have multiple downloads in progress, so we just look up the -// one download (id) and remove it from the set, and remove the set if it -// becomes empty. -void DownloadFileManager::RemoveDownload(int id, DownloadManager* manager) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - if (manager) { - RequestMap::iterator it = requests_.find(manager); - if (it != requests_.end()) { - DownloadRequests& downloads = it->second; - DownloadRequests::iterator rit = downloads.find(id); - if (rit != downloads.end()) - downloads.erase(rit); - if (downloads.empty()) - requests_.erase(it); - } - } - - // A download can only have one manager, so remove it if it exists. - DownloadManagerMap::iterator dit = managers_.find(id); - if (dit != managers_.end()) - managers_.erase(dit); -} - -// Utility function for converting request IDs to a TabContents. Must be called -// only on the UI thread since Profile operations may create UI objects, such as -// the first call to profile->GetDownloadManager(). -// static -DownloadManager* DownloadFileManager::DownloadManagerFromRenderIds( - int render_process_id, int render_view_id) { - TabContents* contents = tab_util::GetTabContentsByID(render_process_id, - render_view_id); - if (contents) { - Profile* profile = contents->profile(); - if (profile) - return profile->GetDownloadManager(); - } - - return NULL; + if (downloads_.empty()) + StopUpdateTimer(); } -// Called by DownloadManagers in their destructor, and only on the UI thread. -void DownloadFileManager::RemoveDownloadManager(DownloadManager* manager) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); +void DownloadFileManager::OnDownloadManagerShutdown(DownloadManager* manager) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); DCHECK(manager); - RequestMap::iterator it = requests_.find(manager); - if (it == requests_.end()) - return; - const DownloadRequests& requests = it->second; - DownloadRequests::const_iterator i = requests.begin(); - for (; i != requests.end(); ++i) { - DownloadManagerMap::iterator dit = managers_.find(*i); - if (dit != managers_.end()) { - DCHECK(dit->second == manager); - managers_.erase(dit); - } + for (DownloadFileMap::iterator i = downloads_.begin(); + i != downloads_.end(); ++i) { + DownloadFile* download_file = i->second; + if (download_file->GetDownloadManager() == manager) + download_file->OnDownloadManagerShutdown(); } - - requests_.erase(it); } // Actions from the UI thread and run on the download thread @@ -472,11 +346,8 @@ void DownloadFileManager::OnFinalDownloadName(int id, delete download; } - if (downloads_.empty()) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &DownloadFileManager::StopUpdateTimer)); - } + if (downloads_.empty()) + StopUpdateTimer(); } // Called only from OnFinalDownloadName or OnIntermediateDownloadName @@ -488,13 +359,14 @@ void DownloadFileManager::CancelDownloadOnRename(int id) { if (!download) return; - DownloadManagerMap::iterator dmit = managers_.find(download->id()); - if (dmit != managers_.end()) { - DownloadManager* dlm = dmit->second; - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(dlm, &DownloadManager::DownloadCancelled, id)); - } else { + DownloadManager* download_manager = download->GetDownloadManager(); + if (!download_manager) { download->CancelDownloadRequest(resource_dispatcher_host_); + return; } + + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(download_manager, + &DownloadManager::DownloadCancelled, id)); } |