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 c556d3e77059062769f8258469fc93a6da575760..cf004940c254a73d25b007c1ef15f479366007bd 100644 |
--- a/chrome/browser/download/download_target_determiner.cc |
+++ b/chrome/browser/download/download_target_determiner.cc |
@@ -22,20 +22,19 @@ |
#include "chrome/common/pref_names.h" |
#include "content/public/browser/browser_context.h" |
#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/download_interrupt_reasons.h" |
#include "grit/generated_resources.h" |
#include "net/base/net_util.h" |
#include "ui/base/l10n/l10n_util.h" |
-#if defined(OS_CHROMEOS) |
-#include "chrome/browser/chromeos/drive/download_handler.h" |
-#include "chrome/browser/chromeos/drive/file_system_util.h" |
-#endif |
- |
using content::BrowserThread; |
using content::DownloadItem; |
namespace { |
+const base::FilePath::CharType kCrdownloadSuffix[] = |
+ FILE_PATH_LITERAL(".crdownload"); |
+ |
// Condenses the results from HistoryService::GetVisibleVisitCountToHost() to a |
// single bool. A host is considered visited before if prior visible visits were |
// found in history and the first such visit was earlier than the most recent |
@@ -58,17 +57,21 @@ DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() { |
DownloadTargetDeterminer::DownloadTargetDeterminer( |
DownloadItem* download, |
+ const base::FilePath& initial_virtual_path, |
DownloadPrefs* download_prefs, |
DownloadTargetDeterminerDelegate* delegate, |
const content::DownloadTargetCallback& callback) |
: next_state_(STATE_GENERATE_TARGET_PATH), |
should_prompt_(false), |
- create_directory_(false), |
- conflict_action_(download->GetForcedFilePath().empty() ? |
- DownloadPathReservationTracker::UNIQUIFY : |
- DownloadPathReservationTracker::OVERWRITE), |
+ should_notify_extensions_(false), |
+ create_target_directory_(false), |
+ conflict_action_(DownloadPathReservationTracker::OVERWRITE), |
danger_type_(download->GetDangerType()), |
+ virtual_path_(initial_virtual_path), |
download_(download), |
+ is_resumption_(download_->GetLastReason() != |
+ content::DOWNLOAD_INTERRUPT_REASON_NONE && |
+ !initial_virtual_path.empty()), |
download_prefs_(download_prefs), |
delegate_(delegate), |
completion_callback_(callback), |
@@ -135,18 +138,26 @@ void DownloadTargetDeterminer::DoLoop() { |
DownloadTargetDeterminer::Result |
DownloadTargetDeterminer::DoGenerateTargetPath() { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- DCHECK(virtual_path_.empty()); |
DCHECK(local_path_.empty()); |
+ DCHECK(!should_prompt_); |
+ DCHECK(!should_notify_extensions_); |
+ DCHECK_EQ(DownloadPathReservationTracker::OVERWRITE, conflict_action_); |
bool is_forced_path = !download_->GetForcedFilePath().empty(); |
next_state_ = STATE_NOTIFY_EXTENSIONS; |
- // If we don't have a forced path, we should construct a path for the |
- // download. Forced paths are only specified for programmatic downloads |
- // (WebStore, Drag&Drop). Treat the path as a virtual path. We will eventually |
- // determine whether this is a local path and if not, figure out a local path. |
- if (!is_forced_path) { |
- std::string default_filename( |
+ if (!virtual_path_.empty() && HasPromptedForPath() && !is_forced_path) { |
+ // 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_); |
+ } 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 |
+ // (WebStore, Drag&Drop). Treat the path as a virtual path. We will |
+ // eventually determine whether this is a local path and if not, figure out |
+ // a local path. |
+ std::string default_filename( |
l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME)); |
base::FilePath generated_filename = net::GenerateFileName( |
download_->GetURL(), |
@@ -166,9 +177,14 @@ DownloadTargetDeterminer::Result |
target_directory = download_prefs_->DownloadPath(); |
} |
virtual_path_ = target_directory.Append(generated_filename); |
+ conflict_action_ = DownloadPathReservationTracker::UNIQUIFY; |
+ should_notify_extensions_ = true; |
} else { |
- DCHECK(!should_prompt_); |
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 |
+ // supplied to a programmatic download is invalid, then the caller needs to |
+ // intervene. |
} |
DCHECK(virtual_path_.IsAbsolute()); |
DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe(); |
@@ -192,9 +208,7 @@ DownloadTargetDeterminer::Result |
next_state_ = STATE_RESERVE_VIRTUAL_PATH; |
- // If the target path is forced or if we don't have an extensions event |
- // router, then proceed with the original path. |
- if (!download_->GetForcedFilePath().empty()) |
+ if (!should_notify_extensions_) |
return CONTINUE; |
delegate_->NotifyExtensions(download_, virtual_path_, |
@@ -224,7 +238,7 @@ void DownloadTargetDeterminer::NotifyExtensionsDone( |
// suggest it. |
net::GenerateSafeFileName(std::string(), false, &new_path); |
virtual_path_ = new_path; |
- create_directory_ = true; |
+ create_target_directory_ = true; |
conflict_action_ = conflict_action; |
} |
@@ -239,7 +253,7 @@ DownloadTargetDeterminer::Result |
next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH; |
delegate_->ReserveVirtualPath( |
- download_, virtual_path_, create_directory_, conflict_action_, |
+ download_, virtual_path_, create_target_directory_, conflict_action_, |
base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone, |
weak_ptr_factory_.GetWeakPtr())); |
return QUIT_DOLOOP; |
@@ -346,11 +360,8 @@ DownloadTargetDeterminer::Result |
// danger level of the download depends on the file type. This excludes cases |
// where the download has already been deemed dangerous, or where the user is |
// going to be prompted or where this is a programmatic download. |
- if (danger_type_ != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS || |
- should_prompt_ || |
- !download_->GetForcedFilePath().empty()) { |
+ if (danger_type_ != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) |
return CONTINUE; |
- } |
// Assume that: |
// IsDangerousFile(VISITED_REFERRER) => IsDangerousFile(NO_VISITS_...) |
@@ -397,6 +408,8 @@ DownloadTargetDeterminer::Result |
DCHECK(!virtual_path_.empty()); |
DCHECK(!local_path_.empty()); |
DCHECK(intermediate_path_.empty()); |
+ DCHECK(!virtual_path_.MatchesExtension(kCrdownloadSuffix)); |
+ DCHECK(!local_path_.MatchesExtension(kCrdownloadSuffix)); |
next_state_ = STATE_NONE; |
@@ -430,6 +443,19 @@ DownloadTargetDeterminer::Result |
return COMPLETE; |
} |
+ // If this is a resumed download, then re-use the existing intermediate path |
+ // if one is available. A resumed download shouldn't cause a non-dangerous |
+ // download to be considered dangerous upon resumption. Therefore the |
+ // intermediate file should already be in the correct form. |
+ if (is_resumption_ && !download_->GetFullPath().empty() && |
+ local_path_.DirName() == download_->GetFullPath().DirName()) { |
+ DCHECK_NE(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, |
+ download_->GetDangerType()); |
+ DCHECK_EQ(kCrdownloadSuffix, download_->GetFullPath().Extension()); |
+ intermediate_path_ = download_->GetFullPath(); |
+ return COMPLETE; |
+ } |
+ |
// Dangerous downloads receive a random intermediate name that looks like: |
// 'Unconfirmed <random>.crdownload'. |
const base::FilePath::CharType kUnconfirmedFormatSuffix[] = |
@@ -463,8 +489,9 @@ void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() { |
FROM_HERE, |
base::Bind(completion_callback_, |
local_path_, |
- (should_prompt_ ? DownloadItem::TARGET_DISPOSITION_PROMPT : |
- DownloadItem::TARGET_DISPOSITION_OVERWRITE), |
+ (HasPromptedForPath() || should_prompt_ |
+ ? DownloadItem::TARGET_DISPOSITION_PROMPT |
+ : DownloadItem::TARGET_DISPOSITION_OVERWRITE), |
danger_type_, |
intermediate_path_)); |
completion_callback_.Reset(); |
@@ -485,12 +512,23 @@ Profile* DownloadTargetDeterminer::GetProfile() { |
} |
bool DownloadTargetDeterminer::ShouldPromptForDownload( |
- const base::FilePath& filename) { |
+ const base::FilePath& filename) const { |
+ 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); |
+ } |
+ |
// If the download path is forced, don't prompt. |
if (!download_->GetForcedFilePath().empty()) { |
// 'Save As' downloads shouldn't have a forced path. |
- DCHECK_NE(DownloadItem::TARGET_DISPOSITION_PROMPT, |
- download_->GetTargetDisposition()); |
+ DCHECK(DownloadItem::TARGET_DISPOSITION_PROMPT != |
+ download_->GetTargetDisposition()); |
return false; |
} |
@@ -525,8 +563,21 @@ bool DownloadTargetDeterminer::ShouldPromptForDownload( |
return false; |
} |
+bool DownloadTargetDeterminer::HasPromptedForPath() const { |
+ return (is_resumption_ && download_->GetTargetDisposition() == |
+ DownloadItem::TARGET_DISPOSITION_PROMPT); |
+} |
+ |
bool DownloadTargetDeterminer::IsDangerousFile(PriorVisitsToReferrer visits) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ // 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_ || |
+ !download_->GetForcedFilePath().empty()) |
+ return false; |
+ |
const bool is_extension_download = |
download_crx_util::IsExtensionDownload(*download_); |
@@ -583,18 +634,19 @@ void DownloadTargetDeterminer::OnDownloadDestroyed( |
// static |
void DownloadTargetDeterminer::Start( |
content::DownloadItem* download, |
+ const base::FilePath& initial_virtual_path, |
DownloadPrefs* download_prefs, |
DownloadTargetDeterminerDelegate* delegate, |
const content::DownloadTargetCallback& 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, download_prefs, delegate, callback); |
+ new DownloadTargetDeterminer(download, initial_virtual_path, download_prefs, |
+ delegate, callback); |
} |
// static |
base::FilePath DownloadTargetDeterminer::GetCrDownloadPath( |
const base::FilePath& suggested_path) { |
- return base::FilePath(suggested_path.value() + |
- FILE_PATH_LITERAL(".crdownload")); |
+ return base::FilePath(suggested_path.value() + kCrdownloadSuffix); |
} |