Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / download / download_target_determiner.cc
blob0ddb3a077c00370e4db7cd8216372274aaf9db4c
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"
34 #endif
36 using content::BrowserThread;
37 using content::DownloadItem;
39 namespace {
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
47 // midnight.
48 void VisitCountsToVisitedBefore(
49 const base::Callback<void(bool)>& callback,
50 HistoryService::Handle unused_handle,
51 bool found_visits,
52 int count,
53 base::Time first_visit) {
54 callback.Run(
55 found_visits && count > 0 &&
56 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
59 } // namespace
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),
83 download_(download),
84 is_resumption_(download_->GetLastReason() !=
85 content::DOWNLOAD_INTERRUPT_REASON_NONE &&
86 !initial_virtual_path.empty()),
87 download_prefs_(download_prefs),
88 delegate_(delegate),
89 completion_callback_(callback),
90 weak_ptr_factory_(this) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
92 DCHECK(download_);
93 DCHECK(delegate);
94 download_->AddObserver(this);
96 DoLoop();
99 DownloadTargetDeterminer::~DownloadTargetDeterminer() {
100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
101 DCHECK(download_);
102 DCHECK(completion_callback_.is_null());
103 download_->RemoveObserver(this);
106 void DownloadTargetDeterminer::DoLoop() {
107 Result result = CONTINUE;
108 do {
109 State current_state = next_state_;
110 next_state_ = STATE_NONE;
112 switch (current_state) {
113 case STATE_GENERATE_TARGET_PATH:
114 result = DoGenerateTargetPath();
115 break;
116 case STATE_NOTIFY_EXTENSIONS:
117 result = DoNotifyExtensions();
118 break;
119 case STATE_RESERVE_VIRTUAL_PATH:
120 result = DoReserveVirtualPath();
121 break;
122 case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH:
123 result = DoPromptUserForDownloadPath();
124 break;
125 case STATE_DETERMINE_LOCAL_PATH:
126 result = DoDetermineLocalPath();
127 break;
128 case STATE_DETERMINE_MIME_TYPE:
129 result = DoDetermineMimeType();
130 break;
131 case STATE_DETERMINE_IF_HANDLED_SAFELY_BY_BROWSER:
132 result = DoDetermineIfHandledSafely();
133 break;
134 case STATE_CHECK_DOWNLOAD_URL:
135 result = DoCheckDownloadUrl();
136 break;
137 case STATE_DETERMINE_INTERMEDIATE_PATH:
138 result = DoDetermineIntermediatePath();
139 break;
140 case STATE_CHECK_VISITED_REFERRER_BEFORE:
141 result = DoCheckVisitedReferrerBefore();
142 break;
143 case STATE_NONE:
144 NOTREACHED();
145 return;
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
177 // a local path.
178 std::string default_filename(
179 l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
180 base::FilePath generated_filename = net::GenerateFileName(
181 download_->GetURL(),
182 download_->GetContentDisposition(),
183 GetProfile()->GetPrefs()->GetString(prefs::kDefaultCharset),
184 download_->GetSuggestedFilename(),
185 download_->GetMimeType(),
186 default_filename);
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();
194 } else {
195 target_directory = download_prefs_->DownloadPath();
197 virtual_path_ = target_directory.Append(generated_filename);
198 conflict_action_ = DownloadPathReservationTracker::UNIQUIFY;
199 should_notify_extensions_ = true;
200 } else {
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
205 // intervene.
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)
218 return COMPLETE;
219 return CONTINUE;
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_)
230 return CONTINUE;
232 delegate_->NotifyExtensions(download_, virtual_path_,
233 base::Bind(&DownloadTargetDeterminer::NotifyExtensionsDone,
234 weak_ptr_factory_.GetWeakPtr()));
235 return QUIT_DOLOOP;
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
256 // suggest it.
257 net::GenerateSafeFileName(std::string(), false, &new_path);
258 virtual_path_ = new_path;
259 create_target_directory_ = true;
260 conflict_action_ = conflict_action;
263 DoLoop();
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()));
277 return QUIT_DOLOOP;
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;
287 DoLoop();
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(
299 download_,
300 virtual_path_,
301 base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone,
302 weak_ptr_factory_.GetWeakPtr()));
303 return QUIT_DOLOOP;
305 return CONTINUE;
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();
314 return;
316 virtual_path_ = virtual_path;
317 download_prefs_->SetSaveFilePath(virtual_path_.DirName());
318 DoLoop();
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(
330 download_,
331 virtual_path_,
332 base::Bind(&DownloadTargetDeterminer::DetermineLocalPathDone,
333 weak_ptr_factory_.GetWeakPtr()));
334 return QUIT_DOLOOP;
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();
344 return;
346 local_path_ = local_path;
347 DoLoop();
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(
361 local_path_,
362 base::Bind(&DownloadTargetDeterminer::DetermineMimeTypeDone,
363 weak_ptr_factory_.GetWeakPtr()));
364 return QUIT_DOLOOP;
366 return CONTINUE;
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;
374 DoLoop();
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.
380 namespace {
382 void InvokeClosureAfterGetPluginCallback(
383 const base::Closure& closure,
384 const std::vector<content::WebPluginInfo>& unused) {
385 closure.Run();
388 enum ActionOnStalePluginList {
389 RETRY_IF_STALE_PLUGIN_LIST,
390 IGNORE_IF_STALE_PLUGIN_LIST
393 void IsHandledBySafePlugin(content::ResourceContext* resource_context,
394 const GURL& url,
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,
410 false, &is_stale,
411 &plugin_info,
412 &actual_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,
420 resource_context,
421 url,
422 mime_type,
423 IGNORE_IF_STALE_PLUGIN_LIST,
424 callback)));
425 return;
427 // In practice, we assume that retrying once is enough.
428 DCHECK(!is_stale);
429 bool is_handled_safely =
430 plugin_found &&
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));
437 } // namespace
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())
450 return CONTINUE;
452 if (net::IsSupportedMimeType(mime_type_)) {
453 is_filetype_handled_safely_ = true;
454 return CONTINUE;
457 #if defined(ENABLE_PLUGINS)
458 BrowserThread::PostTask(
459 BrowserThread::IO,
460 FROM_HERE,
461 base::Bind(
462 &IsHandledBySafePlugin,
463 GetProfile()->GetResourceContext(),
464 net::FilePathToFileURL(local_path_),
465 mime_type_,
466 RETRY_IF_STALE_PLUGIN_LIST,
467 base::Bind(&DownloadTargetDeterminer::DetermineIfHandledSafelyDone,
468 weak_ptr_factory_.GetWeakPtr())));
469 return QUIT_DOLOOP;
470 #else
471 return CONTINUE;
472 #endif
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_;
480 DoLoop();
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(
489 download_,
490 virtual_path_,
491 base::Bind(&DownloadTargetDeterminer::CheckDownloadUrlDone,
492 weak_ptr_factory_.GetWeakPtr()));
493 return QUIT_DOLOOP;
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;
501 DoLoop();
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)
515 return CONTINUE;
517 // Assume that:
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())));
535 return QUIT_DOLOOP;
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;
544 return CONTINUE;
547 void DownloadTargetDeterminer::CheckVisitedReferrerBeforeDone(
548 bool visited_referrer_before) {
549 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
550 if (IsDangerousFile(
551 visited_referrer_before ? VISITED_REFERRER : NO_VISITS_TO_REFERRER))
552 danger_type_ = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
553 DoLoop();
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
570 // file.
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_;
577 return COMPLETE;
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_;
588 return COMPLETE;
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_);
594 return COMPLETE;
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();
607 return COMPLETE;
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;
616 #if defined(OS_WIN)
617 base::string16 unconfirmed_format =
618 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
619 #else
620 std::string unconfirmed_format =
621 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
622 #endif
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);
629 return COMPLETE;
632 void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
633 DCHECK(download_);
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();
654 delete this;
657 void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() {
658 // Path substitution failed.
659 virtual_path_.clear();
660 local_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
676 // for some reason.
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());
688 return false;
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'
693 // download.
694 if (download_prefs_->IsDownloadPathManaged())
695 return false;
697 // Prompt if this is a 'Save As' download.
698 if (download_->GetTargetDisposition() ==
699 DownloadItem::TARGET_DISPOSITION_PROMPT)
700 return true;
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))
712 return true;
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
718 // conflict.
719 return false;
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
732 // contains malware.
733 if (HasPromptedForPath() || should_prompt_ ||
734 !download_->GetForcedFilePath().empty())
735 return false;
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_)) {
746 return false;
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_)) {
755 return true;
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())
761 return false;
763 switch (download_util::GetFileDangerLevel(virtual_path_.BaseName())) {
764 case download_util::NOT_DANGEROUS:
765 return false;
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) {
772 return false;
774 return !download_->HasUserGesture() || visits == NO_VISITS_TO_REFERRER;
776 case download_util::DANGEROUS:
777 return true;
779 NOTREACHED();
780 return false;
783 void DownloadTargetDeterminer::OnDownloadDestroyed(
784 DownloadItem* download) {
785 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
786 DCHECK_EQ(download_, download);
787 CancelOnFailureAndDeleteSelf();
790 // static
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
798 // asynchronously.
799 new DownloadTargetDeterminer(download, initial_virtual_path, download_prefs,
800 delegate, callback);
803 // static
804 base::FilePath DownloadTargetDeterminer::GetCrDownloadPath(
805 const base::FilePath& suggested_path) {
806 return base::FilePath(suggested_path.value() + kCrdownloadSuffix);