summaryrefslogtreecommitdiffstats
path: root/chrome/browser/download/download_file_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/download/download_file_manager.cc')
-rw-r--r--chrome/browser/download/download_file_manager.cc362
1 files changed, 111 insertions, 251 deletions
diff --git a/chrome/browser/download/download_file_manager.cc b/chrome/browser/download/download_file_manager.cc
index d9ba1f2..fc94314 100644
--- a/chrome/browser/download/download_file_manager.cc
+++ b/chrome/browser/download/download_file_manager.cc
@@ -5,13 +5,14 @@
#include "chrome/browser/download/download_file_manager.h"
#include "base/file_util.h"
+#include "base/stl_util-inl.h"
#include "base/task.h"
#include "base/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/download/download_util.h"
-#include "chrome/browser/history/download_types.h"
+#include "chrome/browser/history/download_create_info.h"
#include "chrome/browser/net/chrome_url_request_context.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profile.h"
@@ -19,6 +20,7 @@
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "googleurl/src/gurl.h"
+#include "net/base/io_buffer.h"
#if defined(OS_WIN)
#include "app/win_util.h"
@@ -33,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)
@@ -41,45 +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));
- // Delete any partial downloads during shutdown.
- for (DownloadFileMap::iterator it = downloads_.begin();
- it != downloads_.end(); ++it) {
- DownloadFile* download = it->second;
- if (download->in_progress())
- download->Cancel();
- delete download;
- }
- downloads_.clear();
+ 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,
@@ -90,58 +91,39 @@ void DownloadFileManager::OnStartDownload(DownloadCreateInfo* info) {
return;
}
+ 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;
+
+ // The file is now ready, we can un-pause the request and start saving data.
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &DownloadFileManager::ResumeDownloadRequest,
+ info->child_id, info->request_id));
+
StartUpdateTimer();
- // 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);
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(download_manager,
+ &DownloadManager::StartDownload, info));
}
-// 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);
+void DownloadFileManager::ResumeDownloadRequest(int child_id, int request_id) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ // This balances the pause in DownloadResourceHandler::OnResponseStarted.
+ resource_dispatcher_host_->PauseRequest(child_id, request_id, false);
}
-// 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);
@@ -149,21 +131,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()));
+ }
}
}
@@ -174,21 +157,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,
@@ -196,22 +171,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
@@ -227,28 +192,17 @@ 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) {
+void DownloadFileManager::OnResponseCompleted(int id, DownloadBuffer* buffer) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
delete buffer;
DownloadFileMap::iterator it = downloads_.find(id);
@@ -256,18 +210,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::OnAllDataSaved,
+ 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()) {
@@ -277,9 +228,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
@@ -292,127 +241,40 @@ 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);
+ std::set<DownloadFile*> to_remove;
+
+ for (DownloadFileMap::iterator i = downloads_.begin();
+ i != downloads_.end(); ++i) {
+ DownloadFile* download_file = i->second;
+ if (download_file->GetDownloadManager() == manager) {
+ download_file->CancelDownloadRequest(resource_dispatcher_host_);
+ to_remove.insert(download_file);
}
}
- requests_.erase(it);
+ for (std::set<DownloadFile*>::iterator i = to_remove.begin();
+ i != to_remove.end(); ++i) {
+ downloads_.erase((*i)->id());
+ delete *i;
+ }
}
// Actions from the UI thread and run on the download thread
-// Open a download, or show it in a file explorer window. We run on this
-// thread to avoid blocking the UI with (potentially) slow Shell operations.
-// TODO(paulg): File 'stat' operations.
-#if !defined(OS_MACOSX)
-void DownloadFileManager::OnShowDownloadInShell(const FilePath& full_path) {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
- platform_util::ShowItemInFolder(full_path);
-}
-#endif
-
-// Launches the selected download using ShellExecute 'open' verb. For windows,
-// if there is a valid parent window, the 'safer' version will be used which can
-// display a modal dialog asking for user consent on dangerous files.
-#if !defined(OS_MACOSX)
-void DownloadFileManager::OnOpenDownloadInShell(const FilePath& full_path,
- const GURL& url,
- gfx::NativeView parent_window) {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
-#if defined(OS_WIN)
- if (NULL != parent_window) {
- win_util::SaferOpenItemViaShell(parent_window, L"", full_path,
- UTF8ToWide(url.spec()));
- return;
- }
-#endif
- platform_util::OpenItem(full_path);
-}
-#endif // OS_MACOSX
-
// The DownloadManager in the UI thread has provided an intermediate .crdownload
// name for the download specified by 'id'. Rename the in progress download.
void DownloadFileManager::OnIntermediateDownloadName(
@@ -446,11 +308,11 @@ void DownloadFileManager::OnFinalDownloadName(int id,
bool need_delete_crdownload,
DownloadManager* manager) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
- DownloadFileMap::iterator it = downloads_.find(id);
- if (it == downloads_.end())
+
+ DownloadFile* download = GetDownloadFile(id);
+ if (!download)
return;
- DownloadFile* download = it->second;
if (download->Rename(full_path, true)) {
#if defined(OS_MACOSX)
// Done here because we only want to do this once; see
@@ -474,33 +336,31 @@ void DownloadFileManager::OnFinalDownloadName(int id,
// If the download has completed before we got this final name, we remove it
// from our in progress map.
if (!download->in_progress()) {
- downloads_.erase(it);
+ downloads_.erase(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
// on the FILE thread.
void DownloadFileManager::CancelDownloadOnRename(int id) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
- DownloadFileMap::iterator it = downloads_.find(id);
- if (it == downloads_.end())
+
+ DownloadFile* download = GetDownloadFile(id);
+ if (!download)
return;
- DownloadFile* download = it->second;
- 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));
}