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

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

Issue 2453633006: [downloads] Move platform specific code out of DownloadTargetDeterminer. (Closed)
Patch Set: . Created 4 years, 2 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/download_target_determiner.cc
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
index b3841d70bb7eaae3d542a3a9c7f8a35be23c1f14..fd4bb399b8233c01790f035356920d7eafcfa7be 100644
--- a/chrome/browser/download/download_target_determiner.cc
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -4,13 +4,15 @@
#include "chrome/browser/download/download_target_determiner.h"
+#include <string>
+#include <vector>
+
#include "base/location.h"
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
-#include "build/build_config.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_crx_util.h"
#include "chrome/browser/download/download_prefs.h"
@@ -41,11 +43,6 @@
#include "content/public/common/webplugininfo.h"
#endif
-#if defined(OS_ANDROID)
-#include "chrome/browser/android/download/download_controller.h"
-#include "chrome/browser/android/download/download_manager_service.h"
-#endif
-
#if defined(OS_WIN)
#include "chrome/browser/ui/pdf/adobe_reader_info_win.h"
#endif
@@ -80,32 +77,26 @@ bool g_is_adobe_reader_up_to_date_ = false;
} // namespace
-DownloadTargetInfo::DownloadTargetInfo()
- : target_disposition(DownloadItem::TARGET_DISPOSITION_OVERWRITE),
- danger_type(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
- danger_level(DownloadFileType::NOT_DANGEROUS),
- is_filetype_handled_safely(false) {}
-
-DownloadTargetInfo::~DownloadTargetInfo() {}
-
DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() {
}
DownloadTargetDeterminer::DownloadTargetDeterminer(
DownloadItem* download,
const base::FilePath& initial_virtual_path,
+ DownloadPathReservationTracker::FilenameConflictAction conflict_action,
DownloadPrefs* download_prefs,
DownloadTargetDeterminerDelegate* delegate,
const CompletionCallback& callback)
: next_state_(STATE_GENERATE_TARGET_PATH),
- should_prompt_(false),
+ confirmation_reason_(DownloadConfirmationReason::NONE),
should_notify_extensions_(false),
create_target_directory_(false),
- conflict_action_(DownloadPathReservationTracker::OVERWRITE),
+ conflict_action_(conflict_action),
danger_type_(download->GetDangerType()),
danger_level_(DownloadFileType::NOT_DANGEROUS),
virtual_path_(initial_virtual_path),
is_filetype_handled_safely_(false),
+ result_(DownloadTargetResult::SUCCESS),
download_(download),
is_resumption_(download_->GetLastReason() !=
content::DOWNLOAD_INTERRUPT_REASON_NONE &&
@@ -146,7 +137,7 @@ void DownloadTargetDeterminer::DoLoop() {
result = DoReserveVirtualPath();
break;
case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH:
- result = DoPromptUserForDownloadPath();
+ result = DoRequestConfirmation();
break;
case STATE_DETERMINE_LOCAL_PATH:
result = DoDetermineLocalPath();
@@ -179,16 +170,15 @@ void DownloadTargetDeterminer::DoLoop() {
// determination and delete |this|.
if (result == COMPLETE)
- ScheduleCallbackAndDeleteSelf();
+ ScheduleCallbackAndDeleteSelf(result_);
}
DownloadTargetDeterminer::Result
DownloadTargetDeterminer::DoGenerateTargetPath() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(local_path_.empty());
- DCHECK(!should_prompt_);
+ DCHECK_EQ(confirmation_reason_, DownloadConfirmationReason::NONE);
DCHECK(!should_notify_extensions_);
- DCHECK_EQ(DownloadPathReservationTracker::OVERWRITE, conflict_action_);
bool is_forced_path = !download_->GetForcedFilePath().empty();
next_state_ = STATE_NOTIFY_EXTENSIONS;
@@ -197,7 +187,8 @@ DownloadTargetDeterminer::Result
// The download is being resumed and the user has already been prompted for
// a path. Assume that it's okay to overwrite the file if there's a conflict
// and reuse the selection.
- should_prompt_ = ShouldPromptForDownload(virtual_path_);
+ confirmation_reason_ = ShouldPromptForDownload(virtual_path_);
+ conflict_action_ = DownloadPathReservationTracker::OVERWRITE;
} else if (!is_forced_path) {
// If we don't have a forced path, we should construct a path for the
// download. Forced paths are only specified for programmatic downloads
@@ -220,9 +211,9 @@ DownloadTargetDeterminer::Result
suggested_filename,
download_->GetMimeType(),
default_filename);
- should_prompt_ = ShouldPromptForDownload(generated_filename);
+ confirmation_reason_ = ShouldPromptForDownload(generated_filename);
base::FilePath target_directory;
- if (should_prompt_) {
+ if (confirmation_reason_ != DownloadConfirmationReason::NONE) {
DCHECK(!download_prefs_->IsDownloadPathManaged());
// If the user is going to be prompted and the user has been prompted
// before, then always prefer the last directory that the user selected.
@@ -231,13 +222,9 @@ DownloadTargetDeterminer::Result
target_directory = download_prefs_->DownloadPath();
}
virtual_path_ = target_directory.Append(generated_filename);
-#if defined(OS_ANDROID)
- conflict_action_ = DownloadPathReservationTracker::PROMPT;
-#else
- conflict_action_ = DownloadPathReservationTracker::UNIQUIFY;
-#endif
should_notify_extensions_ = true;
} else {
+ conflict_action_ = DownloadPathReservationTracker::OVERWRITE;
virtual_path_ = download_->GetForcedFilePath();
// If this is a resumed download which was previously interrupted due to an
// issue with the forced path, the user is still not prompted. If the path
@@ -317,39 +304,44 @@ DownloadTargetDeterminer::Result
}
void DownloadTargetDeterminer::ReserveVirtualPathDone(
- const base::FilePath& path, bool verified) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ const base::FilePath& path,
+ DownloadTargetResult result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe()
- << " Verified:" << verified;
+ << " Result:" << static_cast<int>(result);
DCHECK_EQ(STATE_PROMPT_USER_FOR_DOWNLOAD_PATH, next_state_);
-#if BUILDFLAG(ANDROID_JAVA_UI)
- if (!verified) {
- if (path.empty()) {
- DownloadManagerService::OnDownloadCanceled(
- download_, DownloadController::CANCEL_REASON_NO_EXTERNAL_STORAGE);
- CancelOnFailureAndDeleteSelf();
- return;
- }
- if (!download_->GetWebContents()) {
- // If we cannot reserve the path and the WebContent is already gone, there
- // is no way to prompt user for an infobar. This could happen after chrome
- // gets killed, and user tries to resume a download while another app has
- // created the target file (not the temporary .crdownload file).
- DownloadManagerService::OnDownloadCanceled(
- download_,
- DownloadController::CANCEL_REASON_CANNOT_DETERMINE_DOWNLOAD_TARGET);
- CancelOnFailureAndDeleteSelf();
- return;
- }
- }
-#endif
- should_prompt_ = (should_prompt_ || !verified);
+
virtual_path_ = path;
+ result_ = result;
+
+ switch (result) {
+ case DownloadTargetResult::SUCCESS:
+ break;
+
+ case DownloadTargetResult::PATH_NOT_WRITEABLE:
+ confirmation_reason_ = DownloadConfirmationReason::TARGET_NOT_WRITEABLE;
+ break;
+
+ case DownloadTargetResult::NAME_TOO_LONG:
+ confirmation_reason_ = DownloadConfirmationReason::NAME_TOO_LONG;
+ break;
+
+ case DownloadTargetResult::CONFLICT:
+ confirmation_reason_ = DownloadConfirmationReason::TARGET_CONFLICT;
+ break;
+
+ case DownloadTargetResult::USER_CANCELED:
+ case DownloadTargetResult::UNEXPECTED:
+ // These are not considered recoverable errors. The download needs to be
+ // interrupted.
+ break;
+ }
+
DoLoop();
}
DownloadTargetDeterminer::Result
- DownloadTargetDeterminer::DoPromptUserForDownloadPath() {
+DownloadTargetDeterminer::DoRequestConfirmation() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!virtual_path_.empty());
@@ -357,27 +349,36 @@ DownloadTargetDeterminer::Result
// Avoid prompting for a download if it isn't in-progress. The user will be
// prompted once the download is resumed and headers are available.
- if (should_prompt_ && download_->GetState() == DownloadItem::IN_PROGRESS) {
- delegate_->PromptUserForDownloadPath(
- download_,
- virtual_path_,
- base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone,
+ if (confirmation_reason_ != DownloadConfirmationReason::NONE &&
+ download_->GetState() == DownloadItem::IN_PROGRESS) {
+ delegate_->RequestConfirmation(
+ download_, virtual_path_, confirmation_reason_,
+ base::Bind(&DownloadTargetDeterminer::RequestConfirmationDone,
weak_ptr_factory_.GetWeakPtr()));
return QUIT_DOLOOP;
}
return CONTINUE;
}
-void DownloadTargetDeterminer::PromptUserForDownloadPathDone(
+void DownloadTargetDeterminer::RequestConfirmationDone(
+ DownloadConfirmationResult result,
const base::FilePath& virtual_path) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DVLOG(20) << "User selected path:" << virtual_path.AsUTF8Unsafe();
- if (virtual_path.empty()) {
- CancelOnFailureAndDeleteSelf();
+ if (result == DownloadConfirmationResult::CANCELED) {
+ ScheduleCallbackAndDeleteSelf(DownloadTargetResult::USER_CANCELED);
return;
}
+ DCHECK(!virtual_path.empty());
DCHECK_EQ(STATE_DETERMINE_LOCAL_PATH, next_state_);
+ // If the user wasn't prompted, then we need to clear the
+ // confirmation_reason_. This way it's clear that user has not given consent
+ // to download this resource.
+ if (result == DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION)
+ confirmation_reason_ = DownloadConfirmationReason::NONE;
+
+ result_ = DownloadTargetResult::SUCCESS;
virtual_path_ = virtual_path;
download_prefs_->SetSaveFilePath(virtual_path_.DirName());
DoLoop();
@@ -404,8 +405,11 @@ void DownloadTargetDeterminer::DetermineLocalPathDone(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe();
if (local_path.empty()) {
- // Path subsitution failed.
- CancelOnFailureAndDeleteSelf();
+ // Path subsitution failed. Usually caused by something going wrong with the
+ // Google Drive logic (e.g. filesystem error while trying to create the
+ // cache file). We are going to return a generic error here since a more
+ // specific one is unlikely to be helpful to the user.
+ ScheduleCallbackAndDeleteSelf(DownloadTargetResult::UNEXPECTED);
return;
}
DCHECK_EQ(STATE_DETERMINE_MIME_TYPE, next_state_);
@@ -750,19 +754,23 @@ DownloadTargetDeterminer::Result
return COMPLETE;
}
-void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
+void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf(
+ DownloadTargetResult result) {
DCHECK(download_);
DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe()
<< " Local:" << local_path_.AsUTF8Unsafe()
<< " Intermediate:" << intermediate_path_.AsUTF8Unsafe()
- << " Should prompt:" << should_prompt_
+ << " Confirmation reason:" << static_cast<int>(confirmation_reason_)
<< " Danger type:" << danger_type_
- << " Danger level:" << danger_level_;
+ << " Danger level:" << danger_level_
+ << " Result:" << static_cast<int>(result);
std::unique_ptr<DownloadTargetInfo> target_info(new DownloadTargetInfo);
target_info->target_path = local_path_;
+ target_info->result = result;
target_info->target_disposition =
- (HasPromptedForPath() || should_prompt_
+ (HasPromptedForPath() ||
+ confirmation_reason_ != DownloadConfirmationReason::NONE
? DownloadItem::TARGET_DISPOSITION_PROMPT
: DownloadItem::TARGET_DISPOSITION_OVERWRITE);
target_info->danger_type = danger_type_;
@@ -777,35 +785,30 @@ void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
delete this;
}
-void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() {
- // Path substitution failed.
- virtual_path_.clear();
- local_path_.clear();
- intermediate_path_.clear();
- ScheduleCallbackAndDeleteSelf();
-}
-
Profile* DownloadTargetDeterminer::GetProfile() const {
DCHECK(download_->GetBrowserContext());
return Profile::FromBrowserContext(download_->GetBrowserContext());
}
-bool DownloadTargetDeterminer::ShouldPromptForDownload(
+DownloadConfirmationReason DownloadTargetDeterminer::ShouldPromptForDownload(
const base::FilePath& filename) const {
-#if BUILDFLAG(ANDROID_JAVA_UI)
- // Don't prompt user about saving path on Android.
- // TODO(qinmin): show an error toast to warn user in certain cases.
- return false;
-#endif
if (is_resumption_) {
// For resumed downloads, if the target disposition or prefs require
// prompting, the user has already been prompted. Try to respect the user's
// selection, unless we've discovered that the target path cannot be used
// for some reason.
content::DownloadInterruptReason reason = download_->GetLastReason();
- return (reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED ||
- reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE ||
- reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE);
+ switch (reason) {
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
+ return DownloadConfirmationReason::TARGET_NOT_WRITEABLE;
+
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
+ case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
+ return DownloadConfirmationReason::TARGET_NO_SPACE;
+
+ default:
+ return DownloadConfirmationReason::NONE;
+ }
}
// If the download path is forced, don't prompt.
@@ -813,38 +816,38 @@ bool DownloadTargetDeterminer::ShouldPromptForDownload(
// 'Save As' downloads shouldn't have a forced path.
DCHECK(DownloadItem::TARGET_DISPOSITION_PROMPT !=
download_->GetTargetDisposition());
- return false;
+ return DownloadConfirmationReason::NONE;
}
// Don't ask where to save if the download path is managed. Even if the user
// wanted to be prompted for "all" downloads, or if this was a 'Save As'
// download.
if (download_prefs_->IsDownloadPathManaged())
- return false;
+ return DownloadConfirmationReason::NONE;
// Prompt if this is a 'Save As' download.
if (download_->GetTargetDisposition() ==
DownloadItem::TARGET_DISPOSITION_PROMPT)
- return true;
-
- // Check if the user has the "Always prompt for download location" preference
- // set. If so we prompt for most downloads except 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 (download_prefs_->PromptForDownload() &&
- !download_crx_util::IsExtensionDownload(*download_) &&
- !filename.MatchesExtension(extensions::kExtensionFileExtension) &&
- !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename))
- return true;
-
- // Otherwise, don't prompt. Note that the user might still be prompted if
- // there are unresolved conflicts during path reservation (e.g. due to the
- // target path being unwriteable or because there are too many conflicting
- // files), or if an extension signals that the user be prompted on a filename
- // conflict.
- return false;
+ return DownloadConfirmationReason::SAVE_AS;
+
+#if defined(ENABLE_EXTENSIONS)
+ // Don't prompt for extension downloads.
+ if (download_crx_util::IsExtensionDownload(*download_) ||
+ filename.MatchesExtension(extensions::kExtensionFileExtension))
+ return DownloadConfirmationReason::NONE;
+#endif
+
+ // Don't prompt for file types that are marked for opening automatically.
+ if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename))
+ return DownloadConfirmationReason::NONE;
+
+ // For everything else, prompting is controlled by the PromptForDownload pref.
+ // The user may still be prompted even if this pref is disabled due to, for
+ // example, there being an unresolvable filename conflict or the target path
+ // is not writeable.
+ return download_prefs_->PromptForDownload()
+ ? DownloadConfirmationReason::PREFERENCE
+ : DownloadConfirmationReason::NONE;
}
bool DownloadTargetDeterminer::HasPromptedForPath() const {
@@ -859,7 +862,8 @@ DownloadFileType::DangerLevel DownloadTargetDeterminer::GetDangerLevel(
// If the user has has been prompted or will be, assume that the user has
// approved the download. A programmatic download is considered safe unless it
// contains malware.
- if (HasPromptedForPath() || should_prompt_ ||
+ if (HasPromptedForPath() ||
+ confirmation_reason_ != DownloadConfirmationReason::NONE ||
!download_->GetForcedFilePath().empty())
return DownloadFileType::NOT_DANGEROUS;
@@ -877,8 +881,8 @@ DownloadFileType::DangerLevel DownloadTargetDeterminer::GetDangerLevel(
#if defined(ENABLE_EXTENSIONS)
// Extensions that are not from the gallery are considered dangerous.
- // When off-store install is disabled we skip this, since in this case, we
- // will not offer to install the extension.
+ // Exception: If off-store install is disabled, then extension downloads are
+ // not considered dangerous since we will not offer to install these.
if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() &&
is_extension_download &&
!extensions::WebstoreInstaller::GetAssociatedApproval(*download_)) {
@@ -920,20 +924,22 @@ void DownloadTargetDeterminer::OnDownloadDestroyed(
DownloadItem* download) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_EQ(download_, download);
- CancelOnFailureAndDeleteSelf();
+ ScheduleCallbackAndDeleteSelf(DownloadTargetResult::USER_CANCELED);
}
// static
-void DownloadTargetDeterminer::Start(content::DownloadItem* download,
- const base::FilePath& initial_virtual_path,
- DownloadPrefs* download_prefs,
- DownloadTargetDeterminerDelegate* delegate,
- const CompletionCallback& callback) {
+void DownloadTargetDeterminer::Start(
+ content::DownloadItem* download,
+ const base::FilePath& initial_virtual_path,
+ DownloadPathReservationTracker::FilenameConflictAction conflict_action,
+ DownloadPrefs* download_prefs,
+ DownloadTargetDeterminerDelegate* delegate,
+ const CompletionCallback& callback) {
// DownloadTargetDeterminer owns itself and will self destruct when the job is
// complete or the download item is destroyed. The callback is always invoked
// asynchronously.
- new DownloadTargetDeterminer(download, initial_virtual_path, download_prefs,
- delegate, callback);
+ new DownloadTargetDeterminer(download, initial_virtual_path, conflict_action,
+ download_prefs, delegate, callback);
}
// static

Powered by Google App Engine
This is Rietveld 408576698