Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1080)

Unified Diff: chrome/browser/download/chrome_download_manager_delegate.cc

Issue 7640021: Move some Chrome-specific code paths out of DownloadManager and into the delegate. Getting rid of... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/download/chrome_download_manager_delegate.cc
===================================================================
--- chrome/browser/download/chrome_download_manager_delegate.cc (revision 96577)
+++ chrome/browser/download/chrome_download_manager_delegate.cc (working copy)
@@ -4,20 +4,100 @@
#include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/rand_util.h"
+#include "base/stringprintf.h"
+#include "chrome/browser/download/download_extensions.h"
#include "chrome/browser/download/download_file_picker.h"
+#include "chrome/browser/download/download_history.h"
+#include "chrome/browser/download/download_item.h"
+#include "chrome/browser/download/download_manager.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/download/download_safe_browsing_client.h"
+#include "chrome/browser/download/download_util.h"
#include "chrome/browser/download/save_package_file_picker.h"
+#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/prefs/pref_member.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/user_script.h"
#include "chrome/common/pref_names.h"
#include "content/browser/tab_contents/tab_contents.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
-ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate()
- : download_manager_(NULL) {
+ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate() {
}
+ChromeDownloadManagerDelegate::~ChromeDownloadManagerDelegate() {
+}
+
+bool ChromeDownloadManagerDelegate::ShouldStartDownload(int32 download_id) {
+ // We create a download item and store it in our download map, and inform the
+ // history system of a new download. Since this method can be called while the
+ // history service thread is still reading the persistent state, we do not
+ // insert the new DownloadItem into 'history_downloads_' or inform our
+ // observers at this point. OnCreateDownloadEntryComplete() handles that
+ // finalization of the the download creation as a callback from the history
+ // thread.
+ DownloadItem* download =
+ download_manager_->GetActiveDownloadItem(download_id);
+ if (!download)
+ return false;
+
+#if defined(ENABLE_SAFE_BROWSING)
+ // Create a client to verify download URL with safebrowsing.
+ // It deletes itself after the callback.
+ scoped_refptr<DownloadSBClient> sb_client = new DownloadSBClient(
+ download_id, download->url_chain(), download->referrer_url(),
+ download_manager_->profile()->GetPrefs()->GetBoolean(
+ prefs::kSafeBrowsingEnabled));
+ sb_client->CheckDownloadUrl(
+ NewCallback(this, &ChromeDownloadManagerDelegate::CheckDownloadUrlDone));
+#else
+ CheckDownloadUrlDone(download_id, false);
+#endif
+ return false;
+}
+
+void ChromeDownloadManagerDelegate::ChooseDownloadPath(
+ TabContents* tab_contents,
+ const FilePath& suggested_path,
+ void* data) {
+ // Deletes itself.
+ new DownloadFilePicker(
+ download_manager_, tab_contents, suggested_path, data);
+}
+
+TabContents* ChromeDownloadManagerDelegate::
+ GetAlternativeTabContentsToNotifyForDownload() {
+ // Start the download in the last active browser. This is not ideal but better
+ // than fully hiding the download from the user.
+ Browser* last_active = BrowserList::GetLastActiveWithProfile(
+ download_manager_->profile());
+ return last_active ? last_active->GetSelectedTabContents() : NULL;
+}
+
+
+bool ChromeDownloadManagerDelegate::ShouldOpenFileBasedOnExtension(
+ const FilePath& path) {
+ FilePath::StringType extension = path.Extension();
+ if (extension.empty())
+ return false;
+ if (Extension::IsExtension(path))
+ return false;
+ DCHECK(extension[0] == FilePath::kExtensionSeparator);
+ extension.erase(0, 1);
+ return download_manager_->download_prefs()->
+ IsAutoOpenEnabledForExtension(extension);
+}
+
+
void ChromeDownloadManagerDelegate::GetSaveDir(TabContents* tab_contents,
FilePath* website_save_dir,
FilePath* download_save_dir) {
@@ -52,22 +132,230 @@
save_package, suggested_path, can_save_as_complete);
}
-void ChromeDownloadManagerDelegate::ChooseDownloadPath(
- DownloadManager* download_manager,
- TabContents* tab_contents,
- const FilePath& suggested_path,
- void* data) {
- // Deletes itself.
- new DownloadFilePicker(
- download_manager, tab_contents, suggested_path, data);
+void ChromeDownloadManagerDelegate::CheckDownloadUrlDone(
+ int32 download_id, bool is_dangerous_url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DownloadItem* download =
+ download_manager_->GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
+ if (is_dangerous_url)
+ download->MarkUrlDangerous();
+
+ download_manager_->download_history()->CheckVisitedReferrerBefore(
+ download_id,
+ download->referrer_url(),
+ NewCallback(this,
+ &ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone));
}
-TabContents*
- ChromeDownloadManagerDelegate::GetAlternativeTabContentsToNotifyForDownload(
- DownloadManager* download_manager) {
- // Start the download in the last active browser. This is not ideal but better
- // than fully hiding the download from the user.
- Browser* last_active = BrowserList::GetLastActiveWithProfile(
- download_manager->profile());
- return last_active ? last_active->GetSelectedTabContents() : NULL;
+void ChromeDownloadManagerDelegate::CheckVisitedReferrerBeforeDone(
+ int32 download_id,
+ bool visited_referrer_before) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DownloadItem* download =
+ download_manager_->GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
+ // Check whether this download is for an extension install or not.
+ // Allow extensions to be explicitly saved.
+ DownloadStateInfo state = download->state_info();
+ 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 (state.force_file_name.empty()) {
+ FilePath generated_name;
+ download_util::GenerateFileNameFromRequest(*download,
+ &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
+ // conditions where the user changes this pref out from under us.
+ if (download_manager_->download_prefs()->PromptForDownload()) {
+ // But ignore the user's preference for the following scenarios:
+ // 1) Extension installation. Note that we only care here about the case
+ // where an extension is installed, not when one is downloaded with
+ // "save as...".
+ // 2) Filetypes marked "always open." If the user just wants this file
+ // opened, don't bother asking where to keep it.
+ if (!state.is_extension_install &&
+ !ShouldOpenFileBasedOnExtension(generated_name))
+ state.prompt_user_for_save_location = true;
+ }
+ if (download_manager_->download_prefs()->IsDownloadPathManaged()) {
+ 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 (state.prompt_user_for_save_location &&
+ !download_manager_->last_download_path().empty()) {
+ state.suggested_path = download_manager_->last_download_path();
+ } else {
+ state.suggested_path =
+ download_manager_->download_prefs()->download_path();
+ }
+ state.suggested_path = state.suggested_path.Append(generated_name);
+ } else {
+ state.suggested_path = state.force_file_name;
+ }
+
+ if (!state.prompt_user_for_save_location && state.force_file_name.empty()) {
+ state.is_dangerous_file =
+ IsDangerousFile(*download, state, visited_referrer_before);
+ }
+
+ // We need to move over to the download thread because we don't want to stat
+ // the suggested path on the UI thread.
+ // We can only access preferences on the UI thread, so check the download path
+ // now and pass the value to the FILE thread.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &ChromeDownloadManagerDelegate::CheckIfSuggestedPathExists,
+ download->id(),
+ state,
+ download_manager_->download_prefs()->download_path()));
}
+
+void ChromeDownloadManagerDelegate::CheckIfSuggestedPathExists(
+ int32 download_id,
+ DownloadStateInfo state,
+ const FilePath& default_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // Make sure the default download directory exists.
+ // TODO(phajdan.jr): only create the directory when we're sure the user
+ // is going to save there and not to another directory of his choice.
+ file_util::CreateDirectory(default_path);
+
+ // 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 = state.suggested_path.DirName();
+ FilePath filename = state.suggested_path.BaseName();
+ if (!file_util::PathIsWritable(dir)) {
+ VLOG(1) << "Unable to write to directory \"" << dir.value() << "\"";
+ 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 (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;
+ FilePath path;
+#if defined(OS_WIN)
+ string16 unconfirmed_prefix =
+ l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
+#else
+ std::string unconfirmed_prefix =
+ l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
+#endif
+
+ while (path.empty()) {
+ base::SStringPrintf(
+ &file_name,
+ unconfirmed_prefix.append(
+ FILE_PATH_LITERAL(" %d.crdownload")).c_str(),
+ base::RandInt(0, 100000));
+ path = dir.Append(file_name);
+ if (file_util::PathExists(path))
+ path = FilePath();
+ }
+ 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 (state.force_file_name.empty()) {
+ state.path_uniquifier = download_util::GetUniquePathNumberWithCrDownload(
+ state.suggested_path);
+ }
+ // We know the final path, build it if necessary.
+ 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.
+ 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 \""
+ << 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 (!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(
+ state.suggested_path), "", 0);
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &ChromeDownloadManagerDelegate::OnPathExistenceAvailable,
+ download_id,
+ state));
+}
+
+void ChromeDownloadManagerDelegate::OnPathExistenceAvailable(
+ int32 download_id,
+ const DownloadStateInfo& new_state) {
+ DownloadItem* download =
+ download_manager_->GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+ download->SetFileCheckResults(new_state);
+ download_manager_->RestartDownload(download_id);
+}
+
+// TODO(phajdan.jr): This is apparently not being exercised in tests.
+bool ChromeDownloadManagerDelegate::IsDangerousFile(
+ const DownloadItem& download,
+ const DownloadStateInfo& state,
+ bool visited_referrer_before) {
+ 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 || !visited_referrer_before))
+ return true;
+
+ if (state.is_extension_install) {
+ // Extensions that are not from the gallery are considered dangerous.
+ ExtensionService* service =
+ download_manager_->profile()->GetExtensionService();
+ if (!service || !service->IsDownloadFromGallery(download.GetURL(),
+ download.referrer_url()))
+ return true;
+ }
+ return false;
+}
« no previous file with comments | « chrome/browser/download/chrome_download_manager_delegate.h ('k') | chrome/browser/download/download_item.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698