summaryrefslogtreecommitdiffstats
path: root/chrome/browser/download/download_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/download/download_manager.cc')
-rw-r--r--chrome/browser/download/download_manager.cc324
1 files changed, 205 insertions, 119 deletions
diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc
index 54d937b..899fe65 100644
--- a/chrome/browser/download/download_manager.cc
+++ b/chrome/browser/download/download_manager.cc
@@ -17,6 +17,7 @@
#include "base/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_create_info.h"
#include "chrome/browser/download/download_extensions.h"
#include "chrome/browser/download/download_file_manager.h"
#include "chrome/browser/download/download_history.h"
@@ -27,7 +28,7 @@
#include "chrome/browser/download/download_status_updater.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/history/download_create_info.h"
+#include "chrome/browser/history/download_history_info.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab_contents/tab_util.h"
@@ -253,36 +254,51 @@ bool DownloadManager::Init(Profile* profile) {
// observers at this point. OnCreateDownloadEntryComplete() handles that
// finalization of the the download creation as a callback from the
// history thread.
-void DownloadManager::StartDownload(DownloadCreateInfo* info) {
+void DownloadManager::StartDownload(int32 download_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
// Create a client to verify download URL with safebrowsing.
// It deletes itself after the callback.
scoped_refptr<DownloadSBClient> sb_client = new DownloadSBClient(
- info->download_id, info->url_chain, info->referrer_url);
+ download_id, download->url_chain(), download->referrer_url());
sb_client->CheckDownloadUrl(
- info, NewCallback(this, &DownloadManager::CheckDownloadUrlDone));
+ NewCallback(this, &DownloadManager::CheckDownloadUrlDone));
}
-void DownloadManager::CheckDownloadUrlDone(DownloadCreateInfo* info,
+void DownloadManager::CheckDownloadUrlDone(int32 download_id,
bool is_dangerous_url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(info);
- info->is_dangerous_url = is_dangerous_url;
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
+ if (is_dangerous_url)
+ download->MarkUrlDangerous();
+
+ DownloadStateInfo state = download->state_info();
// Check whether this download is for an extension install or not.
// Allow extensions to be explicitly saved.
- if (!info->prompt_user_for_save_location) {
- if (UserScript::IsURLUserScript(info->url(), info->mime_type) ||
- info->mime_type == Extension::kMimeType) {
- info->is_extension_install = true;
+ if (!state.prompt_user_for_save_location) {
+ if (UserScript::IsURLUserScript(download->GetURL(),
+ download->mime_type()) ||
+ (download->mime_type() == Extension::kMimeType)) {
+ state.is_extension_install = true;
}
}
- if (info->save_info.file_path.empty()) {
+ if (state.force_file_name.empty()) {
FilePath generated_name;
- download_util::GenerateFileNameFromInfo(info, &generated_name);
+ download_util::GenerateFileNameFromRequest(download->GetURL(),
+ download->content_disposition(),
+ download->referrer_charset(),
+ download->mime_type(),
+ &generated_name);
// Freeze the user's preference for showing a Save As dialog. We're going
// to bounce around a bunch of threads and we don't want to worry about race
@@ -294,32 +310,29 @@ void DownloadManager::CheckDownloadUrlDone(DownloadCreateInfo* info,
// "save as...".
// 2) Filetypes marked "always open." If the user just wants this file
// opened, don't bother asking where to keep it.
- if (!info->is_extension_install &&
+ if (!state.is_extension_install &&
!ShouldOpenFileBasedOnExtension(generated_name))
- info->prompt_user_for_save_location = true;
+ state.prompt_user_for_save_location = true;
}
if (download_prefs_->IsDownloadPathManaged()) {
- info->prompt_user_for_save_location = false;
+ state.prompt_user_for_save_location = false;
}
// Determine the proper path for a download, by either one of the following:
// 1) using the default download directory.
// 2) prompting the user.
- if (info->prompt_user_for_save_location && !last_download_path_.empty()) {
- info->suggested_path = last_download_path_;
+ if (state.prompt_user_for_save_location && !last_download_path_.empty()) {
+ state.suggested_path = last_download_path_;
} else {
- info->suggested_path = download_prefs_->download_path();
+ state.suggested_path = download_prefs_->download_path();
}
- info->suggested_path = info->suggested_path.Append(generated_name);
+ state.suggested_path = state.suggested_path.Append(generated_name);
} else {
- info->suggested_path = info->save_info.file_path;
+ state.suggested_path = state.force_file_name;
}
- if (!info->prompt_user_for_save_location &&
- info->save_info.file_path.empty()) {
- info->is_dangerous_file = download_util::IsDangerous(
- info, profile(), ShouldOpenFileBasedOnExtension(info->suggested_path));
- }
+ if (!state.prompt_user_for_save_location && state.force_file_name.empty())
+ state.is_dangerous_file = IsDangerous(*download, state);
// We need to move over to the download thread because we don't want to stat
// the suggested path on the UI thread.
@@ -330,14 +343,15 @@ void DownloadManager::CheckDownloadUrlDone(DownloadCreateInfo* info,
NewRunnableMethod(
this,
&DownloadManager::CheckIfSuggestedPathExists,
- info,
+ download_id,
+ state,
download_prefs()->download_path()));
}
-void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info,
+void DownloadManager::CheckIfSuggestedPathExists(int32 download_id,
+ DownloadStateInfo state,
const FilePath& default_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- DCHECK(info);
// Make sure the default download directory exists.
// TODO(phajdan.jr): only create the directory when we're sure the user
@@ -346,18 +360,18 @@ void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info,
// Check writability of the suggested path. If we can't write to it, default
// to the user's "My Documents" directory. We'll prompt them in this case.
- FilePath dir = info->suggested_path.DirName();
- FilePath filename = info->suggested_path.BaseName();
+ FilePath dir = state.suggested_path.DirName();
+ FilePath filename = state.suggested_path.BaseName();
if (!file_util::PathIsWritable(dir)) {
VLOG(1) << "Unable to write to directory \"" << dir.value() << "\"";
- info->prompt_user_for_save_location = true;
- PathService::Get(chrome::DIR_USER_DOCUMENTS, &info->suggested_path);
- info->suggested_path = info->suggested_path.Append(filename);
+ state.prompt_user_for_save_location = true;
+ PathService::Get(chrome::DIR_USER_DOCUMENTS, &state.suggested_path);
+ state.suggested_path = state.suggested_path.Append(filename);
}
// If the download is deemed dangerous, we'll use a temporary name for it.
- if (info->IsDangerous()) {
- info->original_name = FilePath(info->suggested_path).BaseName();
+ if (state.IsDangerous()) {
+ state.target_name = FilePath(state.suggested_path).BaseName();
// Create a temporary file to hold the file until the user approves its
// download.
FilePath::StringType file_name;
@@ -380,61 +394,73 @@ void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info,
if (file_util::PathExists(path))
path = FilePath();
}
- info->suggested_path = path;
+ state.suggested_path = path;
} else {
// Do not add the path uniquifier if we are saving to a specific path as in
// the drag-out case.
- if (info->save_info.file_path.empty()) {
- info->path_uniquifier = download_util::GetUniquePathNumberWithCrDownload(
- info->suggested_path);
+ if (state.force_file_name.empty()) {
+ state.path_uniquifier = download_util::GetUniquePathNumberWithCrDownload(
+ state.suggested_path);
}
// We know the final path, build it if necessary.
- if (info->path_uniquifier > 0) {
- download_util::AppendNumberToPath(&(info->suggested_path),
- info->path_uniquifier);
+ if (state.path_uniquifier > 0) {
+ download_util::AppendNumberToPath(&(state.suggested_path),
+ state.path_uniquifier);
// Setting path_uniquifier to 0 to make sure we don't try to unique it
// later on.
- info->path_uniquifier = 0;
- } else if (info->path_uniquifier == -1) {
+ state.path_uniquifier = 0;
+ } else if (state.path_uniquifier == -1) {
// We failed to find a unique path. We have to prompt the user.
VLOG(1) << "Unable to find a unique path for suggested path \""
- << info->suggested_path.value() << "\"";
- info->prompt_user_for_save_location = true;
+ << state.suggested_path.value() << "\"";
+ state.prompt_user_for_save_location = true;
}
}
// Create an empty file at the suggested path so that we don't allocate the
// same "non-existant" path to multiple downloads.
// See: http://code.google.com/p/chromium/issues/detail?id=3662
- if (!info->prompt_user_for_save_location &&
- info->save_info.file_path.empty()) {
- if (info->IsDangerous())
- file_util::WriteFile(info->suggested_path, "", 0);
+ if (!state.prompt_user_for_save_location &&
+ state.force_file_name.empty()) {
+ if (state.IsDangerous())
+ file_util::WriteFile(state.suggested_path, "", 0);
else
file_util::WriteFile(download_util::GetCrDownloadPath(
- info->suggested_path), "", 0);
+ state.suggested_path), "", 0);
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(this,
&DownloadManager::OnPathExistenceAvailable,
- info));
+ download_id,
+ state));
}
-void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) {
- VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString();
+void DownloadManager::OnPathExistenceAvailable(
+ int32 download_id, DownloadStateInfo new_state) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(info);
- if (info->prompt_user_for_save_location) {
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download = " << download->DebugString(true);
+
+ download->SetFileCheckResults(new_state);
+
+ FilePath suggested_path = download->suggested_path();
+
+ if (download->save_as()) {
// We must ask the user for the place to put the download.
if (!select_file_dialog_.get())
select_file_dialog_ = SelectFileDialog::Create(this);
- TabContents* contents = info->process_handle.GetTabContents();
+ DownloadProcessHandle process_handle = download->process_handle();
+ TabContents* contents = process_handle.GetTabContents();
SelectFileDialog::FileTypeInfo file_type_info;
- FilePath::StringType extension = info->suggested_path.Extension();
+ FilePath::StringType extension = suggested_path.Extension();
if (!extension.empty()) {
extension.erase(extension.begin()); // drop the .
file_type_info.extensions.resize(1);
@@ -443,17 +469,21 @@ void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) {
file_type_info.include_all_files = true;
gfx::NativeWindow owning_window =
contents ? platform_util::GetTopLevel(contents->GetNativeView()) : NULL;
+ // |id_ptr| will be deleted in either FileSelected() or
+ // FileSelectionCancelled().
+ int32* id_ptr = new int32;
+ *id_ptr = download_id;
select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE,
string16(),
- info->suggested_path,
+ suggested_path,
&file_type_info, 0, FILE_PATH_LITERAL(""),
- contents, owning_window, info);
+ contents, owning_window,
+ reinterpret_cast<void*>(id_ptr));
FOR_EACH_OBSERVER(Observer, observers_,
- SelectFileDialogDisplayed(info->download_id));
+ SelectFileDialogDisplayed(download_id));
} else {
// No prompting for download, just continue with the suggested name.
- info->path = info->suggested_path;
- AttachDownloadItem(info);
+ ContinueDownloadWithPath(download, suggested_path);
}
}
@@ -462,52 +492,52 @@ void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) {
DownloadItem* download = new DownloadItem(this, *info,
profile_->IsOffTheRecord());
- DCHECK(!ContainsKey(in_progress_, info->download_id));
- DCHECK(!ContainsKey(active_downloads_, info->download_id));
+ int32 download_id = info->download_id;
+ DCHECK(!ContainsKey(in_progress_, download_id));
+ DCHECK(!ContainsKey(active_downloads_, download_id));
downloads_.insert(download);
- active_downloads_[info->download_id] = download;
+ active_downloads_[download_id] = download;
}
-void DownloadManager::AttachDownloadItem(DownloadCreateInfo* info) {
- VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString();
-
+void DownloadManager::ContinueDownloadWithPath(DownloadItem* download,
+ const FilePath& chosen_file) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(download);
- // Life of |info| ends here. No more references to it after this method.
- scoped_ptr<DownloadCreateInfo> infop(info);
+ int32 download_id = download->id();
// NOTE(ahendrickson) Eventually |active_downloads_| will replace
// |in_progress_|, but we don't want to change the semantics yet.
- DCHECK(!ContainsKey(in_progress_, info->download_id));
- DCHECK(ContainsKey(active_downloads_, info->download_id));
- DownloadItem* download = active_downloads_[info->download_id];
- DCHECK(download != NULL);
+ DCHECK(!ContainsKey(in_progress_, download_id));
DCHECK(ContainsKey(downloads_, download));
+ DCHECK(ContainsKey(active_downloads_, download_id));
- download->SetFileCheckResults(info->path,
- info->is_dangerous_file,
- info->is_dangerous_url,
- info->path_uniquifier,
- info->prompt_user_for_save_location,
- info->is_extension_install,
- info->original_name);
- in_progress_[info->download_id] = download;
+ // Make sure the initial file name is set only once.
+ DCHECK(download->full_path().empty());
+ download->OnPathDetermined(chosen_file);
+ download->UpdateTarget();
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download = " << download->DebugString(true);
+
+ in_progress_[download_id] = download;
UpdateAppIcon(); // Reflect entry into in_progress_.
// Rename to intermediate name.
FilePath download_path;
- if (info->IsDangerous()) {
+ if (download->IsDangerous()) {
// The download is not safe. We can now rename the file to its
// tentative name using RenameInProgressDownloadFile.
// NOTE: The |Rename| below will be a no-op for dangerous files, as we're
// renaming it to the same name.
- download_path = info->path;
+ download_path = download->full_path();
} else {
// The download is a safe download. We need to
// rename it to its intermediate '.crdownload' path. The final
// name after user confirmation will be set from
// DownloadItem::OnDownloadCompleting.
- download_path = download_util::GetCrDownloadPath(info->path);
+ download_path =
+ download_util::GetCrDownloadPath(download->full_path());
}
BrowserThread::PostTask(
@@ -518,7 +548,7 @@ void DownloadManager::AttachDownloadItem(DownloadCreateInfo* info) {
download->Rename(download_path);
- download_history_->AddEntry(*info, download,
+ download_history_->AddEntry(download,
NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
}
@@ -591,7 +621,7 @@ void DownloadManager::CheckDownloadHashDone(int32 download_id,
return;
DVLOG(1) << "CheckDownloadHashDone, url: "
- << active_downloads_[download_id]->url().spec();
+ << active_downloads_[download_id]->GetURL().spec();
}
bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) {
@@ -732,9 +762,11 @@ void DownloadManager::OnDownloadError(int32 download_id,
DownloadItem* download = it->second;
- VLOG(20) << "Error " << os_error << " at offset "
- << download->received_bytes() << " for download = "
- << download->DebugString(true);
+ VLOG(20) << __FUNCTION__ << "()" << " Error " << os_error
+ << " at offset " << download->received_bytes()
+ << " for download = " << download->DebugString(true);
+
+ download->Interrupted(size, os_error);
// TODO(ahendrickson) - Remove this when we add resuming of interrupted
// downloads, as we will keep the download item around in that case.
@@ -748,8 +780,6 @@ void DownloadManager::OnDownloadError(int32 download_id,
download_history_->UpdateEntry(download);
}
- download->Interrupted(size, os_error);
-
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(
@@ -966,19 +996,69 @@ int64 DownloadManager::GetTotalDownloadBytes() {
void DownloadManager::FileSelected(const FilePath& path,
int index, void* params) {
- DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
- if (info->prompt_user_for_save_location)
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ int32* id_ptr = reinterpret_cast<int32*>(params);
+ DCHECK(id_ptr != NULL);
+ int32 download_id = *id_ptr;
+ delete id_ptr;
+
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+ VLOG(20) << __FUNCTION__ << "()" << " path = \"" << path.value() << "\""
+ << " download = " << download->DebugString(true);
+
+ if (download->save_as())
last_download_path_ = path.DirName();
- info->path = path;
- AttachDownloadItem(info);
+ // Make sure the initial file name is set only once.
+ ContinueDownloadWithPath(download, path);
}
void DownloadManager::FileSelectionCanceled(void* params) {
// The user didn't pick a place to save the file, so need to cancel the
// download that's already in progress to the temporary location.
- DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
- DownloadCancelledInternal(info->download_id, info->process_handle);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int32* id_ptr = reinterpret_cast<int32*>(params);
+ DCHECK(id_ptr != NULL);
+ int32 download_id = *id_ptr;
+ delete id_ptr;
+
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download = " << download->DebugString(true);
+
+ DownloadCancelledInternal(download_id, download->process_handle());
+}
+
+// TODO(phajdan.jr): This is apparently not being exercised in tests.
+bool DownloadManager::IsDangerous(const DownloadItem& download,
+ const DownloadStateInfo& state) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ bool auto_open = ShouldOpenFileBasedOnExtension(state.suggested_path);
+ download_util::DownloadDangerLevel danger_level =
+ download_util::GetFileDangerLevel(state.suggested_path.BaseName());
+
+ if (danger_level == download_util::Dangerous)
+ return !(auto_open && state.has_user_gesture);
+
+ if (danger_level == download_util::AllowOnUserGesture &&
+ !state.has_user_gesture)
+ return true;
+
+ if (state.is_extension_install) {
+ // Extensions that are not from the gallery are considered dangerous.
+ ExtensionService* service = profile()->GetExtensionService();
+ if (!service || !service->IsDownloadFromGallery(download.GetURL(),
+ download.referrer_url()))
+ return true;
+ }
+ return false;
}
void DownloadManager::DangerousDownloadValidated(DownloadItem* download) {
@@ -993,9 +1073,9 @@ void DownloadManager::DangerousDownloadValidated(DownloadItem* download) {
// Operations posted to us from the history service ----------------------------
// The history service has retrieved all download entries. 'entries' contains
-// 'DownloadCreateInfo's in sorted order (by ascending start_time).
+// 'DownloadHistoryInfo's in sorted order (by ascending start_time).
void DownloadManager::OnQueryDownloadEntriesComplete(
- std::vector<DownloadCreateInfo>* entries) {
+ std::vector<DownloadHistoryInfo>* entries) {
for (size_t i = 0; i < entries->size(); ++i) {
DownloadItem* download = new DownloadItem(this, entries->at(i));
DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
@@ -1010,16 +1090,15 @@ void DownloadManager::OnQueryDownloadEntriesComplete(
// Once the new DownloadItem's creation info has been committed to the history
// service, we associate the DownloadItem with the db handle, update our
// 'history_downloads_' map and inform observers.
-void DownloadManager::OnCreateDownloadEntryComplete(
- DownloadCreateInfo info,
- int64 db_handle) {
+void DownloadManager::OnCreateDownloadEntryComplete(int32 download_id,
+ int64 db_handle) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DownloadMap::iterator it = in_progress_.find(info.download_id);
- DCHECK(it != in_progress_.end());
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
- DownloadItem* download = it->second;
VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle
- << " download_id = " << info.download_id
+ << " download_id = " << download_id
<< " download = " << download->DebugString(true);
// It's not immediately obvious, but HistoryBackend::CreateDownload() can
@@ -1038,7 +1117,7 @@ void DownloadManager::OnCreateDownloadEntryComplete(
// Show in the appropriate browser UI.
// This includes buttons to save or cancel, for a dangerous download.
- ShowDownloadInBrowser(&info.process_handle, download);
+ ShowDownloadInBrowser(download);
// Inform interested objects about the new download.
NotifyModelChanged();
@@ -1055,22 +1134,20 @@ void DownloadManager::OnCreateDownloadEntryComplete(
} else {
DCHECK(download->IsCancelled())
<< " download = " << download->DebugString(true);
- in_progress_.erase(it);
- active_downloads_.erase(info.download_id);
+ in_progress_.erase(download_id);
+ active_downloads_.erase(download_id);
download_history_->UpdateEntry(download);
download->UpdateObservers();
}
}
-void DownloadManager::ShowDownloadInBrowser(
- DownloadProcessHandle* process_handle, DownloadItem* download) {
- if (!process_handle)
- return;
+void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) {
// The 'contents' may no longer exist if the user closed the tab before we
// get this start completion event. If it does, tell the origin TabContents
// to display its download shelf.
- TabContents* contents = process_handle->GetTabContents();
+ DownloadProcessHandle process_handle = download->process_handle();
+ TabContents* contents = process_handle.GetTabContents();
TabContentsWrapper* wrapper = NULL;
if (contents)
wrapper = TabContentsWrapper::GetCurrentWrapperForContents(contents);
@@ -1099,16 +1176,25 @@ void DownloadManager::NotifyModelChanged() {
FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
}
-DownloadItem* DownloadManager::GetDownloadItem(int id) {
+DownloadItem* DownloadManager::GetDownloadItem(int download_id) {
+ // The |history_downloads_| map is indexed by the download's db_handle,
+ // not its id, so we have to iterate.
for (DownloadMap::iterator it = history_downloads_.begin();
it != history_downloads_.end(); ++it) {
DownloadItem* item = it->second;
- if (item->id() == id)
+ if (item->id() == download_id)
return item;
}
return NULL;
}
+DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) {
+ DCHECK(ContainsKey(active_downloads_, download_id));
+ DownloadItem* download = active_downloads_[download_id];
+ DCHECK(download != NULL);
+ return download;
+}
+
// Confirm that everything in all maps is also in |downloads_|, and that
// everything in |downloads_| is also in some other map.
void DownloadManager::AssertContainersConsistent() const {