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/mime_util.h"
27 #include "net/base/net_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 if (!suggested_path
.empty()) {
245 // If an extension overrides the filename, then the target directory will be
246 // forced to download_prefs_->DownloadPath() since extensions cannot place
247 // downloaded files anywhere except there. This prevents subdirectories from
248 // accumulating: if an extension is allowed to say that a file should go in
249 // last_download_path/music/foo.mp3, then last_download_path will accumulate
250 // the subdirectory /music/ so that the next download may end up in
251 // Downloads/music/music/music/bar.mp3.
252 base::FilePath
new_path(download_prefs_
->DownloadPath().Append(
253 suggested_path
).NormalizePathSeparators());
254 // Do not pass a mime type to GenerateSafeFileName so that it does not force
255 // the filename to have an extension if the (Chrome) extension does not
257 net::GenerateSafeFileName(std::string(), false, &new_path
);
258 virtual_path_
= new_path
;
259 create_target_directory_
= true;
260 conflict_action_
= conflict_action
;
266 DownloadTargetDeterminer::Result
267 DownloadTargetDeterminer::DoReserveVirtualPath() {
268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
269 DCHECK(!virtual_path_
.empty());
271 next_state_
= STATE_PROMPT_USER_FOR_DOWNLOAD_PATH
;
273 delegate_
->ReserveVirtualPath(
274 download_
, virtual_path_
, create_target_directory_
, conflict_action_
,
275 base::Bind(&DownloadTargetDeterminer::ReserveVirtualPathDone
,
276 weak_ptr_factory_
.GetWeakPtr()));
280 void DownloadTargetDeterminer::ReserveVirtualPathDone(
281 const base::FilePath
& path
, bool verified
) {
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
283 DVLOG(20) << "Reserved path: " << path
.AsUTF8Unsafe()
284 << " Verified:" << verified
;
285 should_prompt_
= (should_prompt_
|| !verified
);
286 virtual_path_
= path
;
290 DownloadTargetDeterminer::Result
291 DownloadTargetDeterminer::DoPromptUserForDownloadPath() {
292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
293 DCHECK(!virtual_path_
.empty());
295 next_state_
= STATE_DETERMINE_LOCAL_PATH
;
297 if (should_prompt_
) {
298 delegate_
->PromptUserForDownloadPath(
301 base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone
,
302 weak_ptr_factory_
.GetWeakPtr()));
308 void DownloadTargetDeterminer::PromptUserForDownloadPathDone(
309 const base::FilePath
& virtual_path
) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
311 DVLOG(20) << "User selected path:" << virtual_path
.AsUTF8Unsafe();
312 if (virtual_path
.empty()) {
313 CancelOnFailureAndDeleteSelf();
316 virtual_path_
= virtual_path
;
317 download_prefs_
->SetSaveFilePath(virtual_path_
.DirName());
321 DownloadTargetDeterminer::Result
322 DownloadTargetDeterminer::DoDetermineLocalPath() {
323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
324 DCHECK(!virtual_path_
.empty());
325 DCHECK(local_path_
.empty());
327 next_state_
= STATE_DETERMINE_MIME_TYPE
;
329 delegate_
->DetermineLocalPath(
332 base::Bind(&DownloadTargetDeterminer::DetermineLocalPathDone
,
333 weak_ptr_factory_
.GetWeakPtr()));
337 void DownloadTargetDeterminer::DetermineLocalPathDone(
338 const base::FilePath
& local_path
) {
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
340 DVLOG(20) << "Local path: " << local_path
.AsUTF8Unsafe();
341 if (local_path
.empty()) {
342 // Path subsitution failed.
343 CancelOnFailureAndDeleteSelf();
346 local_path_
= local_path
;
350 DownloadTargetDeterminer::Result
351 DownloadTargetDeterminer::DoDetermineMimeType() {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
353 DCHECK(!virtual_path_
.empty());
354 DCHECK(!local_path_
.empty());
355 DCHECK(mime_type_
.empty());
357 next_state_
= STATE_DETERMINE_IF_HANDLED_SAFELY_BY_BROWSER
;
359 if (virtual_path_
== local_path_
) {
360 delegate_
->GetFileMimeType(
362 base::Bind(&DownloadTargetDeterminer::DetermineMimeTypeDone
,
363 weak_ptr_factory_
.GetWeakPtr()));
369 void DownloadTargetDeterminer::DetermineMimeTypeDone(
370 const std::string
& mime_type
) {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
372 DVLOG(20) << "MIME type: " << mime_type
;
373 mime_type_
= mime_type
;
377 #if defined(ENABLE_PLUGINS)
378 // The code below is used by DoDetermineIfHandledSafely to determine if the
379 // file type is handled by a sandboxed plugin.
382 void InvokeClosureAfterGetPluginCallback(
383 const base::Closure
& closure
,
384 const std::vector
<content::WebPluginInfo
>& unused
) {
388 enum ActionOnStalePluginList
{
389 RETRY_IF_STALE_PLUGIN_LIST
,
390 IGNORE_IF_STALE_PLUGIN_LIST
393 void IsHandledBySafePlugin(content::ResourceContext
* resource_context
,
395 const std::string
& mime_type
,
396 ActionOnStalePluginList stale_plugin_action
,
397 const base::Callback
<void(bool)>& callback
) {
398 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
399 DCHECK(!mime_type
.empty());
400 using content::WebPluginInfo
;
402 std::string actual_mime_type
;
403 bool is_stale
= false;
404 WebPluginInfo plugin_info
;
406 content::PluginService
* plugin_service
=
407 content::PluginService::GetInstance();
408 bool plugin_found
= plugin_service
->GetPluginInfo(-1, -1, resource_context
,
409 url
, GURL(), mime_type
,
413 if (is_stale
&& stale_plugin_action
== RETRY_IF_STALE_PLUGIN_LIST
) {
414 // The GetPlugins call causes the plugin list to be refreshed. Once that's
415 // done we can retry the GetPluginInfo call. We break out of this cycle
416 // after a single retry in order to avoid retrying indefinitely.
417 plugin_service
->GetPlugins(
418 base::Bind(&InvokeClosureAfterGetPluginCallback
,
419 base::Bind(&IsHandledBySafePlugin
,
423 IGNORE_IF_STALE_PLUGIN_LIST
,
427 // In practice, we assume that retrying once is enough.
429 bool is_handled_safely
=
431 (plugin_info
.type
== WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS
||
432 plugin_info
.type
== WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS
);
433 BrowserThread::PostTask(
434 BrowserThread::UI
, FROM_HERE
, base::Bind(callback
, is_handled_safely
));
438 #endif // ENABLE_PLUGINS
440 DownloadTargetDeterminer::Result
441 DownloadTargetDeterminer::DoDetermineIfHandledSafely() {
442 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
443 DCHECK(!virtual_path_
.empty());
444 DCHECK(!local_path_
.empty());
445 DCHECK(!is_filetype_handled_safely_
);
447 next_state_
= STATE_CHECK_DOWNLOAD_URL
;
449 if (mime_type_
.empty())
452 if (net::IsSupportedMimeType(mime_type_
)) {
453 is_filetype_handled_safely_
= true;
457 #if defined(ENABLE_PLUGINS)
458 BrowserThread::PostTask(
462 &IsHandledBySafePlugin
,
463 GetProfile()->GetResourceContext(),
464 net::FilePathToFileURL(local_path_
),
466 RETRY_IF_STALE_PLUGIN_LIST
,
467 base::Bind(&DownloadTargetDeterminer::DetermineIfHandledSafelyDone
,
468 weak_ptr_factory_
.GetWeakPtr())));
475 void DownloadTargetDeterminer::DetermineIfHandledSafelyDone(
476 bool is_handled_safely
) {
477 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
478 is_filetype_handled_safely_
= is_handled_safely
;
479 DVLOG(20) << "Is file type handled safely: " << is_filetype_handled_safely_
;
483 DownloadTargetDeterminer::Result
484 DownloadTargetDeterminer::DoCheckDownloadUrl() {
485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
486 DCHECK(!virtual_path_
.empty());
487 next_state_
= STATE_CHECK_VISITED_REFERRER_BEFORE
;
488 delegate_
->CheckDownloadUrl(
491 base::Bind(&DownloadTargetDeterminer::CheckDownloadUrlDone
,
492 weak_ptr_factory_
.GetWeakPtr()));
496 void DownloadTargetDeterminer::CheckDownloadUrlDone(
497 content::DownloadDangerType danger_type
) {
498 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
499 DVLOG(20) << "URL Check Result:" << danger_type
;
500 danger_type_
= danger_type
;
504 DownloadTargetDeterminer::Result
505 DownloadTargetDeterminer::DoCheckVisitedReferrerBefore() {
506 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
508 next_state_
= STATE_DETERMINE_INTERMEDIATE_PATH
;
510 // Checking if there are prior visits to the referrer is only necessary if the
511 // danger level of the download depends on the file type. This excludes cases
512 // where the download has already been deemed dangerous, or where the user is
513 // going to be prompted or where this is a programmatic download.
514 if (danger_type_
!= content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
)
518 // IsDangerousFile(VISITED_REFERRER) => IsDangerousFile(NO_VISITS_...)
519 // I.e. having visited a referrer only lowers a file's danger level.
520 if (IsDangerousFile(NO_VISITS_TO_REFERRER
)) {
521 // Only need to ping the history DB if the download would be considered safe
522 // if there are prior visits and is considered dangerous otherwise.
523 if (!IsDangerousFile(VISITED_REFERRER
)) {
524 // HistoryServiceFactory redirects incognito profiles to on-record
525 // profiles. There's no history for on-record profiles in unit_tests.
526 HistoryService
* history_service
= HistoryServiceFactory::GetForProfile(
527 GetProfile(), Profile::EXPLICIT_ACCESS
);
529 if (history_service
&& download_
->GetReferrerUrl().is_valid()) {
530 history_service
->GetVisibleVisitCountToHost(
531 download_
->GetReferrerUrl(), &history_consumer_
,
532 base::Bind(&VisitCountsToVisitedBefore
, base::Bind(
533 &DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone
,
534 weak_ptr_factory_
.GetWeakPtr())));
539 // If the danger level doesn't depend on having visited the refererrer URL
540 // or if original profile doesn't have a HistoryService or the referrer url
541 // is invalid, then assume the referrer has not been visited before.
542 danger_type_
= content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
;
547 void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone(
548 bool visited_referrer_before
) {
549 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
551 visited_referrer_before
? VISITED_REFERRER
: NO_VISITS_TO_REFERRER
))
552 danger_type_
= content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
;
556 DownloadTargetDeterminer::Result
557 DownloadTargetDeterminer::DoDetermineIntermediatePath() {
558 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
559 DCHECK(!virtual_path_
.empty());
560 DCHECK(!local_path_
.empty());
561 DCHECK(intermediate_path_
.empty());
562 DCHECK(!virtual_path_
.MatchesExtension(kCrdownloadSuffix
));
563 DCHECK(!local_path_
.MatchesExtension(kCrdownloadSuffix
));
565 next_state_
= STATE_NONE
;
567 // Note that the intermediate filename is always uniquified (i.e. if a file by
568 // the same name exists, it is never overwritten). Therefore the code below
569 // does not attempt to find a name that doesn't conflict with an existing
572 // If the actual target of the download is a virtual path, then the local path
573 // is considered to point to a temporary path. A separate intermediate path is
574 // unnecessary since the local path already serves that purpose.
575 if (virtual_path_
.BaseName() != local_path_
.BaseName()) {
576 intermediate_path_
= local_path_
;
580 // If the download has a forced path and is safe, then just use the
581 // target path. In practice the temporary download file that was created prior
582 // to download filename determination is already named
583 // download_->GetForcedFilePath().
584 if (danger_type_
== content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
&&
585 !download_
->GetForcedFilePath().empty()) {
586 DCHECK_EQ(download_
->GetForcedFilePath().value(), local_path_
.value());
587 intermediate_path_
= local_path_
;
591 // Other safe downloads get a .crdownload suffix for their intermediate name.
592 if (danger_type_
== content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
) {
593 intermediate_path_
= GetCrDownloadPath(local_path_
);
597 // If this is a resumed download, then re-use the existing intermediate path
598 // if one is available. A resumed download shouldn't cause a non-dangerous
599 // download to be considered dangerous upon resumption. Therefore the
600 // intermediate file should already be in the correct form.
601 if (is_resumption_
&& !download_
->GetFullPath().empty() &&
602 local_path_
.DirName() == download_
->GetFullPath().DirName()) {
603 DCHECK_NE(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
,
604 download_
->GetDangerType());
605 DCHECK_EQ(kCrdownloadSuffix
, download_
->GetFullPath().Extension());
606 intermediate_path_
= download_
->GetFullPath();
610 // Dangerous downloads receive a random intermediate name that looks like:
611 // 'Unconfirmed <random>.crdownload'.
612 const base::FilePath::CharType kUnconfirmedFormatSuffix
[] =
613 FILE_PATH_LITERAL(" %d.crdownload");
614 // Range of the <random> uniquifier.
615 const int kUnconfirmedUniquifierRange
= 1000000;
617 base::string16 unconfirmed_format
=
618 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX
);
620 std::string unconfirmed_format
=
621 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX
);
623 unconfirmed_format
.append(kUnconfirmedFormatSuffix
);
625 base::FilePath::StringType file_name
= base::StringPrintf(
626 unconfirmed_format
.c_str(),
627 base::RandInt(0, kUnconfirmedUniquifierRange
));
628 intermediate_path_
= local_path_
.DirName().Append(file_name
);
632 void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
634 DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_
.AsUTF8Unsafe()
635 << " Local:" << local_path_
.AsUTF8Unsafe()
636 << " Intermediate:" << intermediate_path_
.AsUTF8Unsafe()
637 << " Should prompt:" << should_prompt_
638 << " Danger type:" << danger_type_
;
639 scoped_ptr
<DownloadTargetInfo
> target_info(new DownloadTargetInfo
);
641 target_info
->target_path
= local_path_
;
642 target_info
->target_disposition
=
643 (HasPromptedForPath() || should_prompt_
644 ? DownloadItem::TARGET_DISPOSITION_PROMPT
645 : DownloadItem::TARGET_DISPOSITION_OVERWRITE
);
646 target_info
->danger_type
= danger_type_
;
647 target_info
->intermediate_path
= intermediate_path_
;
648 target_info
->mime_type
= mime_type_
;
649 target_info
->is_filetype_handled_safely
= is_filetype_handled_safely_
;
651 base::MessageLoop::current()->PostTask(
652 FROM_HERE
, base::Bind(completion_callback_
, base::Passed(&target_info
)));
653 completion_callback_
.Reset();
657 void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() {
658 // Path substitution failed.
659 virtual_path_
.clear();
661 intermediate_path_
.clear();
662 ScheduleCallbackAndDeleteSelf();
665 Profile
* DownloadTargetDeterminer::GetProfile() {
666 DCHECK(download_
->GetBrowserContext());
667 return Profile::FromBrowserContext(download_
->GetBrowserContext());
670 bool DownloadTargetDeterminer::ShouldPromptForDownload(
671 const base::FilePath
& filename
) const {
672 if (is_resumption_
) {
673 // For resumed downloads, if the target disposition or prefs require
674 // prompting, the user has already been prompted. Try to respect the user's
675 // selection, unless we've discovered that the target path cannot be used
677 content::DownloadInterruptReason reason
= download_
->GetLastReason();
678 return (reason
== content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
||
679 reason
== content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE
||
680 reason
== content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE
);
683 // If the download path is forced, don't prompt.
684 if (!download_
->GetForcedFilePath().empty()) {
685 // 'Save As' downloads shouldn't have a forced path.
686 DCHECK(DownloadItem::TARGET_DISPOSITION_PROMPT
!=
687 download_
->GetTargetDisposition());
691 // Don't ask where to save if the download path is managed. Even if the user
692 // wanted to be prompted for "all" downloads, or if this was a 'Save As'
694 if (download_prefs_
->IsDownloadPathManaged())
697 // Prompt if this is a 'Save As' download.
698 if (download_
->GetTargetDisposition() ==
699 DownloadItem::TARGET_DISPOSITION_PROMPT
)
702 // Check if the user has the "Always prompt for download location" preference
703 // set. If so we prompt for most downloads except for the following scenarios:
704 // 1) Extension installation. Note that we only care here about the case where
705 // an extension is installed, not when one is downloaded with "save as...".
706 // 2) Filetypes marked "always open." If the user just wants this file opened,
707 // don't bother asking where to keep it.
708 if (download_prefs_
->PromptForDownload() &&
709 !download_crx_util::IsExtensionDownload(*download_
) &&
710 !filename
.MatchesExtension(extensions::kExtensionFileExtension
) &&
711 !download_prefs_
->IsAutoOpenEnabledBasedOnExtension(filename
))
714 // Otherwise, don't prompt. Note that the user might still be prompted if
715 // there are unresolved conflicts during path reservation (e.g. due to the
716 // target path being unwriteable or because there are too many conflicting
717 // files), or if an extension signals that the user be prompted on a filename
722 bool DownloadTargetDeterminer::HasPromptedForPath() const {
723 return (is_resumption_
&& download_
->GetTargetDisposition() ==
724 DownloadItem::TARGET_DISPOSITION_PROMPT
);
727 bool DownloadTargetDeterminer::IsDangerousFile(PriorVisitsToReferrer visits
) {
728 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
730 // If the user has has been prompted or will be, assume that the user has
731 // approved the download. A programmatic download is considered safe unless it
733 if (HasPromptedForPath() || should_prompt_
||
734 !download_
->GetForcedFilePath().empty())
737 const bool is_extension_download
=
738 download_crx_util::IsExtensionDownload(*download_
);
740 // User-initiated extension downloads from pref-whitelisted sources are not
741 // considered dangerous.
742 if (download_
->HasUserGesture() &&
743 is_extension_download
&&
744 download_crx_util::OffStoreInstallAllowedByPrefs(
745 GetProfile(), *download_
)) {
749 // Extensions that are not from the gallery are considered dangerous.
750 // When off-store install is disabled we skip this, since in this case, we
751 // will not offer to install the extension.
752 if (extensions::FeatureSwitch::easy_off_store_install()->IsEnabled() &&
753 is_extension_download
&&
754 !extensions::WebstoreInstaller::GetAssociatedApproval(*download_
)) {
758 // Anything the user has marked auto-open is OK if it's user-initiated.
759 if (download_prefs_
->IsAutoOpenEnabledBasedOnExtension(virtual_path_
) &&
760 download_
->HasUserGesture())
763 switch (download_util::GetFileDangerLevel(virtual_path_
.BaseName())) {
764 case download_util::NOT_DANGEROUS
:
767 case download_util::ALLOW_ON_USER_GESTURE
:
768 // "Allow on user gesture" is OK when we have a user gesture and the
769 // hosting page has been visited before today.
770 if (download_
->GetTransitionType() &
771 content::PAGE_TRANSITION_FROM_ADDRESS_BAR
) {
774 return !download_
->HasUserGesture() || visits
== NO_VISITS_TO_REFERRER
;
776 case download_util::DANGEROUS
:
783 void DownloadTargetDeterminer::OnDownloadDestroyed(
784 DownloadItem
* download
) {
785 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
786 DCHECK_EQ(download_
, download
);
787 CancelOnFailureAndDeleteSelf();
791 void DownloadTargetDeterminer::Start(content::DownloadItem
* download
,
792 const base::FilePath
& initial_virtual_path
,
793 DownloadPrefs
* download_prefs
,
794 DownloadTargetDeterminerDelegate
* delegate
,
795 const CompletionCallback
& callback
) {
796 // DownloadTargetDeterminer owns itself and will self destruct when the job is
797 // complete or the download item is destroyed. The callback is always invoked
799 new DownloadTargetDeterminer(download
, initial_virtual_path
, download_prefs
,
804 base::FilePath
DownloadTargetDeterminer::GetCrDownloadPath(
805 const base::FilePath
& suggested_path
) {
806 return base::FilePath(suggested_path
.value() + kCrdownloadSuffix
);