1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/download/download_target_determiner.h"
7 #include "base/prefs/pref_service.h"
8 #include "base/rand_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/download/chrome_download_manager_delegate.h"
12 #include "chrome/browser/download/download_crx_util.h"
13 #include "chrome/browser/download/download_extensions.h"
14 #include "chrome/browser/download/download_prefs.h"
15 #include "chrome/browser/extensions/webstore_installer.h"
16 #include "chrome/browser/history/history_service.h"
17 #include "chrome/browser/history/history_service_factory.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/pref_names.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/download_interrupt_reasons.h"
23 #include "extensions/common/constants.h"
24 #include "extensions/common/feature_switch.h"
25 #include "grit/generated_resources.h"
26 #include "net/base/filename_util.h"
27 #include "net/base/mime_util.h"
28 #include "ui/base/l10n/l10n_util.h"
30 #if defined(ENABLE_PLUGINS)
31 #include "chrome/browser/plugins/plugin_prefs.h"
32 #include "content/public/browser/plugin_service.h"
33 #include "content/public/common/webplugininfo.h"
36 using content::BrowserThread;
37 using content::DownloadItem;
41 const base::FilePath::CharType kCrdownloadSuffix[] =
42 FILE_PATH_LITERAL(".crdownload");
44 // Condenses the results from HistoryService::GetVisibleVisitCountToHost() to a
45 // single bool. A host is considered visited before if prior visible visits were
46 // found in history and the first such visit was earlier than the most recent
48 void VisitCountsToVisitedBefore(
49 const base::Callback<void(bool)>& callback,
50 HistoryService::Handle unused_handle,
53 base::Time first_visit) {
55 found_visits && count > 0 &&
56 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
61 DownloadTargetInfo::DownloadTargetInfo()
62 : is_filetype_handled_safely(false) {}
64 DownloadTargetInfo::~DownloadTargetInfo() {}
66 DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() {
69 DownloadTargetDeterminer::DownloadTargetDeterminer(
70 DownloadItem* download,
71 const base::FilePath& initial_virtual_path,
72 DownloadPrefs* download_prefs,
73 DownloadTargetDeterminerDelegate* delegate,
74 const CompletionCallback& callback)
75 : next_state_(STATE_GENERATE_TARGET_PATH),
76 should_prompt_(false),
77 should_notify_extensions_(false),
78 create_target_directory_(false),
79 conflict_action_(DownloadPathReservationTracker::OVERWRITE),
80 danger_type_(download->GetDangerType()),
81 virtual_path_(initial_virtual_path),
82 is_filetype_handled_safely_(false),
84 is_resumption_(download_->GetLastReason() !=
85 content::DOWNLOAD_INTERRUPT_REASON_NONE &&
86 !initial_virtual_path.empty()),
87 download_prefs_(download_prefs),
89 completion_callback_(callback),
90 weak_ptr_factory_(this) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94 download_->AddObserver(this);
99 DownloadTargetDeterminer::~DownloadTargetDeterminer() {
100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
102 DCHECK(completion_callback_.is_null());
103 download_->RemoveObserver(this);
106 void DownloadTargetDeterminer::DoLoop() {
107 Result result = CONTINUE;
109 State current_state = next_state_;
110 next_state_ = STATE_NONE;
112 switch (current_state) {
113 case STATE_GENERATE_TARGET_PATH:
114 result = DoGenerateTargetPath();
116 case STATE_NOTIFY_EXTENSIONS:
117 result = DoNotifyExtensions();
119 case STATE_RESERVE_VIRTUAL_PATH:
120 result = DoReserveVirtualPath();
122 case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH:
123 result = DoPromptUserForDownloadPath();
125 case STATE_DETERMINE_LOCAL_PATH:
126 result = DoDetermineLocalPath();
128 case STATE_DETERMINE_MIME_TYPE:
129 result = DoDetermineMimeType();
131 case STATE_DETERMINE_IF_HANDLED_SAFELY_BY_BROWSER:
132 result = DoDetermineIfHandledSafely();
134 case STATE_CHECK_DOWNLOAD_URL:
135 result = DoCheckDownloadUrl();
137 case STATE_DETERMINE_INTERMEDIATE_PATH:
138 result = DoDetermineIntermediatePath();
140 case STATE_CHECK_VISITED_REFERRER_BEFORE:
141 result = DoCheckVisitedReferrerBefore();
147 } while (result == CONTINUE);
148 // Note that if a callback completes synchronously, the handler will still
149 // return QUIT_DOLOOP. In this case, an inner DoLoop() may complete the target
150 // determination and delete |this|.
152 if (result == COMPLETE)
153 ScheduleCallbackAndDeleteSelf();
156 DownloadTargetDeterminer::Result
157 DownloadTargetDeterminer::DoGenerateTargetPath() {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 DCHECK(local_path_.empty());
160 DCHECK(!should_prompt_);
161 DCHECK(!should_notify_extensions_);
162 DCHECK_EQ(DownloadPathReservationTracker::OVERWRITE, conflict_action_);
163 bool is_forced_path = !download_->GetForcedFilePath().empty();
165 next_state_ = STATE_NOTIFY_EXTENSIONS;
167 if (!virtual_path_.empty() && HasPromptedForPath() && !is_forced_path) {
168 // The download is being resumed and the user has already been prompted for
169 // a path. Assume that it's okay to overwrite the file if there's a conflict
170 // and reuse the selection.
171 should_prompt_ = ShouldPromptForDownload(virtual_path_);
172 } else if (!is_forced_path) {
173 // If we don't have a forced path, we should construct a path for the
174 // download. Forced paths are only specified for programmatic downloads
175 // (WebStore, Drag&Drop). Treat the path as a virtual path. We will
176 // eventually determine whether this is a local path and if not, figure out
178 std::string default_filename(
179 l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
180 base::FilePath generated_filename = net::GenerateFileName(
182 download_->GetContentDisposition(),
183 GetProfile()->GetPrefs()->GetString(prefs::kDefaultCharset),
184 download_->GetSuggestedFilename(),
185 download_->GetMimeType(),
187 should_prompt_ = ShouldPromptForDownload(generated_filename);
188 base::FilePath target_directory;
189 if (should_prompt_) {
190 DCHECK(!download_prefs_->IsDownloadPathManaged());
191 // If the user is going to be prompted and the user has been prompted
192 // before, then always prefer the last directory that the user selected.
193 target_directory = download_prefs_->SaveFilePath();
195 target_directory = download_prefs_->DownloadPath();
197 virtual_path_ = target_directory.Append(generated_filename);
198 conflict_action_ = DownloadPathReservationTracker::UNIQUIFY;
199 should_notify_extensions_ = true;
201 virtual_path_ = download_->GetForcedFilePath();
202 // If this is a resumed download which was previously interrupted due to an
203 // issue with the forced path, the user is still not prompted. If the path
204 // supplied to a programmatic download is invalid, then the caller needs to
207 DCHECK(virtual_path_.IsAbsolute());
208 DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe();
210 // If the download is DOA, don't bother going any further. This would be the
211 // case for a download that failed to initialize (e.g. the initial temporary
212 // file couldn't be created because both the downloads directory and the
213 // temporary directory are unwriteable).
215 // A virtual path is determined for DOA downloads for display purposes. This
216 // is why this check is performed here instead of at the start.
217 if (download_->GetState() != DownloadItem::IN_PROGRESS)
222 DownloadTargetDeterminer::Result
223 DownloadTargetDeterminer::DoNotifyExtensions() {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
225 DCHECK(!virtual_path_.empty());
227 next_state_ = STATE_RESERVE_VIRTUAL_PATH;
229 if (!should_notify_extensions_)
232 delegate_->NotifyExtensions(download_, virtual_path_,
233 base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone,
234 weak_ptr_factory_.GetWeakPtr()));
238 void DownloadTargetDeterminer::NotifyExtensionsDone(
239 const base::FilePath& suggested_path,
240 DownloadPathReservationTracker::FilenameConflictAction conflict_action) {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
242 DVLOG(20) << "Extension suggested path: " << suggested_path.AsUTF8Unsafe();
244 // Extensions should not call back here more than once.
245 DCHECK_EQ(STATE_RESERVE_VIRTUAL_PATH, next_state_);
247 if (!suggested_path.empty()) {
248 // If an extension overrides the filename, then the target directory will be
249 // forced to download_prefs_->DownloadPath() since extensions cannot place
250 // downloaded files anywhere except there. This prevents subdirectories from
251 // accumulating: if an extension is allowed to say that a file should go in
252 // last_download_path/music/foo.mp3, then last_download_path will accumulate
253 // the subdirectory /music/ so that the next download may end up in
254 // Downloads/music/music/music/bar.mp3.
255 base::FilePath new_path(download_prefs_->DownloadPath().Append(
256 suggested_path).NormalizePathSeparators());
257 // Do not pass a mime type to GenerateSafeFileName so that it does not force
258 // the filename to have an extension if the (Chrome) extension does not
260 net::GenerateSafeFileName(std::string(), false, &new_path);
261 virtual_path_ = new_path;
262 create_target_directory_ = true;
264 // An extension may set conflictAction without setting filename.
265 if (conflict_action != DownloadPathReservationTracker::UNIQUIFY)
266 conflict_action_ = conflict_action;
271 DownloadTargetDeterminer::Result
272 DownloadTargetDeterminer::DoReserveVirtualPath() {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274 DCHECK(!virtual_path_.empty());
276 next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH;
278 delegate_->ReserveVirtualPath(
279 download_, virtual_path_, create_target_directory_, conflict_action_,
280 base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone,
281 weak_ptr_factory_.GetWeakPtr()));
285 void DownloadTargetDeterminer::ReserveVirtualPathDone(
286 const base::FilePath& path, bool verified) {
287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
288 DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe()
289 << " Verified:" << verified;
290 DCHECK_EQ(STATE_PROMPT_USER_FOR_DOWNLOAD_PATH, next_state_);
292 should_prompt_ = (should_prompt_ || !verified);
293 virtual_path_ = path;
297 DownloadTargetDeterminer::Result
298 DownloadTargetDeterminer::DoPromptUserForDownloadPath() {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
300 DCHECK(!virtual_path_.empty());
302 next_state_ = STATE_DETERMINE_LOCAL_PATH;
304 if (should_prompt_) {
305 delegate_->PromptUserForDownloadPath(
308 base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone,
309 weak_ptr_factory_.GetWeakPtr()));
315 void DownloadTargetDeterminer::PromptUserForDownloadPathDone(
316 const base::FilePath& virtual_path) {
317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
318 DVLOG(20) << "User selected path:" << virtual_path.AsUTF8Unsafe();
319 if (virtual_path.empty()) {
320 CancelOnFailureAndDeleteSelf();
323 DCHECK_EQ(STATE_DETERMINE_LOCAL_PATH, next_state_);
325 virtual_path_ = virtual_path;
326 download_prefs_->SetSaveFilePath(virtual_path_.DirName());
330 DownloadTargetDeterminer::Result
331 DownloadTargetDeterminer::DoDetermineLocalPath() {
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
333 DCHECK(!virtual_path_.empty());
334 DCHECK(local_path_.empty());
336 next_state_ = STATE_DETERMINE_MIME_TYPE;
338 delegate_->DetermineLocalPath(
341 base::Bind(&DownloadTargetDeterminer::DetermineLocalPathDone,
342 weak_ptr_factory_.GetWeakPtr()));
346 void DownloadTargetDeterminer::DetermineLocalPathDone(
347 const base::FilePath& local_path) {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
349 DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe();
350 if (local_path.empty()) {
351 // Path subsitution failed.
352 CancelOnFailureAndDeleteSelf();
355 DCHECK_EQ(STATE_DETERMINE_MIME_TYPE, next_state_);
357 local_path_ = local_path;
361 DownloadTargetDeterminer::Result
362 DownloadTargetDeterminer::DoDetermineMimeType() {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364 DCHECK(!virtual_path_.empty());
365 DCHECK(!local_path_.empty());
366 DCHECK(mime_type_.empty());
368 next_state_ = STATE_DETERMINE_IF_HANDLED_SAFELY_BY_BROWSER;
370 if (virtual_path_ == local_path_) {
371 delegate_->GetFileMimeType(
373 base::Bind(&DownloadTargetDeterminer::DetermineMimeTypeDone,
374 weak_ptr_factory_.GetWeakPtr()));
380 void DownloadTargetDeterminer::DetermineMimeTypeDone(
381 const std::string& mime_type) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383 DVLOG(20) << "MIME type: " << mime_type;
384 DCHECK_EQ(STATE_DETERMINE_IF_HANDLED_SAFELY_BY_BROWSER, next_state_);
386 mime_type_ = mime_type;
390 #if defined(ENABLE_PLUGINS)
391 // The code below is used by DoDetermineIfHandledSafely to determine if the
392 // file type is handled by a sandboxed plugin.
395 void InvokeClosureAfterGetPluginCallback(
396 const base::Closure& closure,
397 const std::vector<content::WebPluginInfo>& unused) {
401 enum ActionOnStalePluginList {
402 RETRY_IF_STALE_PLUGIN_LIST,
403 IGNORE_IF_STALE_PLUGIN_LIST
406 void IsHandledBySafePlugin(content::ResourceContext* resource_context,
408 const std::string& mime_type,
409 ActionOnStalePluginList stale_plugin_action,
410 const base::Callback<void(bool)>& callback) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
412 DCHECK(!mime_type.empty());
413 using content::WebPluginInfo;
415 std::string actual_mime_type;
416 bool is_stale = false;
417 WebPluginInfo plugin_info;
419 content::PluginService* plugin_service =
420 content::PluginService::GetInstance();
421 bool plugin_found = plugin_service->GetPluginInfo(-1, -1, resource_context,
422 url, GURL(), mime_type,
426 if (is_stale && stale_plugin_action == RETRY_IF_STALE_PLUGIN_LIST) {
427 // The GetPlugins call causes the plugin list to be refreshed. Once that's
428 // done we can retry the GetPluginInfo call. We break out of this cycle
429 // after a single retry in order to avoid retrying indefinitely.
430 plugin_service->GetPlugins(
431 base::Bind(&InvokeClosureAfterGetPluginCallback,
432 base::Bind(&IsHandledBySafePlugin,
436 IGNORE_IF_STALE_PLUGIN_LIST,
440 // In practice, we assume that retrying once is enough.
442 bool is_handled_safely =
444 (plugin_info.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS ||
445 plugin_info.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS);
446 BrowserThread::PostTask(
447 BrowserThread::UI, FROM_HERE, base::Bind(callback, is_handled_safely));
451 #endif // ENABLE_PLUGINS
453 DownloadTargetDeterminer::Result
454 DownloadTargetDeterminer::DoDetermineIfHandledSafely() {
455 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
456 DCHECK(!virtual_path_.empty());
457 DCHECK(!local_path_.empty());
458 DCHECK(!is_filetype_handled_safely_);
460 next_state_ = STATE_CHECK_DOWNLOAD_URL;
462 if (mime_type_.empty())
465 if (net::IsSupportedMimeType(mime_type_)) {
466 is_filetype_handled_safely_ = true;
470 #if defined(ENABLE_PLUGINS)
471 BrowserThread::PostTask(
475 &IsHandledBySafePlugin,
476 GetProfile()->GetResourceContext(),
477 net::FilePathToFileURL(local_path_),
479 RETRY_IF_STALE_PLUGIN_LIST,
480 base::Bind(&DownloadTargetDeterminer::DetermineIfHandledSafelyDone,
481 weak_ptr_factory_.GetWeakPtr())));
488 void DownloadTargetDeterminer::DetermineIfHandledSafelyDone(
489 bool is_handled_safely) {
490 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
491 DVLOG(20) << "Is file type handled safely: " << is_filetype_handled_safely_;
492 DCHECK_EQ(STATE_CHECK_DOWNLOAD_URL, next_state_);
493 is_filetype_handled_safely_ = is_handled_safely;
497 DownloadTargetDeterminer::Result
498 DownloadTargetDeterminer::DoCheckDownloadUrl() {
499 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
500 DCHECK(!virtual_path_.empty());
501 next_state_ = STATE_CHECK_VISITED_REFERRER_BEFORE;
502 delegate_->CheckDownloadUrl(
505 base::Bind(&DownloadTargetDeterminer::CheckDownloadUrlDone,
506 weak_ptr_factory_.GetWeakPtr()));
510 void DownloadTargetDeterminer::CheckDownloadUrlDone(
511 content::DownloadDangerType danger_type) {
512 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
513 DVLOG(20) << "URL Check Result:" << danger_type;
514 DCHECK_EQ(STATE_CHECK_VISITED_REFERRER_BEFORE, next_state_);
515 danger_type_ = danger_type;
519 DownloadTargetDeterminer::Result
520 DownloadTargetDeterminer::DoCheckVisitedReferrerBefore() {
521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
523 next_state_ = STATE_DETERMINE_INTERMEDIATE_PATH;
525 // Checking if there are prior visits to the referrer is only necessary if the
526 // danger level of the download depends on the file type. This excludes cases
527 // where the download has already been deemed dangerous, or where the user is
528 // going to be prompted or where this is a programmatic download.
529 if (danger_type_ != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)
533 // IsDangerousFile(VISITED_REFERRER) => IsDangerousFile(NO_VISITS_...)
534 // I.e. having visited a referrer only lowers a file's danger level.
535 if (IsDangerousFile(NO_VISITS_TO_REFERRER)) {
536 // Only need to ping the history DB if the download would be considered safe
537 // if there are prior visits and is considered dangerous otherwise.
538 if (!IsDangerousFile(VISITED_REFERRER)) {
539 // HistoryServiceFactory redirects incognito profiles to on-record
540 // profiles. There's no history for on-record profiles in unit_tests.
541 HistoryService* history_service = HistoryServiceFactory::GetForProfile(
542 GetProfile(), Profile::EXPLICIT_ACCESS);
544 if (history_service && download_->GetReferrerUrl().is_valid()) {
545 history_service->GetVisibleVisitCountToHost(
546 download_->GetReferrerUrl(), &history_consumer_,
547 base::Bind(&VisitCountsToVisitedBefore, base::Bind(
548 &DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone,
549 weak_ptr_factory_.GetWeakPtr())));
554 // If the danger level doesn't depend on having visited the refererrer URL
555 // or if original profile doesn't have a HistoryService or the referrer url
556 // is invalid, then assume the referrer has not been visited before.
557 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
562 void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone(
563 bool visited_referrer_before) {
564 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
565 DCHECK_EQ(STATE_DETERMINE_INTERMEDIATE_PATH, next_state_);
567 visited_referrer_before ? VISITED_REFERRER : NO_VISITS_TO_REFERRER))
568 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
572 DownloadTargetDeterminer::Result
573 DownloadTargetDeterminer::DoDetermineIntermediatePath() {
574 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
575 DCHECK(!virtual_path_.empty());
576 DCHECK(!local_path_.empty());
577 DCHECK(intermediate_path_.empty());
578 DCHECK(!virtual_path_.MatchesExtension(kCrdownloadSuffix));
579 DCHECK(!local_path_.MatchesExtension(kCrdownloadSuffix));
581 next_state_ = STATE_NONE;
583 // Note that the intermediate filename is always uniquified (i.e. if a file by
584 // the same name exists, it is never overwritten). Therefore the code below
585 // does not attempt to find a name that doesn't conflict with an existing
588 // If the actual target of the download is a virtual path, then the local path
589 // is considered to point to a temporary path. A separate intermediate path is
590 // unnecessary since the local path already serves that purpose.
591 if (virtual_path_.BaseName() != local_path_.BaseName()) {
592 intermediate_path_ = local_path_;
596 // If the download has a forced path and is safe, then just use the
597 // target path. In practice the temporary download file that was created prior
598 // to download filename determination is already named
599 // download_->GetForcedFilePath().
600 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS &&
601 !download_->GetForcedFilePath().empty()) {
602 DCHECK_EQ(download_->GetForcedFilePath().value(), local_path_.value());
603 intermediate_path_ = local_path_;
607 // Other safe downloads get a .crdownload suffix for their intermediate name.
608 if (danger_type_ == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
609 intermediate_path_ = GetCrDownloadPath(local_path_);
613 // If this is a resumed download, then re-use the existing intermediate path
614 // if one is available. A resumed download shouldn't cause a non-dangerous
615 // download to be considered dangerous upon resumption. Therefore the
616 // intermediate file should already be in the correct form.
617 if (is_resumption_ && !download_->GetFullPath().empty() &&
618 local_path_.DirName() == download_->GetFullPath().DirName()) {
619 DCHECK_NE(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
620 download_->GetDangerType());
621 DCHECK_EQ(kCrdownloadSuffix, download_->GetFullPath().Extension());
622 intermediate_path_ = download_->GetFullPath();
626 // Dangerous downloads receive a random intermediate name that looks like:
627 // 'Unconfirmed <random>.crdownload'.
628 const base::FilePath::CharType kUnconfirmedFormatSuffix[] =
629 FILE_PATH_LITERAL(" %d.crdownload");
630 // Range of the <random> uniquifier.
631 const int kUnconfirmedUniquifierRange = 1000000;
633 base::string16 unconfirmed_format =
634 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
636 std::string unconfirmed_format =
637 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
639 unconfirmed_format.append(kUnconfirmedFormatSuffix);
641 base::FilePath::StringType file_name = base::StringPrintf(
642 unconfirmed_format.c_str(),
643 base::RandInt(0, kUnconfirmedUniquifierRange));
644 intermediate_path_ = local_path_.DirName().Append(file_name);
648 void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
650 DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe()
651 << " Local:" << local_path_.AsUTF8Unsafe()
652 << " Intermediate:" << intermediate_path_.AsUTF8Unsafe()
653 << " Should prompt:" << should_prompt_
654 << " Danger type:" << danger_type_;
655 scoped_ptr<DownloadTargetInfo> target_info(new DownloadTargetInfo);
657 target_info->target_path = local_path_;
658 target_info->target_disposition =
659 (HasPromptedForPath() || should_prompt_
660 ? DownloadItem::TARGET_DISPOSITION_PROMPT
661 : DownloadItem::TARGET_DISPOSITION_OVERWRITE);
662 target_info->danger_type = danger_type_;
663 target_info->intermediate_path = intermediate_path_;
664 target_info->mime_type = mime_type_;
665 target_info->is_filetype_handled_safely = is_filetype_handled_safely_;
667 base::MessageLoop::current()->PostTask(
668 FROM_HERE, base::Bind(completion_callback_, base::Passed(&target_info)));
669 completion_callback_.Reset();
673 void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() {
674 // Path substitution failed.
675 virtual_path_.clear();
677 intermediate_path_.clear();
678 ScheduleCallbackAndDeleteSelf();
681 Profile* DownloadTargetDeterminer::GetProfile() {
682 DCHECK(download_->GetBrowserContext());
683 return Profile::FromBrowserContext(download_->GetBrowserContext());
686 bool DownloadTargetDeterminer::ShouldPromptForDownload(
687 const base::FilePath& filename) const {
688 if (is_resumption_) {
689 // For resumed downloads, if the target disposition or prefs require
690 // prompting, the user has already been prompted. Try to respect the user's
691 // selection, unless we've discovered that the target path cannot be used
693 content::DownloadInterruptReason reason = download_->GetLastReason();
694 return (reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED ||
695 reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE ||
696 reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE);
699 // If the download path is forced, don't prompt.
700 if (!download_->GetForcedFilePath().empty()) {
701 // 'Save As' downloads shouldn't have a forced path.
702 DCHECK(DownloadItem::TARGET_DISPOSITION_PROMPT !=
703 download_->GetTargetDisposition());
707 // Don't ask where to save if the download path is managed. Even if the user
708 // wanted to be prompted for "all" downloads, or if this was a 'Save As'
710 if (download_prefs_->IsDownloadPathManaged())
713 // Prompt if this is a 'Save As' download.
714 if (download_->GetTargetDisposition() ==
715 DownloadItem::TARGET_DISPOSITION_PROMPT)
718 // Check if the user has the "Always prompt for download location" preference
719 // set. If so we prompt for most downloads except for the following scenarios:
720 // 1) Extension installation. Note that we only care here about the case where
721 // an extension is installed, not when one is downloaded with "save as...".
722 // 2) Filetypes marked "always open." If the user just wants this file opened,
723 // don't bother asking where to keep it.
724 if (download_prefs_->PromptForDownload() &&
725 !download_crx_util::IsExtensionDownload(*download_) &&
726 !filename.MatchesExtension(extensions::kExtensionFileExtension) &&
727 !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename))
730 // Otherwise, don't prompt. Note that the user might still be prompted if
731 // there are unresolved conflicts during path reservation (e.g. due to the
732 // target path being unwriteable or because there are too many conflicting
733 // files), or if an extension signals that the user be prompted on a filename
738 bool DownloadTargetDeterminer::HasPromptedForPath() const {
739 return (is_resumption_ && download_->GetTargetDisposition() ==
740 DownloadItem::TARGET_DISPOSITION_PROMPT);
743 bool DownloadTargetDeterminer::IsDangerousFile(PriorVisitsToReferrer visits) {
744 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
746 // If the user has has been prompted or will be, assume that the user has
747 // approved the download. A programmatic download is considered safe unless it
749 if (HasPromptedForPath() || should_prompt_ ||
750 !download_->GetForcedFilePath().empty())
753 const bool is_extension_download =
754 download_crx_util::IsExtensionDownload(*download_);
756 // User-initiated extension downloads from pref-whitelisted sources are not
757 // considered dangerous.
758 if (download_->HasUserGesture() &&
759 is_extension_download &&
760 download_crx_util::OffStoreInstallAllowedByPrefs(
761 GetProfile(), *download_)) {
765 // Extensions that are not from the gallery are considered dangerous.
766 // When off-store install is disabled we skip this, since in this case, we
767 // will not offer to install the extension.
768 if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() &&
769 is_extension_download &&
770 !extensions::WebstoreInstaller::GetAssociatedApproval(*download_)) {
774 // Anything the user has marked auto-open is OK if it's user-initiated.
775 if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(virtual_path_) &&
776 download_->HasUserGesture())
779 switch (download_util::GetFileDangerLevel(virtual_path_.BaseName())) {
780 case download_util::NOT_DANGEROUS:
783 case download_util::ALLOW_ON_USER_GESTURE:
784 // "Allow on user gesture" is OK when we have a user gesture and the
785 // hosting page has been visited before today.
786 if (download_->GetTransitionType() &
787 content::PAGE_TRANSITION_FROM_ADDRESS_BAR) {
790 return !download_->HasUserGesture() || visits == NO_VISITS_TO_REFERRER;
792 case download_util::DANGEROUS:
799 void DownloadTargetDeterminer::OnDownloadDestroyed(
800 DownloadItem* download) {
801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
802 DCHECK_EQ(download_, download);
803 CancelOnFailureAndDeleteSelf();
807 void DownloadTargetDeterminer::Start(content::DownloadItem* download,
808 const base::FilePath& initial_virtual_path,
809 DownloadPrefs* download_prefs,
810 DownloadTargetDeterminerDelegate* delegate,
811 const CompletionCallback& callback) {
812 // DownloadTargetDeterminer owns itself and will self destruct when the job is
813 // complete or the download item is destroyed. The callback is always invoked
815 new DownloadTargetDeterminer(download, initial_virtual_path, download_prefs,
820 base::FilePath DownloadTargetDeterminer::GetCrDownloadPath(
821 const base::FilePath& suggested_path) {
822 return base::FilePath(suggested_path.value() + kCrdownloadSuffix);